summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x[-rw-r--r--]AUTHORS0
-rwxr-xr-x[-rw-r--r--]COPYING0
-rwxr-xr-x[-rw-r--r--]ChangeLog0
-rwxr-xr-x[-rw-r--r--]CodingStyle0
-rwxr-xr-x[-rw-r--r--]Doxy.page.h69
-rwxr-xr-x[-rw-r--r--]Doxyfile10
-rwxr-xr-x[-rw-r--r--]Doxyfile.devel0
-rwxr-xr-x[-rw-r--r--]INSTALL0
-rwxr-xr-x[-rw-r--r--]KNOWN_ISSUES6
-rwxr-xr-x[-rw-r--r--]Knot.config0
-rwxr-xr-x[-rw-r--r--]Knot.creator0
-rwxr-xr-x[-rw-r--r--]Knot.files26
-rwxr-xr-x[-rw-r--r--]Knot.includes0
-rwxr-xr-x[-rw-r--r--]Makefile.am2
-rwxr-xr-x[-rw-r--r--]Makefile.in2
-rwxr-xr-x[-rw-r--r--]NEWS0
-rwxr-xr-x[-rw-r--r--]README11
-rwxr-xr-x[-rw-r--r--]RELNOTES63
-rwxr-xr-x[-rw-r--r--]aclocal.m40
-rwxr-xr-xconfigure1476
-rwxr-xr-x[-rw-r--r--]configure.ac45
-rwxr-xr-xdoc/.gitignore17
-rwxr-xr-xdoc/Makefile.am10
-rwxr-xr-xdoc/Makefile.in664
-rwxr-xr-xdoc/configuration.texi196
-rwxr-xr-xdoc/indices.texi4
-rwxr-xr-xdoc/installation.texi297
-rwxr-xr-xdoc/introduction.texi70
-rwxr-xr-xdoc/knot.texi229
-rwxr-xr-xdoc/mdate-sh205
-rwxr-xr-xdoc/migration.texi29
-rwxr-xr-xdoc/reference.texi774
-rwxr-xr-xdoc/requirements.texi52
-rwxr-xr-xdoc/running.texi187
-rwxr-xr-xdoc/security.texi15
-rwxr-xr-xdoc/texinfo.tex9291
-rwxr-xr-xdoc/troubleshooting.texi131
-rwxr-xr-x[-rw-r--r--]knot.sample.conf.in0
-rwxr-xr-x[-rw-r--r--]m4/ax_check_compiler_flags.m40
-rwxr-xr-x[-rw-r--r--]m4/ax_ext.m40
-rwxr-xr-x[-rw-r--r--]m4/ax_gcc_x86_cpuid.m40
-rwxr-xr-x[-rw-r--r--]m4/libtool.m40
-rwxr-xr-x[-rw-r--r--]m4/ltoptions.m40
-rwxr-xr-x[-rw-r--r--]m4/ltsugar.m40
-rwxr-xr-x[-rw-r--r--]m4/ltversion.m40
-rwxr-xr-x[-rw-r--r--]m4/lt~obsolete.m40
-rwxr-xr-x[-rw-r--r--]samples/Makefile.am0
-rwxr-xr-x[-rw-r--r--]samples/Makefile.in0
-rwxr-xr-x[-rw-r--r--]samples/bogus25.com.zone0
-rwxr-xr-x[-rw-r--r--]samples/example.com.zone0
-rwxr-xr-x[-rw-r--r--]samples/example.com.zone.signed0
-rwxr-xr-x[-rw-r--r--]samples/example.com.zone.signed.nsec30
-rwxr-xr-x[-rw-r--r--]samples/knot.full.conf16
-rwxr-xr-x[-rw-r--r--]samples/knot.min.conf2
-rwxr-xr-x[-rw-r--r--]samples/knot.sample.conf.in2
-rwxr-xr-x[-rw-r--r--]scripts/urcu-tls-compat.patch0
-rwxr-xr-x[-rw-r--r--]src/Makefile.am16
-rwxr-xr-x[-rw-r--r--]src/Makefile.in94
-rwxr-xr-x[-rw-r--r--]src/common/LICENSE.txt0
-rwxr-xr-x[-rw-r--r--]src/common/acl.c0
-rwxr-xr-x[-rw-r--r--]src/common/acl.h0
-rwxr-xr-x[-rw-r--r--]src/common/base32.c0
-rwxr-xr-x[-rw-r--r--]src/common/base32.h0
-rwxr-xr-x[-rw-r--r--]src/common/base32hex.c0
-rwxr-xr-x[-rw-r--r--]src/common/base32hex.h0
-rwxr-xr-xsrc/common/base64.c574
-rwxr-xr-xsrc/common/base64.h60
-rwxr-xr-x[-rw-r--r--]src/common/crc.h2
-rwxr-xr-x[-rw-r--r--]src/common/dSFMT-params.h0
-rwxr-xr-x[-rw-r--r--]src/common/dSFMT-params521.h0
-rwxr-xr-x[-rw-r--r--]src/common/dSFMT.c0
-rwxr-xr-x[-rw-r--r--]src/common/dSFMT.h0
-rw-r--r--src/common/dynamic-array.c225
-rw-r--r--src/common/dynamic-array.h156
-rwxr-xr-x[-rw-r--r--]src/common/errors.c0
-rwxr-xr-x[-rw-r--r--]src/common/errors.h0
-rwxr-xr-x[-rw-r--r--]src/common/evqueue.c7
-rwxr-xr-x[-rw-r--r--]src/common/evqueue.h0
-rwxr-xr-x[-rw-r--r--]src/common/evsched.c74
-rwxr-xr-x[-rw-r--r--]src/common/evsched.h4
-rwxr-xr-x[-rw-r--r--]src/common/fdset.c0
-rwxr-xr-x[-rw-r--r--]src/common/fdset.h2
-rwxr-xr-x[-rw-r--r--]src/common/fdset_epoll.c0
-rwxr-xr-x[-rw-r--r--]src/common/fdset_epoll.h0
-rwxr-xr-x[-rw-r--r--]src/common/fdset_kqueue.c0
-rwxr-xr-x[-rw-r--r--]src/common/fdset_kqueue.h0
-rwxr-xr-x[-rw-r--r--]src/common/fdset_poll.c3
-rwxr-xr-x[-rw-r--r--]src/common/fdset_poll.h0
-rwxr-xr-x[-rw-r--r--]src/common/general-tree.c0
-rwxr-xr-x[-rw-r--r--]src/common/general-tree.h0
-rwxr-xr-xsrc/common/heap.c149
-rwxr-xr-xsrc/common/heap.h57
-rwxr-xr-x[-rw-r--r--]src/common/latency.c0
-rwxr-xr-x[-rw-r--r--]src/common/latency.h0
-rwxr-xr-x[-rw-r--r--]src/common/libtap/README0
-rwxr-xr-x[-rw-r--r--]src/common/libtap/tap.c0
-rwxr-xr-x[-rw-r--r--]src/common/libtap/tap.h0
-rwxr-xr-x[-rw-r--r--]src/common/libtap/tap_unit.h0
-rwxr-xr-x[-rw-r--r--]src/common/lists.c0
-rwxr-xr-x[-rw-r--r--]src/common/lists.h1
-rwxr-xr-x[-rw-r--r--]src/common/log.c0
-rwxr-xr-x[-rw-r--r--]src/common/log.h0
-rwxr-xr-x[-rw-r--r--]src/common/mempattern.c0
-rwxr-xr-x[-rw-r--r--]src/common/mempattern.h0
-rwxr-xr-x[-rw-r--r--]src/common/modified_tree.h2
-rwxr-xr-x[-rw-r--r--]src/common/print.c0
-rwxr-xr-x[-rw-r--r--]src/common/print.h0
-rwxr-xr-x[-rw-r--r--]src/common/prng.c0
-rwxr-xr-x[-rw-r--r--]src/common/prng.h2
-rwxr-xr-x[-rw-r--r--]src/common/ref.c0
-rwxr-xr-x[-rw-r--r--]src/common/ref.h0
-rwxr-xr-x[-rw-r--r--]src/common/skip-list.c0
-rwxr-xr-x[-rw-r--r--]src/common/skip-list.h0
-rwxr-xr-x[-rw-r--r--]src/common/slab/alloc-common.h0
-rwxr-xr-x[-rw-r--r--]src/common/slab/slab.c2
-rwxr-xr-x[-rw-r--r--]src/common/slab/slab.h0
-rwxr-xr-x[-rw-r--r--]src/common/sockaddr.c0
-rwxr-xr-x[-rw-r--r--]src/common/sockaddr.h0
-rwxr-xr-x[-rw-r--r--]src/common/tree.h0
-rwxr-xr-x[-rw-r--r--]src/config.h.in15
-rwxr-xr-xsrc/knot.conf.5296
-rwxr-xr-x[-rw-r--r--]src/knot.service0
-rwxr-xr-x[-rw-r--r--]src/knot.spec0
-rwxr-xr-x[-rw-r--r--]src/knot.sysconfig0
-rwxr-xr-x[-rw-r--r--]src/knot/common.h8
-rwxr-xr-x[-rw-r--r--]src/knot/conf/cf-lex.l1
-rwxr-xr-x[-rw-r--r--]src/knot/conf/cf-parse.y25
-rwxr-xr-x[-rw-r--r--]src/knot/conf/conf.c167
-rwxr-xr-x[-rw-r--r--]src/knot/conf/conf.h30
-rwxr-xr-x[-rw-r--r--]src/knot/conf/logconf.c0
-rwxr-xr-x[-rw-r--r--]src/knot/conf/logconf.h0
-rwxr-xr-x[-rw-r--r--]src/knot/ctl/knotc_main.c48
-rwxr-xr-x[-rw-r--r--]src/knot/ctl/process.c50
-rwxr-xr-x[-rw-r--r--]src/knot/ctl/process.h9
-rwxr-xr-x[-rw-r--r--]src/knot/main.c30
-rwxr-xr-x[-rw-r--r--]src/knot/other/debug.h0
-rwxr-xr-x[-rw-r--r--]src/knot/other/error.c4
-rwxr-xr-x[-rw-r--r--]src/knot/other/error.h4
-rwxr-xr-x[-rw-r--r--]src/knot/server/dthreads.c57
-rwxr-xr-x[-rw-r--r--]src/knot/server/dthreads.h5
-rwxr-xr-x[-rw-r--r--]src/knot/server/journal.c52
-rwxr-xr-x[-rw-r--r--]src/knot/server/journal.h2
-rwxr-xr-x[-rw-r--r--]src/knot/server/notify.c38
-rwxr-xr-x[-rw-r--r--]src/knot/server/notify.h0
-rwxr-xr-x[-rw-r--r--]src/knot/server/server.c66
-rwxr-xr-x[-rw-r--r--]src/knot/server/server.h0
-rwxr-xr-x[-rw-r--r--]src/knot/server/socket.c21
-rwxr-xr-x[-rw-r--r--]src/knot/server/socket.h0
-rwxr-xr-x[-rw-r--r--]src/knot/server/tcp-handler.c66
-rwxr-xr-x[-rw-r--r--]src/knot/server/tcp-handler.h0
-rwxr-xr-x[-rw-r--r--]src/knot/server/udp-handler.c91
-rwxr-xr-x[-rw-r--r--]src/knot/server/udp-handler.h0
-rwxr-xr-x[-rw-r--r--]src/knot/server/xfr-handler.c418
-rwxr-xr-x[-rw-r--r--]src/knot/server/xfr-handler.h11
-rwxr-xr-x[-rw-r--r--]src/knot/server/zones.c778
-rwxr-xr-x[-rw-r--r--]src/knot/server/zones.h19
-rwxr-xr-x[-rw-r--r--]src/knot/stat/gatherer.c0
-rwxr-xr-x[-rw-r--r--]src/knot/stat/gatherer.h0
-rwxr-xr-x[-rw-r--r--]src/knot/stat/stat-common.h0
-rwxr-xr-x[-rw-r--r--]src/knot/stat/stat.c0
-rwxr-xr-x[-rw-r--r--]src/knot/stat/stat.h0
-rwxr-xr-x[-rw-r--r--]src/knot/zone/semantic-check.c77
-rwxr-xr-x[-rw-r--r--]src/knot/zone/semantic-check.h4
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-dump-text.c195
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-dump-text.h2
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-dump.c61
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-dump.h4
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-load.c260
-rwxr-xr-x[-rw-r--r--]src/knot/zone/zone-load.h2
-rwxr-xr-x[-rw-r--r--]src/knotc.824
-rwxr-xr-x[-rw-r--r--]src/knotd.82
-rwxr-xr-x[-rw-r--r--]src/libknot/common.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/consts.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/dname.c192
-rwxr-xr-x[-rw-r--r--]src/libknot/dname.h2
-rwxr-xr-x[-rw-r--r--]src/libknot/edns.c45
-rwxr-xr-x[-rw-r--r--]src/libknot/edns.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/cuckoo-hash-table.c677
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/cuckoo-hash-table.h1
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/hash-functions.c0
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/hash-functions.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/universal-system.c0
-rwxr-xr-x[-rw-r--r--]src/libknot/hash/universal-system.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/libknot.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/nameserver/name-server.c1560
-rwxr-xr-x[-rw-r--r--]src/libknot/nameserver/name-server.h42
-rwxr-xr-x[-rw-r--r--]src/libknot/nsec3.c30
-rwxr-xr-x[-rw-r--r--]src/libknot/nsec3.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/packet.c331
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/packet.h3
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/query.c2
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/query.h6
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/response.c208
-rwxr-xr-x[-rw-r--r--]src/libknot/packet/response.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/rdata.c223
-rwxr-xr-x[-rw-r--r--]src/libknot/rdata.h7
-rwxr-xr-x[-rw-r--r--]src/libknot/rrset.c266
-rwxr-xr-x[-rw-r--r--]src/libknot/rrset.h44
-rwxr-xr-x[-rw-r--r--]src/libknot/tsig-op.c254
-rwxr-xr-x[-rw-r--r--]src/libknot/tsig-op.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/tsig.c49
-rwxr-xr-x[-rw-r--r--]src/libknot/tsig.h2
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/changesets.c30
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/changesets.h8
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/ddns.c4
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/ddns.h2
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/xfr-in.c754
-rwxr-xr-x[-rw-r--r--]src/libknot/updates/xfr-in.h5
-rw-r--r--src/libknot/util/conv.c325
-rw-r--r--src/libknot/util/conv.h44
-rwxr-xr-x[-rw-r--r--]src/libknot/util/debug.c8
-rwxr-xr-x[-rw-r--r--]src/libknot/util/debug.h69
-rwxr-xr-x[-rw-r--r--]src/libknot/util/descriptor.c2
-rwxr-xr-x[-rw-r--r--]src/libknot/util/descriptor.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/util/error.h5
-rwxr-xr-x[-rw-r--r--]src/libknot/util/libknot_error.c5
-rwxr-xr-x[-rw-r--r--]src/libknot/util/tolower.c0
-rwxr-xr-x[-rw-r--r--]src/libknot/util/tolower.h2
-rwxr-xr-x[-rw-r--r--]src/libknot/util/utils.c0
-rwxr-xr-x[-rw-r--r--]src/libknot/util/utils.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/util/wire.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/dname-table.c0
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/dname-table.h0
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/node.c231
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/node.h36
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone-contents.c1038
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone-contents.h23
-rwxr-xr-xsrc/libknot/zone/zone-diff.c1004
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone-diff.h (renamed from src/tests/common/da_tests.h)15
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone-tree.c23
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone-tree.h2
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone.c44
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zone.h69
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zonedb.c123
-rwxr-xr-x[-rw-r--r--]src/libknot/zone/zonedb.h3
-rwxr-xr-x[-rw-r--r--]src/tests/README0
-rwxr-xr-x[-rw-r--r--]src/tests/common/acl_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/common/acl_tests.h0
-rw-r--r--src/tests/common/da_tests.c330
-rwxr-xr-x[-rw-r--r--]src/tests/common/events_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/common/events_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/common/fdset_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/common/fdset_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/common/skiplist_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/common/skiplist_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/common/slab_tests.c7
-rwxr-xr-x[-rw-r--r--]src/tests/common/slab_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/files/sample_conf0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/conf_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/conf_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/dthreads_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/dthreads_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/journal_tests.c28
-rwxr-xr-x[-rw-r--r--]src/tests/knot/journal_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/server_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/knot/server_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/files/parsed_databin71188 -> 71188 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/files/parsed_data_queriesbin1325 -> 1325 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/files/raw_databin72100 -> 72100 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/files/raw_data_queriesbin1387 -> 1387 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/cuckoo_tests.c1
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/cuckoo_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/dname_table_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/dname_table_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/dname_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/dname_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/edns_tests.c3
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/edns_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/node_tests.c18
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/node_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/nsec3_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/nsec3_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/packet_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/packet_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/query_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/query_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/rdata_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/rdata_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/response_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/response_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/rrset_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/rrset_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/tsig_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/tsig_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zone_tests.c12
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zone_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zone_tree_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zone_tree_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zonedb_tests.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/libknot/zonedb_tests.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/files/parsed_databin4851 -> 4851 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/files/parsed_data_queriesbin1325 -> 1325 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/files/raw_databin4935 -> 4935 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/files/raw_data_queriesbin1387 -> 1387 bytes
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/dname_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/dname_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/edns_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/edns_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/node_tests_realdata.c4
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/node_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/packet_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/packet_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/rdata_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/rdata_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/response_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/response_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/rrset_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/rrset_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/zone_tests_realdata.c2
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/zone_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot_tests_loader_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/libknot_tests_loader_realdata.h0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/realdata/unittests_libknot_realdata.c0
-rwxr-xr-x[-rw-r--r--]src/tests/libknot/unittests_libknot.c0
-rwxr-xr-x[-rw-r--r--]src/tests/unittests_main.c2
-rwxr-xr-x[-rw-r--r--]src/tests/xfr_tests.c6
-rwxr-xr-x[-rw-r--r--]src/tests/xfr_tests.h0
-rwxr-xr-x[-rw-r--r--]src/zcompile/LICENSE0
-rwxr-xr-x[-rw-r--r--]src/zcompile/parser-descriptor.c1
-rwxr-xr-x[-rw-r--r--]src/zcompile/parser-descriptor.h0
-rwxr-xr-x[-rw-r--r--]src/zcompile/parser-util.c18
-rwxr-xr-x[-rw-r--r--]src/zcompile/parser-util.h0
-rwxr-xr-x[-rw-r--r--]src/zcompile/tests/unittests_zp_main.c0
-rwxr-xr-x[-rw-r--r--]src/zcompile/tests/zcompile_tests.c0
-rwxr-xr-x[-rw-r--r--]src/zcompile/zcompile-error.c0
-rwxr-xr-x[-rw-r--r--]src/zcompile/zcompile-error.h0
-rwxr-xr-x[-rw-r--r--]src/zcompile/zcompile.c263
-rwxr-xr-x[-rw-r--r--]src/zcompile/zcompile.h7
-rwxr-xr-x[-rw-r--r--]src/zcompile/zcompile_main.c3
-rwxr-xr-x[-rw-r--r--]src/zcompile/zlexer.l0
-rwxr-xr-x[-rw-r--r--]src/zcompile/zparser.y17
-rwxr-xr-x[-rw-r--r--]tests/querytcp.c0
334 files changed, 20609 insertions, 6204 deletions
diff --git a/AUTHORS b/AUTHORS
index c032e47..c032e47 100644..100755
--- a/AUTHORS
+++ b/AUTHORS
diff --git a/COPYING b/COPYING
index 173ff49..173ff49 100644..100755
--- a/COPYING
+++ b/COPYING
diff --git a/ChangeLog b/ChangeLog
index e69de29..e69de29 100644..100755
--- a/ChangeLog
+++ b/ChangeLog
diff --git a/CodingStyle b/CodingStyle
index 986417c..986417c 100644..100755
--- a/CodingStyle
+++ b/CodingStyle
diff --git a/Doxy.page.h b/Doxy.page.h
index 4d0704c..9d73024 100644..100755
--- a/Doxy.page.h
+++ b/Doxy.page.h
@@ -3,68 +3,65 @@
\defgroup server Server control module.
\defgroup threading Threading API.
\defgroup network Socket API.
+\defgroup config Server configuration.
\defgroup query_processing DNS query processing.
\defgroup utils Utilities, constants and macros.
\defgroup debugging Server debugging API.
\defgroup logging Server logging API.
\defgroup statistics Statistics module (optional).
-\defgroup dnslib dnslib - Generic DNS library.
+\defgroup libknot libknot - library of DNS-related functions
\defgroup hashing Hash table and functions.
\defgroup common_lib Common library.
\defgroup alloc Memory allocation.
\defgroup tests Unit tests.
\defgroup zoneparser Zone compiler utility
\defgroup ctl Control utility
+\defgroup zone-load-dump Zone loading and dumping
+\defgroup xfr Zone transfers
\mainpage Knot API documentation.
Knot is an open-source, high-performace, purely authoritative DNS server.
-<h2>Requirements</h2>
-- liburcu (at least 0.4.5): http://lttng.org/urcu
-- automake
-- autoconf
-- libtool
+<h2>Features</h2>
-<h2>Installation</h2>
-Knot uses autotools to generate makefiles.
+Knot DNS supports the following DNS features:
+- TCP/UDP protocols
+- AXFR - master, slave
+- IXFR - master (primary master experimental), slave
+- TSIG
+- ENDS0
+- DNSSEC, including NSEC3
+- NSID
+- Unknown RR types
-\todo Add some more info about usage and requirements.
+Server features:
+- Adding/removing zones on-the-fly
+- Reconfiguring server instance on-the-fly
+- IPv4 / IPv6 support
+- Semantic checks of zones
-\code
-$ autoreconf -i
-$ ./configure
-$ make
-\endcode
-
-<h2>Starting the server</h2>
+<h2>Compiling and running the server</h2>
-When compiled, the following executables are created (in the src/ directory):
-- \em knotd - The server
-- \em knotc - Control utility
-- \em knot-zcompile - Zone compiler
-- \em unittests - Unit tests for the server and dnslib
-- \em unittests-zcompile - Unit tests for the zone compiler
+See the User manual - links to current version are provided in the
+<a href="https://git.nic.cz/redmine/projects/knot-dns/wiki">Knot DNS Wiki</a>.
-1. Add path to knotd and knot-zcompile executables to PATH
-
-2. Prepare a configuration file. You may copy and edit the one provided with
- the server (\em samples/knot.conf.sample).
-
-2. Compile zone
+Alternatively you can generate the manual from the sources in Info format:
\code
-$ src/knotc -c path-to-config-file compile
+$ make doc
\endcode
-3. Run the server
+or in PDF:
+
\code
-$ src/knotc -c path-to-config-file start
+$ make pdf
\endcode
<h2>Server modules</h2>
- \ref server
- \ref threading
- \ref network
+- \ref config
- \ref query_processing
- \ref utils
- \ref debugging
@@ -72,17 +69,19 @@ $ src/knotc -c path-to-config-file start
- \ref statistics
<h2>DNS library</h2>
-
-- \ref dnslib
+- \ref libknot
- \ref hashing
+- \ref xfr
-<h2>Common library</h2>
+<h2>Zone processing</h2>
+- \ref zoneparser
+- \ref zone-load-dump
+<h2>Common library</h2>
- \ref common_lib
- \ref alloc
<h2>Other modules</h2>
- \ref tests
-- \ref zoneparser
- \ref ctl
*/
diff --git a/Doxyfile b/Doxyfile
index 4966e80..1a5bb0a 100644..100755
--- a/Doxyfile
+++ b/Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = Knot
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 0.1.0
+PROJECT_NUMBER = 1.1.0
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
@@ -591,7 +591,11 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = src/ \
+INPUT = src/common \
+ src/libknot \
+ src/common \
+ src/knot \
+ src/zcompile \
Doxy.page.h
# This tag can be used to specify the character encoding of the source files
@@ -622,7 +626,7 @@ 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 =
+EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
diff --git a/Doxyfile.devel b/Doxyfile.devel
index 4b73045..4b73045 100644..100755
--- a/Doxyfile.devel
+++ b/Doxyfile.devel
diff --git a/INSTALL b/INSTALL
index 7d1c323..7d1c323 100644..100755
--- a/INSTALL
+++ b/INSTALL
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
index b10e4a3..fff55a8 100644..100755
--- a/KNOWN_ISSUES
+++ b/KNOWN_ISSUES
@@ -1,7 +1,7 @@
Features not supported
======================
-Here is a list of the most notable features that are not supported in the
+Here is a list of the most notable features that are not supported in the
current version of Knot.
* Other DNS classes than IN (CH, CS, HS)
@@ -10,6 +10,4 @@ current version of Knot.
Known bugs
==========
-* Slow start with too many zones.
-* IXFR is still quite slow with large transfers (more than 50 000 RRs
- changed), despite the previous improvements.
+* ACL may not always find the best match so it may behave counter-intuitively.
diff --git a/Knot.config b/Knot.config
index 8cec188..8cec188 100644..100755
--- a/Knot.config
+++ b/Knot.config
diff --git a/Knot.creator b/Knot.creator
index e94cbbd..e94cbbd 100644..100755
--- a/Knot.creator
+++ b/Knot.creator
diff --git a/Knot.files b/Knot.files
index 71a588f..99c6d59 100644..100755
--- a/Knot.files
+++ b/Knot.files
@@ -7,7 +7,6 @@ configure.ac
KNOWN_ISSUES
README
tests/querytcp.c
-src/libknot/Makefile.am
src/libknot/libknot.h
src/libknot/common.h
src/libknot/dname.h
@@ -75,14 +74,14 @@ src/common/mempattern.c
src/common/mempattern.h
src/common/lists.h
src/common/lists.c
+src/common/heap.h
+src/common/heap.c
src/common/base32.h
src/common/base32.c
src/common/print.c
src/common/print.h
src/common/latency.c
src/common/latency.h
-src/common/dynamic-array.c
-src/common/dynamic-array.h
src/common/skip-list.c
src/common/skip-list.h
src/common/tree.h
@@ -181,8 +180,6 @@ src/zcompile/zcompile-error.c
src/tests/unittests_main.c
src/tests/common/acl_tests.c
src/tests/common/acl_tests.h
-src/tests/common/da_tests.c
-src/tests/common/da_tests.h
src/tests/common/events_tests.c
src/tests/common/events_tests.h
src/tests/common/skiplist_tests.c
@@ -254,11 +251,26 @@ src/libknot/tsig.h
src/libknot/tsig.c
src/libknot/tsig-op.c
src/libknot/tsig-op.h
-src/libknot/util/conv.c
-src/libknot/util/conv.h
src/tests/libknot/libknot/tsig_tests.h
src/tests/libknot/libknot/tsig_tests.c
src/knot/zone/semantic-check.c
src/knot/zone/semantic-check.h
src/tests/xfr_tests.h
src/tests/xfr_tests.c
+src/common/base64.c
+src/common/base64.h
+doc/knot.texi
+doc/configuration.texi
+doc/indices.texi
+doc/installation.texi
+doc/introduction.texi
+doc/migration.texi
+doc/reference.texi
+doc/requirements.texi
+doc/security.texi
+doc/troubleshooting.texi
+doc/version.texi
+src/libknot/zone/zone-diff.h
+src/libknot/zone/zone-diff.c
+
+doc/running.texi
diff --git a/Knot.includes b/Knot.includes
index 8184956..8184956 100644..100755
--- a/Knot.includes
+++ b/Knot.includes
diff --git a/Makefile.am b/Makefile.am
index 30ea215..a3ae405 100644..100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,2 +1,2 @@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src samples
+SUBDIRS = src samples doc
diff --git a/Makefile.in b/Makefile.in
index 3511c13..f8bcf7d 100644..100755
--- a/Makefile.in
+++ b/Makefile.in
@@ -227,7 +227,7 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src samples
+SUBDIRS = src samples doc
all: all-recursive
.SUFFIXES:
diff --git a/NEWS b/NEWS
index e69de29..e69de29 100644..100755
--- a/NEWS
+++ b/NEWS
diff --git a/README b/README
index 69163ac..d50168f 100644..100755
--- a/README
+++ b/README
@@ -40,7 +40,7 @@ $ sudo dpkg -i liburcu0_0.5.4-1_amd64.deb
$ sudo dpkg -i liburcu-dev_0.5.4-1_amd64.deb
# Go for the real thing
-$ git clone git://git.nic.cz/knot
+$ git clone git://git.nic.cz/knot-dns.git
$ cd knot
$ autoreconf -if
$ ./configure
@@ -75,6 +75,10 @@ $ autoreconf -if
$ ./configure
$ make && sudo make install
+It is also present in port tree, so you can install it from there.
+$ cd /usr/ports/dns/knot
+$ sudo make install
+
Installation on OpenBSD/NetBSD
==============================
Also works for OS X, if you don't want to install gcc from ports.
@@ -82,6 +86,7 @@ Prerequisites:
- liburcu needs patch for absent compiler TLS capability
- patch is located in "scripts/urcu-tls-compat.patch"
- patch compatible with liburcu-0.6.7
+- liburcu >= 0.7.0 works out of the box
$ mkdir liburcu && cd liburcu
$ wget "http://lttng.org/files/urcu/userspace-rcu-0.6.7.tar.bz2"
@@ -124,6 +129,10 @@ is '-c' that specifies config file for our server. Compiled zones are saved to
storage defined in 'storage' variable in configuration.
$ knotc -h # see what it can do
+
+Copy zone to the specified directory and compile.
+
+$ mkdir -p /tmp/knot-minimal/samples; cp samples/example.com.zone /tmp/knot-minimal/samples/
$ knotc -c myserver.conf compile # compile zone files to binary format
Third, lets load server. You can do this by running 'knotd' directly, or with
diff --git a/RELNOTES b/RELNOTES
index 01614be..42e5206 100644..100755
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,3 +1,66 @@
+v1.1.0 - Aug 31, 2012
+---------------------
+
+Bugfixes:
+ * Syncing journal to zone was not updating the compiled zone database.
+
+Other improvements:
+ * Better checks of corrupted zone database.
+
+
+v1.1.0-rc2 - Aug 23, 2012
+-------------------------
+
+New features:
+ * Signing SOA with TSIG queries when checking zone version with master.
+
+Bugfixes:
+ * Fixed ixfr-from-differences journal generation in case of IPSECKEY
+ and APL records.
+ * Fixed possible leak on server shutdown with a pending transfer.
+
+Other improvements:
+ * Improved user manual.
+
+
+v1.1.0-rc1 - Aug 17, 2012
+-------------------------
+
+New features:
+ * Optionally disable ANY queries for authoritative answers.
+ * Dropping identical records in zone and incoming transfers.
+ * Support for '/' in zone names.
+ * Generating journal from reloaded zone (EXPERIMENTAL).
+ * Outgoing-only interfaces in configuration file.
+ * Following DNAME if the synthetized name is in the same zone.
+
+Bugfixes:
+ * Crash when zone contained RRSIG signing a CNAME, but did not
+ contain the CNAME.
+ * Malformed packets parsing.
+ * Failed IXFR caused memory leaks.
+ * Failed IXFR might have resulted in inconsistent zone structures.
+ * Fixed answering to +dnssec queries when NSEC3 chain is corrupted.
+ * Fixed answering when transitioning from NSEC3 to NSEC.
+ * Fixed answering when zone contains multiple NSEC3 chains.
+ * Handling RRSets with different TTLs - TTL from the first RR is used.
+ * Synchronization of zone reload and zone transfers.
+ * Fixed build on NetBSD 5 and FreeBSD.
+ * Fixed binding to both IPv4 and IPv6 at the same time on special
+ interfaces.
+ * Fixed access rights of created files.
+ * Semantic checks corrupted RDATA domain names which are covered by
+ wildcard in the same zone.
+
+Other improvements:
+ * IXFR-in optimized.
+ * Many zones loading optimized.
+ * More detailed log messages (mostly transfer-related).
+ * Copying Question section to error responses.
+ * Using zone name from config file as default origin in zone file.
+ * Additional records are now added to response also from
+ wildcard-covered names.
+
v1.0.6 - Jun 13, 2012
---------------------
diff --git a/aclocal.m4 b/aclocal.m4
index 5184c03..5184c03 100644..100755
--- a/aclocal.m4
+++ b/aclocal.m4
diff --git a/configure b/configure
index 7404088..6cec3a0 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for knot 1.0.6.
+# Generated by GNU Autoconf 2.68 for knot 1.1.0.
#
# Report bugs to <knot-dns@labs.nic.cz>.
#
@@ -570,8 +570,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='knot'
PACKAGE_TARNAME='knot'
-PACKAGE_VERSION='1.0.6'
-PACKAGE_STRING='knot 1.0.6'
+PACKAGE_VERSION='1.1.0'
+PACKAGE_STRING='knot 1.1.0'
PACKAGE_BUGREPORT='knot-dns@labs.nic.cz'
PACKAGE_URL=''
@@ -749,6 +749,7 @@ enable_ldns
enable_debug
enable_debuglevel
enable_recvmmsg
+enable_lto
'
ac_precious_vars='build_alias
host_alias
@@ -1303,7 +1304,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures knot 1.0.6 to adapt to many kinds of systems.
+\`configure' configures knot 1.1.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1373,7 +1374,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of knot 1.0.6:";;
+ short | recursive ) echo "Configuration of knot 1.1.0:";;
esac
cat <<\_ACEOF
@@ -1391,7 +1392,7 @@ Optional Features:
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--enable-ldns=yes|no Enable tests with ldns [default=no]
- --enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler
+ --enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler,stash
compile selected debug modules [default=none]
--enable-debuglevel=brief|verbose|details
enable given debug level [default=disabled]
@@ -1399,6 +1400,8 @@ Optional Features:
enable recvmmsg() network API under Linux (kernel
support required) (set to 'no' if you have trouble
running server under valgrind) [default=yes]
+ --enable-lto=yes|no enable link-time optimizations, enable if not broken
+ for some extra speed [default=no]
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1491,7 +1494,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-knot configure 1.0.6
+knot configure 1.1.0
generated by GNU Autoconf 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
@@ -2041,7 +2044,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by knot $as_me 1.0.6, which was
+It was created by knot $as_me 1.1.0, which was
generated by GNU Autoconf 2.68. Invocation command line was
$ $0 $@
@@ -2859,7 +2862,7 @@ fi
# Define the identity of the package.
PACKAGE='knot'
- VERSION='1.0.6'
+ VERSION='1.1.0'
cat >>confdefs.h <<_ACEOF
@@ -4769,6 +4772,677 @@ $as_echo "#define HAVE_SSSE3 /**/" >>confdefs.h
+# Checks for programs.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ # for automake 1.12
+AM_PROG_AR
+
# Enable maintainer mode by default for development
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to disable maintainer-specific portions of Makefiles" >&5
@@ -11944,675 +12618,6 @@ CC="$lt_save_CC"
-# Checks for programs.
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-if test -n "$ac_tool_prefix"; then
- # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- ac_cv_prog_CC="${ac_tool_prefix}gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-fi
-if test -z "$ac_cv_prog_CC"; then
- ac_ct_CC=$CC
- # Extract the first word of "gcc", so it can be a program name with args.
-set dummy gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$ac_ct_CC"; then
- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- ac_cv_prog_ac_ct_CC="gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
- if test "x$ac_ct_CC" = x; then
- CC=""
- else
- case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
- CC=$ac_ct_CC
- fi
-else
- CC="$ac_cv_prog_CC"
-fi
-
-if test -z "$CC"; then
- if test -n "$ac_tool_prefix"; then
- # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- ac_cv_prog_CC="${ac_tool_prefix}cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
- fi
-fi
-if test -z "$CC"; then
- # Extract the first word of "cc", so it can be a program name with args.
-set dummy cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
- ac_prog_rejected=no
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
- ac_prog_rejected=yes
- continue
- fi
- ac_cv_prog_CC="cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-if test $ac_prog_rejected = yes; then
- # We found a bogon in the path, so make sure we never use it.
- set dummy $ac_cv_prog_CC
- shift
- if test $# != 0; then
- # We chose a different compiler from the bogus one.
- # However, it has the same basename, so the bogon will be chosen
- # first if we set CC to just the basename; use the full file name.
- shift
- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
- fi
-fi
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-fi
-if test -z "$CC"; then
- if test -n "$ac_tool_prefix"; then
- for ac_prog in cl.exe
- do
- # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
-set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
- test -n "$CC" && break
- done
-fi
-if test -z "$CC"; then
- ac_ct_CC=$CC
- for ac_prog in cl.exe
-do
- # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$ac_ct_CC"; then
- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
- ac_cv_prog_ac_ct_CC="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
- test -n "$ac_ct_CC" && break
-done
-
- if test "x$ac_ct_CC" = x; then
- CC=""
- else
- case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
- CC=$ac_ct_CC
- fi
-fi
-
-fi
-
-
-test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5; }
-
-# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
-set X $ac_compile
-ac_compiler=$2
-for ac_option in --version -v -V -qversion; do
- { { ac_try="$ac_compiler $ac_option >&5"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
- (eval "$ac_compiler $ac_option >&5") 2>conftest.err
- ac_status=$?
- if test -s conftest.err; then
- sed '10a\
-... rest of stderr output deleted ...
- 10q' conftest.err >conftest.er1
- cat conftest.er1 >&5
- fi
- rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }
-done
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if ${ac_cv_c_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-#ifndef __GNUC__
- choke me
-#endif
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_compiler_gnu=yes
-else
- ac_compiler_gnu=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-ac_cv_c_compiler_gnu=$ac_compiler_gnu
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-$as_echo "$ac_cv_c_compiler_gnu" >&6; }
-if test $ac_compiler_gnu = yes; then
- GCC=yes
-else
- GCC=
-fi
-ac_test_CFLAGS=${CFLAGS+set}
-ac_save_CFLAGS=$CFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
-$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if ${ac_cv_prog_cc_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_save_c_werror_flag=$ac_c_werror_flag
- ac_c_werror_flag=yes
- ac_cv_prog_cc_g=no
- CFLAGS="-g"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_g=yes
-else
- CFLAGS=""
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-else
- ac_c_werror_flag=$ac_save_c_werror_flag
- CFLAGS="-g"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_g=yes
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- ac_c_werror_flag=$ac_save_c_werror_flag
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-$as_echo "$ac_cv_prog_cc_g" >&6; }
-if test "$ac_test_CFLAGS" = set; then
- CFLAGS=$ac_save_CFLAGS
-elif test $ac_cv_prog_cc_g = yes; then
- if test "$GCC" = yes; then
- CFLAGS="-g -O2"
- else
- CFLAGS="-g"
- fi
-else
- if test "$GCC" = yes; then
- CFLAGS="-O2"
- else
- CFLAGS=
- fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
-$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if ${ac_cv_prog_cc_c89+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_prog_cc_c89=no
-ac_save_CC=$CC
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
-struct buf { int x; };
-FILE * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
- char **p;
- int i;
-{
- return p[i];
-}
-static char *f (char * (*g) (char **, int), char **p, ...)
-{
- char *s;
- va_list v;
- va_start (v,p);
- s = g (p, va_arg (v,int));
- va_end (v);
- return s;
-}
-
-/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
- function prototypes and stuff, but not '\xHH' hex character constants.
- These don't provoke an error unfortunately, instead are silently treated
- as 'x'. The following induces an error, until -std is added to get
- proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
- array size at least. It's necessary to write '\x00'==0 to get something
- that's true only with -std. */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
-
-/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
- inside strings and character constants. */
-#define FOO(x) 'x'
-int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
-
-int test (int i, double x);
-struct s1 {int (*f) (int a);};
-struct s2 {int (*f) (double a);};
-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
-int argc;
-char **argv;
-int
-main ()
-{
-return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
- ;
- return 0;
-}
-_ACEOF
-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
- -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
-do
- CC="$ac_save_CC $ac_arg"
- if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_c89=$ac_arg
-fi
-rm -f core conftest.err conftest.$ac_objext
- test "x$ac_cv_prog_cc_c89" != "xno" && break
-done
-rm -f conftest.$ac_ext
-CC=$ac_save_CC
-
-fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c89" in
- x)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-$as_echo "none needed" >&6; } ;;
- xno)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-$as_echo "unsupported" >&6; } ;;
- *)
- CC="$CC $ac_cv_prog_cc_c89"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
-$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
-esac
-if test "x$ac_cv_prog_cc_c89" != xno; then :
-
-fi
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-depcc="$CC" am_compiler_list=
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
-$as_echo_n "checking dependency style of $depcc... " >&6; }
-if ${am_cv_CC_dependencies_compiler_type+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
- # We make a subdir and do the tests there. Otherwise we can end up
- # making bogus files that we don't know about and never remove. For
- # instance it was reported that on HP-UX the gcc test will end up
- # making a dummy file named `D' -- because `-MD' means `put the output
- # in D'.
- mkdir conftest.dir
- # Copy depcomp to subdir because otherwise we won't find it if we're
- # using a relative directory.
- cp "$am_depcomp" conftest.dir
- cd conftest.dir
- # We will build objects and dependencies in a subdirectory because
- # it helps to detect inapplicable dependency modes. For instance
- # both Tru64's cc and ICC support -MD to output dependencies as a
- # side effect of compilation, but ICC will put the dependencies in
- # the current directory while Tru64 will put them in the object
- # directory.
- mkdir sub
-
- am_cv_CC_dependencies_compiler_type=none
- if test "$am_compiler_list" = ""; then
- am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
- fi
- am__universal=false
- case " $depcc " in #(
- *\ -arch\ *\ -arch\ *) am__universal=true ;;
- esac
-
- for depmode in $am_compiler_list; do
- # Setup a source with many dependencies, because some compilers
- # like to wrap large dependency lists on column 80 (with \), and
- # we should not choose a depcomp mode which is confused by this.
- #
- # We need to recreate these files for each test, as the compiler may
- # overwrite some of them when testing with obscure command lines.
- # This happens at least with the AIX C compiler.
- : > sub/conftest.c
- for i in 1 2 3 4 5 6; do
- echo '#include "conftst'$i'.h"' >> sub/conftest.c
- # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
- # Solaris 8's {/usr,}/bin/sh.
- touch sub/conftst$i.h
- done
- echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
-
- # We check with `-c' and `-o' for the sake of the "dashmstdout"
- # mode. It turns out that the SunPro C++ compiler does not properly
- # handle `-M -o', and we need to detect this. Also, some Intel
- # versions had trouble with output in subdirs
- am__obj=sub/conftest.${OBJEXT-o}
- am__minus_obj="-o $am__obj"
- case $depmode in
- gcc)
- # This depmode causes a compiler race in universal mode.
- test "$am__universal" = false || continue
- ;;
- nosideeffect)
- # after this tag, mechanisms are not by side-effect, so they'll
- # only be used when explicitly requested
- if test "x$enable_dependency_tracking" = xyes; then
- continue
- else
- break
- fi
- ;;
- msvisualcpp | msvcmsys)
- # This compiler won't grok `-c -o', but also, the minuso test has
- # not run yet. These depmodes are late enough in the game, and
- # so weak that their functioning should not be impacted.
- am__obj=conftest.${OBJEXT-o}
- am__minus_obj=
- ;;
- none) break ;;
- esac
- if depmode=$depmode \
- source=sub/conftest.c object=$am__obj \
- depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
- $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
- >/dev/null 2>conftest.err &&
- grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
- grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
- grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
- ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
- # icc doesn't choke on unknown options, it will just issue warnings
- # or remarks (even with -Werror). So we grep stderr for any message
- # that says an option was ignored or not supported.
- # When given -MP, icc 7.0 and 7.1 complain thusly:
- # icc: Command line warning: ignoring option '-M'; no argument required
- # The diagnosis changed in icc 8.0:
- # icc: Command line remark: option '-MP' not supported
- if (grep 'ignoring option' conftest.err ||
- grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
- am_cv_CC_dependencies_compiler_type=$depmode
- break
- fi
- fi
- done
-
- cd ..
- rm -rf conftest.dir
-else
- am_cv_CC_dependencies_compiler_type=none
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
-$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
-CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
-
- if
- test "x$enable_dependency_tracking" != xno \
- && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
- am__fastdepCC_TRUE=
- am__fastdepCC_FALSE='#'
-else
- am__fastdepCC_TRUE='#'
- am__fastdepCC_FALSE=
-fi
-
-
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reentrant lex" >&5
$as_echo_n "checking for reentrant lex... " >&6; }
if ${ac_cv_path_LEX+:} false; then :
@@ -12883,8 +12888,8 @@ done
test -n "$YACC" || YACC="yacc"
YACC_BISON=`bison --version | awk '{print $1;exit}'`
-if test "$YACC_BISON" != "bison"; then
- as_fn_error $? "GNU bison needed for reentrant parsers, set the \$YACC variable before running configure" "$LINENO" 5
+if test "x$YACC_BISON" != "xbison"; then :
+ as_fn_error $? "GNU bison needed for reentrant parsers, set the \$YACC variable before running configure" "$LINENO" 5
fi
@@ -13172,6 +13177,9 @@ $as_echo "#define KNOT_HASH_DEBUG 1" >>confdefs.h
compiler)
$as_echo "#define KNOT_COMPILER_DEBUG 1" >>confdefs.h
;;
+ stash)
+$as_echo "#define KNOT_STASH_DEBUG 1" >>confdefs.h
+ ;;
esac
done
@@ -13238,7 +13246,10 @@ fi
# Check for link time optimizations support and predictive commoning
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-flto\"" >&5
+# Check whether --enable-lto was given.
+if test "${enable_lto+set}" = set; then :
+ enableval=$enable_lto; case "${enableval}" in
+ yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-flto\"" >&5
$as_echo_n "checking whether C compiler accepts \"-flto\"... " >&6; }
ax_save_FLAGS=$CFLAGS
CFLAGS=""-flto""
@@ -13268,6 +13279,12 @@ if test "x$ax_check_compiler_flags" = xyes; then
else
:
fi
+ ;;
+ no) ;;
+ *) as_fn_error $? "bad value ${enableval} for --enable-lto" "$LINENO" 5 ;;
+ esac
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-fpredictive-commoning\"" >&5
$as_echo_n "checking whether C compiler accepts \"-fpredictive-commoning\"... " >&6; }
@@ -13760,9 +13777,9 @@ if test "$ac_res" != no; then :
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing crc32" >&5
-$as_echo_n "checking for library containing crc32... " >&6; }
-if ${ac_cv_search_crc32+:} false; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing adler32" >&5
+$as_echo_n "checking for library containing adler32... " >&6; }
+if ${ac_cv_search_adler32+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -13775,11 +13792,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
#ifdef __cplusplus
extern "C"
#endif
-char crc32 ();
+char adler32 ();
int
main ()
{
-return crc32 ();
+return adler32 ();
;
return 0;
}
@@ -13792,25 +13809,25 @@ for ac_lib in '' z; do
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_search_crc32=$ac_res
+ ac_cv_search_adler32=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if ${ac_cv_search_crc32+:} false; then :
+ if ${ac_cv_search_adler32+:} false; then :
break
fi
done
-if ${ac_cv_search_crc32+:} false; then :
+if ${ac_cv_search_adler32+:} false; then :
else
- ac_cv_search_crc32=no
+ ac_cv_search_adler32=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_crc32" >&5
-$as_echo "$ac_cv_search_crc32" >&6; }
-ac_res=$ac_cv_search_crc32
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_adler32" >&5
+$as_echo "$ac_cv_search_adler32" >&6; }
+ac_res=$ac_cv_search_adler32
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
@@ -13845,7 +13862,7 @@ fi
done
-for ac_header in arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h cap-ng.h syslog.h unistd.h urcu.h ev.h
+for ac_header in arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -14526,7 +14543,63 @@ fi
done
-ac_config_files="$ac_config_files Makefile samples/Makefile src/Makefile"
+# Check for cpu_set_t/cpuset_t compatibility
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+cpu_set_t set; CPU_ZERO(&set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_LINUX 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread_np.h>
+int
+main ()
+{
+cpuset_t set; CPU_ZERO(&set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_BSD 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sched.h>
+int
+main ()
+{
+cpuset_t* set = cpuset_create(); cpuset_destroy(set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_NETBSD 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ac_config_files="$ac_config_files Makefile samples/Makefile src/Makefile doc/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -14653,14 +14726,14 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
-if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
- as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
@@ -15070,7 +15143,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by knot $as_me 1.0.6, which was
+This file was extended by knot $as_me 1.1.0, which was
generated by GNU Autoconf 2.68. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -15136,7 +15209,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-knot config.status 1.0.6
+knot config.status 1.1.0
configured by $0, generated by GNU Autoconf 2.68,
with options \\"\$ac_cs_config\\"
@@ -15548,6 +15621,7 @@ do
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;;
"src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+ "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
diff --git a/configure.ac b/configure.ac
index 5875517..eeca939 100644..100755
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
AC_PREREQ([2.65])
-AC_INIT([knot], [1.0.6], [knot-dns@labs.nic.cz])
+AC_INIT([knot], [1.1.0], [knot-dns@labs.nic.cz])
AM_INIT_AUTOMAKE([gnu -Wall -Werror])
AC_CONFIG_SRCDIR([src/knot/main.c])
AC_CONFIG_HEADERS([src/config.h])
@@ -9,6 +9,11 @@ AC_CONFIG_MACRO_DIR([m4])
AC_USE_SYSTEM_EXTENSIONS([_GNU_SOURCE])
AX_EXT
+# Checks for programs.
+AC_PROG_CC
+m4_pattern_allow([AM_PROG_AR]) # for automake 1.12
+AM_PROG_AR
+
# Enable maintainer mode by default for development
AM_MAINTAINER_MODE([enable])
@@ -16,9 +21,6 @@ AM_MAINTAINER_MODE([enable])
AC_PROG_LIBTOOL
LT_INIT
-# Checks for programs.
-AC_PROG_CC
-
AC_CACHE_CHECK([for reentrant lex], [ac_cv_path_LEX],
[AC_PATH_PROGS_FEATURE_CHECK([LEX], [$LEX flex gflex],
[cat >conftest.l <<_ACEOF
@@ -47,9 +49,8 @@ AM_PROG_LEX
AC_PROG_YACC
YACC_BISON=`bison --version | awk '{print $1;exit}'`
-if test "$YACC_BISON" != "bison"; then
- AC_MSG_ERROR([GNU bison needed for reentrant parsers, set the \$YACC variable before running configure])
-fi
+AS_IF([test "x$YACC_BISON" != "xbison"],
+ [AC_MSG_ERROR([GNU bison needed for reentrant parsers, set the \$YACC variable before running configure])])
AC_PROG_INSTALL
# Set compiler compatibility flags
@@ -57,7 +58,7 @@ AC_PROG_CPP_WERROR
AC_PROG_CC_C99
AC_ARG_ENABLE([ldns],
- AC_HELP_STRING([--enable-ldns=yes|no], [Enable tests with ldns [default=no]]),
+ AS_HELP_STRING([--enable-ldns=yes|no], [Enable tests with ldns [default=no]]),
[case "${enableval}" in
yes) AC_SEARCH_LIBS([ldns_rr_list_pop_rrset], [ldns], [AC_DEFINE([HAVE_LDNS], [1], [ldns present])],
AC_MSG_ERROR([ldns not found])) ;;
@@ -67,7 +68,7 @@ AC_ARG_ENABLE([ldns],
# Debug modules
AC_ARG_ENABLE([debug],
- AS_HELP_STRING([--enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler],
+ AS_HELP_STRING([--enable-debug=server,zones,xfr,packet,dname,rr,ns,hash,compiler,stash],
[compile selected debug modules [default=none]]),
[
echo ${enableval}|tr "," "\n"|while read val; do
@@ -81,6 +82,7 @@ AC_ARG_ENABLE([debug],
ns) AC_DEFINE([KNOT_NS_DEBUG], [1], [Nameserver debug.]) ;;
hash) AC_DEFINE([KNOT_HASH_DEBUG], [1], [Hashtable debug.]) ;;
compiler) AC_DEFINE([KNOT_COMPILER_DEBUG], [1], [Zone compiler debug.]) ;;
+ stash) AC_DEFINE([KNOT_STASH_DEBUG], [1], [Hash table stash debug.]) ;;
esac
done
], [])
@@ -123,7 +125,15 @@ AC_ARG_ENABLE([recvmmsg],
])
# Check for link time optimizations support and predictive commoning
-AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], [])
+AC_ARG_ENABLE([lto],
+ AS_HELP_STRING([--enable-lto=yes|no], [enable link-time optimizations, enable if not broken for some extra speed [default=no]]),
+ [case "${enableval}" in
+ yes) AX_CHECK_COMPILER_FLAGS("-flto", [CFLAGS="$CFLAGS -flto"], []) ;;
+ no) ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-lto]) ;;
+ esac], [
+ ])
+
AX_CHECK_COMPILER_FLAGS("-fpredictive-commoning", [CFLAGS="$CFLAGS -fpredictive-commoning"], [])
# Checks for libraries.
@@ -139,12 +149,12 @@ AC_SEARCH_LIBS([dlopen], [dl])
AC_SEARCH_LIBS([clock_gettime], [rt])
AC_SEARCH_LIBS([OpenSSL_add_all_digests], [crypto],[], [AC_MSG_ERROR([libcrypto not found])])
AC_SEARCH_LIBS([capng_apply], [cap-ng])
-AC_SEARCH_LIBS([crc32], [z])
+AC_SEARCH_LIBS([adler32], [z])
#AC_SEARCH_LIBS([ldns_rr_list_pop_rrset], [ldns], [], [AC_MSG_ERROR([libldns not found])])
# Checks for header files.
AC_HEADER_RESOLV
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h cap-ng.h syslog.h unistd.h urcu.h ev.h])
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h malloc.h netdb.h netinet/in_systm.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/time.h cap-ng.h syslog.h unistd.h urcu.h ev.h pthread_np.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
@@ -166,7 +176,16 @@ AC_FUNC_FORK
AC_FUNC_MMAP
AC_CHECK_FUNCS([gethostbyname gettimeofday clock_gettime memalign memmove memset munmap regcomp pselect select socket sqrt strcasecmp strchr strdup strerror strncasecmp strtol strtoul poll epoll_wait kqueue setgroups sendmmsg madvise pthread_setaffinity_np])
+# Check for cpu_set_t/cpuset_t compatibility
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[cpu_set_t set; CPU_ZERO(&set);]])],
+[AC_DEFINE(HAVE_CPUSET_LINUX, 1, [Define if Linux-like cpu_set_t exists.])])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread_np.h>]], [[cpuset_t set; CPU_ZERO(&set);]])],
+[AC_DEFINE(HAVE_CPUSET_BSD, 1, [Define if FreeBSD-like cpuset_t exists.])])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sched.h>]], [[cpuset_t* set = cpuset_create(); cpuset_destroy(set);]])],
+[AC_DEFINE(HAVE_CPUSET_NETBSD, 1, [Define if cpuset_t and cpuset(3) exists.])])
+
AC_CONFIG_FILES([Makefile
samples/Makefile
- src/Makefile])
+ src/Makefile
+ doc/Makefile])
AC_OUTPUT
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100755
index 0000000..b9dc5b2
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,17 @@
+*.info
+*.pdf
+*.html
+*.dvi
+*.aux
+*.cp
+*.fn
+*.ky
+*.log
+*.pg
+*.toc
+*.tp
+*.vr
+texinfo.tex
+version.texi
+mdate-sh
+stamp-vti
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100755
index 0000000..9cec847
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,10 @@
+info_TEXINFOS = knot.texi
+knot_TEXINFOS = \
+ introduction.texi \
+ requirements.texi \
+ installation.texi \
+ configuration.texi \
+ reference.texi \
+ security.texi \
+ troubleshooting.texi \
+ migration.texi
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100755
index 0000000..b45e192
--- /dev/null
+++ b/doc/Makefile.in
@@ -0,0 +1,664 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in 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.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = doc
+DIST_COMMON = $(knot_TEXINFOS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/stamp-vti \
+ $(srcdir)/version.texi mdate-sh texinfo.tex
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compiler_flags.m4 \
+ $(top_srcdir)/m4/ax_ext.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+INFO_DEPS = $(srcdir)/knot.info
+am__TEXINFO_TEX_DIR = $(srcdir)
+DVIS = knot.dvi
+PDFS = knot.pdf
+PSS = knot.ps
+HTMLS = knot.html
+TEXINFOS = knot.texi
+TEXI2DVI = texi2dvi
+TEXI2PDF = $(TEXI2DVI) --pdf --batch
+MAKEINFOHTML = $(MAKEINFO) --html
+AM_MAKEINFOHTMLFLAGS = $(AM_MAKEINFOFLAGS)
+DVIPS = dvips
+am__installdirs = "$(DESTDIR)$(infodir)"
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIMD_FLAGS = @SIMD_FLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+info_TEXINFOS = knot.texi
+knot_TEXINFOS = \
+ introduction.texi \
+ requirements.texi \
+ installation.texi \
+ configuration.texi \
+ reference.texi \
+ security.texi \
+ troubleshooting.texi \
+ migration.texi
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .dvi .html .info .pdf .ps .texi
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu doc/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+.texi.info:
+ restore=: && backupdir="$(am__leading_dot)am$$$$" && \
+ am__cwd=`pwd` && $(am__cd) $(srcdir) && \
+ rm -rf $$backupdir && mkdir $$backupdir && \
+ if ($(MAKEINFO) --version) >/dev/null 2>&1; then \
+ for f in $@ $@-[0-9] $@-[0-9][0-9] $(@:.info=).i[0-9] $(@:.info=).i[0-9][0-9]; do \
+ if test -f $$f; then mv $$f $$backupdir; restore=mv; else :; fi; \
+ done; \
+ else :; fi && \
+ cd "$$am__cwd"; \
+ if $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \
+ -o $@ $<; \
+ then \
+ rc=0; \
+ $(am__cd) $(srcdir); \
+ else \
+ rc=$$?; \
+ $(am__cd) $(srcdir) && \
+ $$restore $$backupdir/* `echo "./$@" | sed 's|[^/]*$$||'`; \
+ fi; \
+ rm -rf $$backupdir; exit $$rc
+
+.texi.dvi:
+ TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \
+ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \
+ $(TEXI2DVI) $<
+
+.texi.pdf:
+ TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \
+ MAKEINFO='$(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir)' \
+ $(TEXI2PDF) $<
+
+.texi.html:
+ rm -rf $(@:.html=.htp)
+ if $(MAKEINFOHTML) $(AM_MAKEINFOHTMLFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) \
+ -o $(@:.html=.htp) $<; \
+ then \
+ rm -rf $@; \
+ if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \
+ mv $(@:.html=) $@; else mv $(@:.html=.htp) $@; fi; \
+ else \
+ if test ! -d $(@:.html=.htp) && test -d $(@:.html=); then \
+ rm -rf $(@:.html=); else rm -Rf $(@:.html=.htp) $@; fi; \
+ exit 1; \
+ fi
+$(srcdir)/knot.info: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS)
+knot.dvi: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS)
+knot.pdf: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS)
+knot.html: knot.texi $(srcdir)/version.texi $(knot_TEXINFOS)
+$(srcdir)/version.texi: @MAINTAINER_MODE_TRUE@ $(srcdir)/stamp-vti
+$(srcdir)/stamp-vti: knot.texi $(top_srcdir)/configure
+ @(dir=.; test -f ./knot.texi || dir=$(srcdir); \
+ set `$(SHELL) $(srcdir)/mdate-sh $$dir/knot.texi`; \
+ echo "@set UPDATED $$1 $$2 $$3"; \
+ echo "@set UPDATED-MONTH $$2 $$3"; \
+ echo "@set EDITION $(VERSION)"; \
+ echo "@set VERSION $(VERSION)") > vti.tmp
+ @cmp -s vti.tmp $(srcdir)/version.texi \
+ || (echo "Updating $(srcdir)/version.texi"; \
+ cp vti.tmp $(srcdir)/version.texi)
+ -@rm -f vti.tmp
+ @cp $(srcdir)/version.texi $@
+
+mostlyclean-vti:
+ -rm -f vti.tmp
+
+maintainer-clean-vti:
+@MAINTAINER_MODE_TRUE@ -rm -f $(srcdir)/stamp-vti $(srcdir)/version.texi
+.dvi.ps:
+ TEXINPUTS="$(am__TEXINFO_TEX_DIR)$(PATH_SEPARATOR)$$TEXINPUTS" \
+ $(DVIPS) -o $@ $<
+
+uninstall-dvi-am:
+ @$(NORMAL_UNINSTALL)
+ @list='$(DVIS)'; test -n "$(dvidir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(dvidir)/$$f'"; \
+ rm -f "$(DESTDIR)$(dvidir)/$$f"; \
+ done
+
+uninstall-html-am:
+ @$(NORMAL_UNINSTALL)
+ @list='$(HTMLS)'; test -n "$(htmldir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " rm -rf '$(DESTDIR)$(htmldir)/$$f'"; \
+ rm -rf "$(DESTDIR)$(htmldir)/$$f"; \
+ done
+
+uninstall-info-am:
+ @$(PRE_UNINSTALL)
+ @if test -d '$(DESTDIR)$(infodir)' && \
+ (install-info --version && \
+ install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \
+ list='$(INFO_DEPS)'; \
+ for file in $$list; do \
+ relfile=`echo "$$file" | sed 's|^.*/||'`; \
+ echo " install-info --info-dir='$(DESTDIR)$(infodir)' --remove '$(DESTDIR)$(infodir)/$$relfile'"; \
+ if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$relfile"; \
+ then :; else test ! -f "$(DESTDIR)$(infodir)/$$relfile" || exit 1; fi; \
+ done; \
+ else :; fi
+ @$(NORMAL_UNINSTALL)
+ @list='$(INFO_DEPS)'; \
+ for file in $$list; do \
+ relfile=`echo "$$file" | sed 's|^.*/||'`; \
+ relfile_i=`echo "$$relfile" | sed 's|\.info$$||;s|$$|.i|'`; \
+ (if test -d "$(DESTDIR)$(infodir)" && cd "$(DESTDIR)$(infodir)"; then \
+ echo " cd '$(DESTDIR)$(infodir)' && rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]"; \
+ rm -f $$relfile $$relfile-[0-9] $$relfile-[0-9][0-9] $$relfile_i[0-9] $$relfile_i[0-9][0-9]; \
+ else :; fi); \
+ done
+
+uninstall-pdf-am:
+ @$(NORMAL_UNINSTALL)
+ @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(pdfdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(pdfdir)/$$f"; \
+ done
+
+uninstall-ps-am:
+ @$(NORMAL_UNINSTALL)
+ @list='$(PSS)'; test -n "$(psdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(psdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(psdir)/$$f"; \
+ done
+
+dist-info: $(INFO_DEPS)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ list='$(INFO_DEPS)'; \
+ for base in $$list; do \
+ case $$base in \
+ $(srcdir)/*) base=`echo "$$base" | sed "s|^$$srcdirstrip/||"`;; \
+ esac; \
+ if test -f $$base; then d=.; else d=$(srcdir); fi; \
+ base_i=`echo "$$base" | sed 's|\.info$$||;s|$$|.i|'`; \
+ for file in $$d/$$base $$d/$$base-[0-9] $$d/$$base-[0-9][0-9] $$d/$$base_i[0-9] $$d/$$base_i[0-9][0-9]; do \
+ if test -f $$file; then \
+ relfile=`expr "$$file" : "$$d/\(.*\)"`; \
+ test -f "$(distdir)/$$relfile" || \
+ cp -p $$file "$(distdir)/$$relfile"; \
+ else :; fi; \
+ done; \
+ done
+
+mostlyclean-aminfo:
+ -rm -rf knot.aux knot.cp knot.cps knot.fn knot.fns knot.ky knot.kys \
+ knot.log knot.pg knot.pgs knot.st knot.sts knot.tmp knot.toc \
+ knot.tp knot.tps knot.vr
+
+clean-aminfo:
+ -test -z "knot.dvi knot.pdf knot.ps knot.html" \
+ || rm -rf knot.dvi knot.pdf knot.ps knot.html
+
+maintainer-clean-aminfo:
+ @list='$(INFO_DEPS)'; for i in $$list; do \
+ i_i=`echo "$$i" | sed 's|\.info$$||;s|$$|.i|'`; \
+ echo " rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]"; \
+ rm -f $$i $$i-[0-9] $$i-[0-9][0-9] $$i_i[0-9] $$i_i[0-9][0-9]; \
+ done
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-info
+check-am: all-am
+check: check-am
+all-am: Makefile $(INFO_DEPS)
+installdirs:
+ for dir in "$(DESTDIR)$(infodir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-aminfo clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am: $(DVIS)
+
+html: html-am
+
+html-am: $(HTMLS)
+
+info: info-am
+
+info-am: $(INFO_DEPS)
+
+install-data-am: install-info-am
+
+install-dvi: install-dvi-am
+
+install-dvi-am: $(DVIS)
+ @$(NORMAL_INSTALL)
+ test -z "$(dvidir)" || $(MKDIR_P) "$(DESTDIR)$(dvidir)"
+ @list='$(DVIS)'; test -n "$(dvidir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dvidir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(dvidir)" || exit $$?; \
+ done
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am: $(HTMLS)
+ @$(NORMAL_INSTALL)
+ test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"
+ @list='$(HTMLS)'; list2=; test -n "$(htmldir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p" || test -d "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ $(am__strip_dir) \
+ if test -d "$$d$$p"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(MKDIR_P) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \
+ echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f" || exit $$?; \
+ else \
+ list2="$$list2 $$d$$p"; \
+ fi; \
+ done; \
+ test -z "$$list2" || { echo "$$list2" | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \
+ done; }
+install-info: install-info-am
+
+install-info-am: $(INFO_DEPS)
+ @$(NORMAL_INSTALL)
+ test -z "$(infodir)" || $(MKDIR_P) "$(DESTDIR)$(infodir)"
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \
+ for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ esac; \
+ if test -f $$file; then d=.; else d=$(srcdir); fi; \
+ file_i=`echo "$$file" | sed 's|\.info$$||;s|$$|.i|'`; \
+ for ifile in $$d/$$file $$d/$$file-[0-9] $$d/$$file-[0-9][0-9] \
+ $$d/$$file_i[0-9] $$d/$$file_i[0-9][0-9] ; do \
+ if test -f $$ifile; then \
+ echo "$$ifile"; \
+ else : ; fi; \
+ done; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(infodir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(infodir)" || exit $$?; done
+ @$(POST_INSTALL)
+ @if (install-info --version && \
+ install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \
+ list='$(INFO_DEPS)'; test -n "$(infodir)" || list=; \
+ for file in $$list; do \
+ relfile=`echo "$$file" | sed 's|^.*/||'`; \
+ echo " install-info --info-dir='$(DESTDIR)$(infodir)' '$(DESTDIR)$(infodir)/$$relfile'";\
+ install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$relfile" || :;\
+ done; \
+ else : ; fi
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am: $(PDFS)
+ @$(NORMAL_INSTALL)
+ test -z "$(pdfdir)" || $(MKDIR_P) "$(DESTDIR)$(pdfdir)"
+ @list='$(PDFS)'; test -n "$(pdfdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pdfdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pdfdir)" || exit $$?; done
+install-ps: install-ps-am
+
+install-ps-am: $(PSS)
+ @$(NORMAL_INSTALL)
+ test -z "$(psdir)" || $(MKDIR_P) "$(DESTDIR)$(psdir)"
+ @list='$(PSS)'; test -n "$(psdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(psdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(psdir)" || exit $$?; done
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-aminfo \
+ maintainer-clean-generic maintainer-clean-vti
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-aminfo mostlyclean-generic \
+ mostlyclean-libtool mostlyclean-vti
+
+pdf: pdf-am
+
+pdf-am: $(PDFS)
+
+ps: ps-am
+
+ps-am: $(PSS)
+
+uninstall-am: uninstall-dvi-am uninstall-html-am uninstall-info-am \
+ uninstall-pdf-am uninstall-ps-am
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-aminfo clean-generic \
+ clean-libtool dist-info distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-aminfo \
+ maintainer-clean-generic maintainer-clean-vti mostlyclean \
+ mostlyclean-aminfo mostlyclean-generic mostlyclean-libtool \
+ mostlyclean-vti pdf pdf-am ps ps-am uninstall uninstall-am \
+ uninstall-dvi-am uninstall-html-am uninstall-info-am \
+ uninstall-pdf-am uninstall-ps-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/doc/configuration.texi b/doc/configuration.texi
new file mode 100755
index 0000000..8b7a1ef
--- /dev/null
+++ b/doc/configuration.texi
@@ -0,0 +1,196 @@
+@node Knot DNS Configuration, Running Knot DNS, Knot DNS Installation, Top
+@chapter Knot DNS Configuration
+
+In this chapter we provide suggested configurations and explain the meaning of individual configuration options.
+
+@menu
+* Minimal configuration::
+* Slave configuration::
+* Master configuration::
+* Configuring multiple interfaces::
+* Enabling zone semantic checks::
+* Creating IXFR differences from zone file changes::
+@end menu
+
+@node Minimal configuration
+@section Minimal configuration
+
+The following configuration presents a minimal configuration
+file which can be used as a base for your Knot DNS setup.
+
+@example
+
+# This is a sample of a minimal configuration file for Knot DNS.
+#
+# For exhaustive list of all options see samples/knot.full.conf
+# in the source directory.
+#
+
+system @{
+ storage "/var/lib/knot";
+@}
+
+interfaces @{
+ my_interface @{ address 127.0.0.1@@53; @}
+ second_int @{ address ::1; @}
+@}
+
+log @{
+ syslog @{ any notice, warning, error; @}
+@}
+
+zones @{
+ example.com @{
+ file "/etc/knot/example.com";
+ @}
+@}
+
+@end example
+
+@page
+Now let's go step by step through this minimal configuration file:
+
+@enumerate
+
+@item
+In @code{system} statement we have configured @code{storage}
+directory where Knot DNS will store compiled zone files,
+PID file and for slave zone also their journal files. (See @ref{system} and @ref{storage})
+
+@item
+The @code{interfaces} statement defines interfaces where Knot
+DNS will listen for incoming connections. We have defined two
+interfaces: one IPv4 called @kbd{my_interface} explicitly listening
+on port 53 and second IPv6 called @kbd{second_int} also listening on
+port 53, which is the default port for the DNS. See @ref{interfaces}.
+
+@item
+The @code{log} statement defines the log facilities for Knot DNS.
+In this example we told Knot DNS to send its log messages with the severities
+@code{debug}, @code{warning} and @code{notice} into the syslog.
+If you omit this sections, all severities will printed to
+either @code{stdout} or @code{stderr}, and the severities
+from the @code{warning} and more serious to syslog. You can find all
+possible combinations in the @ref{log}.
+
+@item
+The @code{zones} statement is probably the most important one,
+because it defines the zones that Knot DNS will serve. In its most simple
+form you can define a zone by its name and zone file.
+@end enumerate
+
+@page
+@node Slave configuration
+@section Slave configuration
+
+Knot DNS doesn't strictly differ between master and slave zones.
+The only requirement is to have @code{xfr-in} @code{zones} statement set for given zone,
+thus allowing both incoming XFR from that remote and using it as the
+zone master. Note that you need to explicitly allow incoming NOTIFY, otherwise
+the daemon would reject them.
+Also, you can specify paths, relative to the storage directory.
+See @ref{zones} and @ref{storage}.
+If the zone file doesn't exist and @code{xfr-in} is set, it will be bootstrapped over AXFR.
+
+@example
+remotes @{
+ master @{ address 127.0.0.1@@53; @}
+@}
+zones @{
+ example.com @{
+ file "example.com"; # relative to 'storage'
+ xfr-in master; # define 'master' for this zone
+ notify-in master; # also allow NOTIFY from 'master'
+ @}
+@}
+@end example
+
+You can also use TSIG for access control. For this, you need to configure a TSIG key
+and assign it to a remote.
+Supported algorithms for TSIG key are:@*
+@code{hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, hmac-sha512}
+@*
+Key secret is written in a base64 encoded format. See @ref{keys}.
+
+@example
+keys @{
+ key0 hmac-md5 "Wg=="; # keyname algorithm secret
+@}
+remotes @{
+ master @{ address 127.0.0.1@@53; key key0; @}
+@}
+zones @{
+ example.com @{
+ file "example.com"; # relative to 'storage'
+ xfr-in master; # define 'master' for this zone
+ notify-in master; # also allow NOTIFY from 'master'
+ @}
+@}
+@end example
+
+As of now it is not possible to associate multiple keys with a remote.
+
+@page
+@node Master configuration
+@section Master configuration
+
+You can specify which remotes to allow for outgoing XFR and NOTIFY @code{zones}.
+
+@example
+remotes @{
+ slave @{ address 127.0.0.1@@53; @}
+ any @{ address 0.0.0.0/0; @}
+ subnet1 @{ address 192.168.1.0/8; @}
+ subnet2 @{ address 192.168.2.0/8; @}
+@}
+zones @{
+ example.com @{
+ file "/var/zones/example.com";
+ xfr-out subnet1, subnet2; # allow outgoing transfers
+ notify-out slave;
+ @}
+@}
+@end example
+
+You can also secure outgoing XFRs with TSIG.
+
+@example
+keys @{
+ key0 hmac-md5 "Wg=="; # keyname algorithm secret
+@}
+remotes @{
+ any @{ address 0.0.0.0/0; key key0; @}
+@}
+zones @{
+ example.com @{
+ file "/var/zones/example.com";
+ xfr-out any; # uses 'any' remote secured with TSIG key 'key0'
+ @}
+@}
+@end example
+
+@node Configuring multiple interfaces
+@section Configuring multiple interfaces
+
+Knot DNS support binding to multiple available interfaces in the @code{interfaces} section.
+@*You can also use the special addresses for "any address" like @code{0.0.0.0} or @code{[::]}.
+
+@example
+interfaces @{
+ if1 @{ address 192.168.1.2@@53; @}
+ anyv6 @{ address [::]@@53; @}
+@}
+@end example
+
+@node Enabling zone semantic checks
+@section Enabling zone semantic checks
+You can turn on more detailed semantic
+checks of zone file in this @code{zones} statement (@pxref{zones}). Refer to @ref{zones List of zone semantic checks} to see
+which checks are enabled by default and which are optional.
+
+@node Creating IXFR differences from zone file changes
+@section Creating IXFR differences from zone file changes
+If Knot is being run as a master server, experimental feature @code{ixfr-from-differences}
+can be enabled to create IXFR differences from changes made to the master zone file.
+See @ref{Controlling running daemon} for more information. For more about @code{zones} statement see @ref{zones}.
+
diff --git a/doc/indices.texi b/doc/indices.texi
new file mode 100755
index 0000000..f3a1e88
--- /dev/null
+++ b/doc/indices.texi
@@ -0,0 +1,4 @@
+@node Statement Index, Knot DNS Configuration Reference, Troubleshooting, Top
+@unnumbered Statement Index
+
+@printindex st
diff --git a/doc/installation.texi b/doc/installation.texi
new file mode 100755
index 0000000..0d88f06
--- /dev/null
+++ b/doc/installation.texi
@@ -0,0 +1,297 @@
+@node Knot DNS Installation, Knot DNS Configuration, Knot DNS Resource Requirements, Top
+@chapter Knot DNS Installation
+
+@menu
+* Required build environment::
+* Required libraries::
+* Installation from the sources::
+* Installation from packages::
+@end menu
+
+@node Required build environment
+@section Required build environment
+
+GCC at least 4.1 is strictly required for atomic built-ins, but 4.2 or newer is recommended.
+Another requirement is @code{_GNU_SOURCE} support, otherwise it adapts to the compiler available features.
+Clang should work, but it is not officially supported.
+
+Knot DNS build system relies on these standard tools:
+@itemize
+@item
+make
+@item
+libtool
+@item
+autoconf >= 2.65
+@item
+flex >= 2.5.31
+@item
+bison >= 2.3
+@end itemize
+
+@node Required libraries
+@section Required libraries
+
+Knot DNS requires few libraries to be compiled:
+
+@itemize
+
+@item
+OpenSSL, at least 0.9.8
+@item
+zlib
+@item
+Userspace RCU, at least 0.5.4
+@item
+libcap-ng, at least 0.6.4 (optional library)
+@end itemize
+
+If libcap-ng library is available, Knot DNS will take advantage of
+the POSIX 1003.1e capabilites(7) by sandboxing the exposed threads.
+Most rights are stripped from the exposed threads for security reasons.
+
+You can probably find OpenSSL and zlib libraries already included in
+your system or distribution. If not, zlib resides at
+@url{http://zlib.net}, and OpenSSL can be found at
+@url{http://www.openssl.org}.
+
+@menu
+* Userspace RCU::
+@end menu
+
+@node Userspace RCU
+@subsection Userspace RCU
+
+liburcu is a LGPLv2.1 userspace RCU (read-copy-update)
+library. This data synchronization library provides read-side
+access which scales linearly with the number of cores. It does
+so by allowing multiple copies of a given data structure to
+live at the same time, and by monitoring the data structure
+accesses to detect grace periods after which memory reclamation
+is possible. (@url{http://lttng.org/urcu,Userspace RCU})
+
+Binary packages for Debian can be found under @code{liburcu1} for the
+library and @code{liburcu-dev} for development files.
+
+Minimum supported version of Userspace RCU library is 0.5.4,
+but we recommend using latest available version.
+It is crucial especially on non-Linux systems, as we got some compatibility
+patches accepted to later releases of Userspace RCU.
+OpenBSD, NetBSD and OS X platforms are supported from version 0.7.0.
+
+@node Installation from the sources
+@section Installation from the sources
+
+You can find the source files for the latest release on @url{www.knot-dns.cz}.
+Alternatively, you can fetch the sources from git repository @url{git://git.nic.cz/knot-dns.git}
+
+After unpacking the sources, the compilation and installation is
+a quite straightforward process using autotools.
+
+@menu
+* Configuring and generating Makefiles::
+* Compilation::
+* Installation::
+@end menu
+
+@node Configuring and generating Makefiles
+@subsection Configuring and generating Makefiles
+
+If you want to compile from Git sources, you need to bootstrap the
+@command{./configure} file first.
+
+@example
+$ autoreconf -i -f
+@end example
+
+For all available configure options run:
+
+@example
+$ ./configure --help
+@end example
+
+If you have trouble with unknown syscalls under valgrind, disable recvmmsg by
+adding a parameter @command{--enable-recvmmsg=no} to configure.
+
+Knot DNS has also support for link time optimizations.
+You can enable it by the configure parameter @command{./configure --enable-lto=yes}.
+It is however disabled by default as it is known to be broken in some compiler
+versions and may result in an unexpected behaviour.
+
+If you want to add debug messages, there are two steps to do that.
+First you have to enable modules you are interested in.
+Available are: @code{server, zones, xfr, packet, dname, rr, ns, hash, compiler}.
+You can combine multiple modules as a comma-separated list.
+Then you can narrow the verbosity of the debugging message by specifying the
+verbosity as @code{brief, verbose, details}.
+
+For example:
+@example
+$ ./configure --enable-debug=server,packet --enable-debuglevel=brief
+$ ./configure --enable-debug=server,packet --enable-debuglevel=verbose
+@end example
+
+For more detailed information, see @ref{Debug messages}.
+
+In most simple case you can just run configure without any options.
+
+@example
+$ ./configure
+@end example
+
+@node Compilation
+@subsection Compilation
+
+After running @command{./configure} you can compile
+Knot DNS by running @command{make} command, which will produce binaries
+and other related files.
+
+@example
+$ make
+@end example
+
+Knot DNS build process is safe to parallelize
+using @command{make -j N}, where N is number of
+concurrent processes. Using this option can increase speed of
+the compilation.
+
+For example to use maximum 8 concurrent processes you would use:
+
+@example
+$ make -j 8
+@end example
+
+@node Installation
+@subsection Installation
+
+When you have finished building the Knot DNS, it's time to
+install the binaries and configuration files into the
+operation system hierarchy. You can do so by
+executing @command{make install} command. When installing as a
+non-root user you might have to gain elevated privileges by
+switching to root user, e.g. @command{sudo make install}
+or @command{su -c 'make install'}.
+
+@example
+$ make install
+@end example
+
+@node Installation from packages
+@section Installation from packages
+
+In addition to providing the packages in .DEB and .RPM format,
+the Knot DNS might already be available in your favourite
+distribution, or in a ports tree.
+
+@menu
+* Installing Knot DNS packages on Debian::
+* Installing Knot DNS packages on Ubuntu::
+* Installing Knot DNS RPMs on Fedora::
+* Installing Knot DNS from ports on FreeBSD::
+@end menu
+
+@node Installing Knot DNS packages on Debian
+@subsection Installing Knot DNS packages on Debian
+
+Knot DNS is already available from Debian wheezy upwards. In
+addition to the official packages we also provide custom
+repository, which can be used by adding:
+
+@example
+deb @url{http://deb.knot-dns.cz/debian/} <codename> main
+deb-src @url{http://deb.knot-dns.cz/debian/} <codename> main
+@end example
+
+@noindent
+to your @file{/etc/apt/sources.list} or into separate file in
+@file{/etc/apt/sources.list.d/}.
+
+As an example, for Debian squeeze (current stable) the Knot
+DNS packages can be added by executing following command as
+the root user.
+
+@example
+
+$ cat >/etc/apt/sources.list.d/knot.list <<EOF
+deb http://deb.knot-dns.cz/debian/ <codename> main
+deb-src http://deb.knot-dns.cz/debian/ <codename> main
+EOF
+$ apt-get update
+$ apt-get install knot
+
+@end example
+
+@node Installing Knot DNS packages on Ubuntu
+@subsection Installing Knot DNS packages on Ubuntu
+
+Prepackaged version of the Knot DNS can be found in Ubuntu
+from version 12.10 (Quantal Quetzal). In addition to the
+package included in the main archive, we provide Personal
+Package Archive (PPA) as an option to upgrade to last stable
+version of the Knot DNS or to install it on older versions of
+Ubuntu Linux.
+
+We typically provide packages for all supported versions of Ubuntu
+Linux including 5 year support for
+@url{https://wiki.ubuntu.com/LTS,LTS} versions of Ubuntu Linux. At
+the time of writing this manual this includes Ubuntu 10.04 LTS, 11.04,
+11.10 and 12.04 LTS.
+
+@menu
+* Adding official PPA repository for Knot DNS::
+@end menu
+
+@node Adding official PPA repository for Knot DNS
+@subsubsection Adding official PPA repository for Knot DNS
+
+To start installing and using software from a Personal
+Package Archive, you first need to tell Ubuntu where to find
+the PPA.
+
+@example
+
+$ sudo add-apt-repository ppa:cz.nic-labs/knot-dns
+$ sudo apt-get update
+$ sudo apt-get install knot
+
+@end example
+
+@noindent
+Running this sequence of commands will ensure that you will
+install Knot DNS on your system and keep it up-to-date
+in the future, when new versions are released.
+
+@page
+@node Installing Knot DNS RPMs on Fedora
+@subsection Installing Knot DNS RPMs on Fedora
+
+There are RPM packages for @code{Knot DNS} available for i386 and amd64 targets.
+If you want use the Fedora repository, add a file with the
+following lines into @file{/etc/yum.repos.d/}
+
+@example
+[knot]
+name=Network.CZ Repository
+baseurl=ftp://repo.network.cz/pub/redhat/
+enabled=1
+gpgcheck=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-network.cz
+@end example
+
+When you have added a new repository, you can install Knot DNS as a
+regular package.
+
+@example
+$ yum install knot
+@end example
+
+@node Installing Knot DNS from ports on FreeBSD
+@subsection Installing Knot DNS from ports on FreeBSD
+
+Knot DNS is in ports tree under @code{dns/knot}.
+
+@example
+$ cd /usr/ports/dns/knot
+$ sudo make install
+@end example
+
diff --git a/doc/introduction.texi b/doc/introduction.texi
new file mode 100755
index 0000000..4217001
--- /dev/null
+++ b/doc/introduction.texi
@@ -0,0 +1,70 @@
+@node Introduction, Knot DNS Resource Requirements, Top, Top
+@chapter Introduction
+
+The reader of this document is assumed to know the principles of
+Domain Name System.
+
+@menu
+* What is Knot DNS::
+* Knot DNS features::
+* Scope of this document::
+@end menu
+
+@node What is Knot DNS
+@section What is Knot DNS
+
+Knot DNS is a high-performance open source DNS server. It
+implements only authoritative domain name service. Knot DNS
+is best suited for use on TLD domains but can reliably serve
+any other zones as well.
+
+Knot DNS benefits from its multi-threaded and mostly lock-free
+implementation which allows it to scale well on SMP systems and
+operate non-stop even when adding or removing zones.
+
+@node Knot DNS features
+@section Knot DNS features
+
+Knot DNS supports the following DNS features:
+
+@itemize
+@item TCP/UDP protocols
+@item AXFR - master, slave
+@item IXFR - master (primary master experimental), slave
+@item TSIG
+@item ENDS0
+@item DNSSEC, including NSEC3
+@item NSID
+@item Unknown RR types
+@end itemize
+
+@*
+Server features:
+
+@itemize
+@item Adding/removing zones on-the-fly
+@item Reconfiguring server instance on-the-fly
+@item IPv4 / IPv6 support
+@item Semantic checks of zones
+@end itemize
+
+@*
+For more info and downloads see
+@url{http://www.knot-dns.cz, www.knot-dns.cz}.
+
+Git repository:
+@url{git://git.nic.cz/knot-dns.git, git://git.nic.cz/knot-dns.git}
+
+Knot DNS issue tracker:
+@url{https://git.nic.cz/redmine/projects/knot-dns,
+git.nic.cz/redmine/projects/knot-dns}
+
+Knot DNS users mailing list:
+@url{mailto:knot-dns-users@@lists.nic.cz, knot-dns-users@@lists.nic.cz}
+
+@node Scope of this document
+@section Scope of this document
+
+This document covers the basic information on installing,
+configuring and troubleshooting the Knot DNS server.
+
diff --git a/doc/knot.texi b/doc/knot.texi
new file mode 100755
index 0000000..b0929ae
--- /dev/null
+++ b/doc/knot.texi
@@ -0,0 +1,229 @@
+\input texinfo @c -*-texinfo-*-
+@setfilename knot.info
+@include version.texi
+@documentencoding utf-8
+@settitle Knot DNS @value{VERSION}
+
+@paragraphindent 0
+
+@defindex st
+@syncodeindex vr st
+
+@copying
+This manual is for Knot DNS (version @value{VERSION}, @value{UPDATED}),
+which is a high-performance authoritative-only DNS server.
+
+Copyright @copyright{} 2012 CZ.NIC, z.s.p.o.
+
+@quotation
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+@end quotation
+@end copying
+
+@dircategory Internet-application/server
+@direntry
+* Knot DNS: (Knot DNS) An authoritative-only DNS server
+@end direntry
+
+@titlepage
+@title Knot DNS Reference Manual
+@subtitle for version @value{VERSION}, @value{UPDATED}
+@author Jan Kadlec (@email{jan.kadlec@@nic.cz})
+@author Lubos Slovak (@email{lubos.slovak@@nic.cz})
+@author Ondrej Sury (@email{ondrej@@sury.org})
+@author Marek Vavrusa (@email{marek.vavrusa@@nic.cz})
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top, Introduction, (dir), (dir)
+@top Knot DNS
+
+This manual is for Knot DNS (version @value{VERSION}, @value{UPDATED}).
+@end ifnottex
+
+@menu
+* Introduction::
+* Knot DNS Resource Requirements::
+* Knot DNS Installation::
+* Knot DNS Configuration::
+* Running Knot DNS::
+* Troubleshooting::
+* Statement Index::
+* Knot DNS Configuration Reference::
+
+@detailmenu
+ --- The Detailed Node Listing ---
+
+Introduction
+
+* What is Knot DNS::
+* Knot DNS features::
+* Scope of this document::
+
+Knot DNS Resource Requirements
+
+* Hardware requirements::
+* CPU requirements::
+* Memory requirements::
+* Supported operating system::
+
+Knot DNS Installation
+
+* Required build environment::
+* Required libraries::
+* Installation from the sources::
+* Installation from packages::
+
+Required libraries
+
+* Userspace RCU::
+
+Installation from the sources
+
+* Configuring and generating Makefiles::
+* Compilation::
+* Installation::
+
+Installation from packages
+
+* Installing Knot DNS packages on Debian::
+* Installing Knot DNS packages on Ubuntu::
+* Installing Knot DNS RPMs on Fedora::
+* Installing Knot DNS from ports on FreeBSD::
+
+Installing Knot DNS packages on Ubuntu
+
+* Adding official PPA repository for Knot DNS::
+
+Knot DNS Configuration
+
+* Minimal configuration::
+* Slave configuration::
+* Master configuration::
+* Configuring multiple interfaces::
+
+Sample Configurations
+
+* Minimal configuration::
+* Slave configuration::
+* Master configuration::
+* Configuring multiple interfaces::
+* Enabling zone semantic checks::
+* Creating IXFR differences from zone file changes::
+
+Running Knot DNS
+
+* Running a slave server::
+* Running a master server::
+* Controlling running daemon::
+
+Troubleshooting
+
+* Submitting a bugreport::
+* Generating backtrace::
+* Debug messages::
+
+Debug messages
+
+* Enabling debug messages in server::
+
+Enabling debug messages in server
+
+* Example::
+
+Knot DNS Configuration Reference
+
+* system::
+* keys::
+* interfaces::
+* remotes::
+* zones::
+* log::
+
+@code{system} Statement
+
+* system Syntax::
+* system Statement Definition and Usage::
+* system Example::
+
+Statement Definition and Usage
+
+* identity::
+* version::
+* nsid::
+* storage::
+* pidfile::
+* workers::
+* user::
+
+@code{keys} Statement
+
+* keys Syntax::
+* keys Statement Definition and Usage::
+* Example::
+
+Statement Definition and Usage
+
+* key_id::
+
+interfaces
+
+* interfaces Syntax::
+* interfaces Statement Definition and Usage::
+* interfaces Examples::
+
+Statement Definition and Usage
+
+* interface_id::
+
+@code{remotes} Statement
+
+* remotes Syntax::
+* remotes Statement Definition and Grammar::
+
+@code{zones} Statement
+
+* zones Syntax::
+* zones Statement Definition and Grammar::
+* zones List of zone semantic checks::
+
+@code{log} Statement
+
+* log Syntax::
+* log Statement Definition and Grammar::
+
+@end detailmenu
+@end menu
+
+@c main chapters
+
+@include introduction.texi
+@include requirements.texi
+@include installation.texi
+@include configuration.texi
+@include running.texi
+@include troubleshooting.texi
+
+@c indices
+@include indices.texi
+
+@c appendixes
+@include reference.texi
+
+@bye
diff --git a/doc/mdate-sh b/doc/mdate-sh
new file mode 100755
index 0000000..e631b22
--- /dev/null
+++ b/doc/mdate-sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+# Get modification time of a file or directory and pretty-print it.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1995, 1996, 1997, 2003, 2004, 2005, 2007, 2009 Free
+# Software Foundation, Inc.
+# written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 1995
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case $1 in
+ '')
+ echo "$0: No file. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: mdate-sh [--help] [--version] FILE
+
+Pretty-print the modification time of FILE.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "mdate-sh $scriptversion"
+ exit $?
+ ;;
+esac
+
+# Prevent date giving response in another language.
+LANG=C
+export LANG
+LC_ALL=C
+export LC_ALL
+LC_TIME=C
+export LC_TIME
+
+# GNU ls changes its time format in response to the TIME_STYLE
+# variable. Since we cannot assume `unset' works, revert this
+# variable to its documented default.
+if test "${TIME_STYLE+set}" = set; then
+ TIME_STYLE=posix-long-iso
+ export TIME_STYLE
+fi
+
+save_arg1=$1
+
+# Find out how to get the extended ls output of a file or directory.
+if ls -L /dev/null 1>/dev/null 2>&1; then
+ ls_command='ls -L -l -d'
+else
+ ls_command='ls -l -d'
+fi
+# Avoid user/group names that might have spaces, when possible.
+if ls -n /dev/null 1>/dev/null 2>&1; then
+ ls_command="$ls_command -n"
+fi
+
+# A `ls -l' line looks as follows on OS/2.
+# drwxrwx--- 0 Aug 11 2001 foo
+# This differs from Unix, which adds ownership information.
+# drwxrwx--- 2 root root 4096 Aug 11 2001 foo
+#
+# To find the date, we split the line on spaces and iterate on words
+# until we find a month. This cannot work with files whose owner is a
+# user named `Jan', or `Feb', etc. However, it's unlikely that `/'
+# will be owned by a user whose name is a month. So we first look at
+# the extended ls output of the root directory to decide how many
+# words should be skipped to get the date.
+
+# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below.
+set x`$ls_command /`
+
+# Find which argument is the month.
+month=
+command=
+until test $month
+do
+ shift
+ # Add another shift to the command.
+ command="$command shift;"
+ case $1 in
+ Jan) month=January; nummonth=1;;
+ Feb) month=February; nummonth=2;;
+ Mar) month=March; nummonth=3;;
+ Apr) month=April; nummonth=4;;
+ May) month=May; nummonth=5;;
+ Jun) month=June; nummonth=6;;
+ Jul) month=July; nummonth=7;;
+ Aug) month=August; nummonth=8;;
+ Sep) month=September; nummonth=9;;
+ Oct) month=October; nummonth=10;;
+ Nov) month=November; nummonth=11;;
+ Dec) month=December; nummonth=12;;
+ esac
+done
+
+# Get the extended ls output of the file or directory.
+set dummy x`eval "$ls_command \"\$save_arg1\""`
+
+# Remove all preceding arguments
+eval $command
+
+# Because of the dummy argument above, month is in $2.
+#
+# On a POSIX system, we should have
+#
+# $# = 5
+# $1 = file size
+# $2 = month
+# $3 = day
+# $4 = year or time
+# $5 = filename
+#
+# On Darwin 7.7.0 and 7.6.0, we have
+#
+# $# = 4
+# $1 = day
+# $2 = month
+# $3 = year or time
+# $4 = filename
+
+# Get the month.
+case $2 in
+ Jan) month=January; nummonth=1;;
+ Feb) month=February; nummonth=2;;
+ Mar) month=March; nummonth=3;;
+ Apr) month=April; nummonth=4;;
+ May) month=May; nummonth=5;;
+ Jun) month=June; nummonth=6;;
+ Jul) month=July; nummonth=7;;
+ Aug) month=August; nummonth=8;;
+ Sep) month=September; nummonth=9;;
+ Oct) month=October; nummonth=10;;
+ Nov) month=November; nummonth=11;;
+ Dec) month=December; nummonth=12;;
+esac
+
+case $3 in
+ ???*) day=$1;;
+ *) day=$3; shift;;
+esac
+
+# Here we have to deal with the problem that the ls output gives either
+# the time of day or the year.
+case $3 in
+ *:*) set `date`; eval year=\$$#
+ case $2 in
+ Jan) nummonthtod=1;;
+ Feb) nummonthtod=2;;
+ Mar) nummonthtod=3;;
+ Apr) nummonthtod=4;;
+ May) nummonthtod=5;;
+ Jun) nummonthtod=6;;
+ Jul) nummonthtod=7;;
+ Aug) nummonthtod=8;;
+ Sep) nummonthtod=9;;
+ Oct) nummonthtod=10;;
+ Nov) nummonthtod=11;;
+ Dec) nummonthtod=12;;
+ esac
+ # For the first six month of the year the time notation can also
+ # be used for files modified in the last year.
+ if (expr $nummonth \> $nummonthtod) > /dev/null;
+ then
+ year=`expr $year - 1`
+ fi;;
+ *) year=$3;;
+esac
+
+# The result.
+echo $day $month $year
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/doc/migration.texi b/doc/migration.texi
new file mode 100755
index 0000000..9d4e0fa
--- /dev/null
+++ b/doc/migration.texi
@@ -0,0 +1,29 @@
+@node Migration for other DNS servers, , Knot DNS Configuration Reference, Top
+@appendix Migration for other DNS servers
+
+@menu
+* Knot DNS for BIND users::
+* Knot DNS for NSD users::
+* Knot DNS for PowerDNS users::
+* Knot DNS for djbdns users::
+@end menu
+
+@node Knot DNS for BIND users
+@appendixsec Knot DNS for BIND users
+
+[TODO]
+
+@node Knot DNS for NSD users
+@appendixsec Knot DNS for NSD users
+
+[TODO]
+
+@node Knot DNS for PowerDNS users
+@appendixsec Knot DNS for PowerDNS users
+
+[TODO]
+
+@node Knot DNS for djbdns users
+@appendixsec Knot DNS for djbdns users
+
+[TODO]
diff --git a/doc/reference.texi b/doc/reference.texi
new file mode 100755
index 0000000..9dffd00
--- /dev/null
+++ b/doc/reference.texi
@@ -0,0 +1,774 @@
+@node Knot DNS Configuration Reference, , Statement Index, Top
+@appendix Knot DNS Configuration Reference
+
+This reference describes every configuration option in Knot DNS server.
+
+@menu
+* system::
+* keys::
+* interfaces::
+* remotes::
+* zones::
+* log::
+@end menu
+
+@node system
+@section @code{system} Statement
+@stindex system
+
+The @code{system} statement contains general options related to the
+operating system and other general options which do not fit anywhere
+else.
+
+@menu
+* system Syntax::
+* system Statement Definition and Usage::
+* system Example::
+@end menu
+
+@node system Syntax
+@subsection @code{system} Syntax
+
+@example
+@code{system} @code{@{}
+ [ @code{identity} @code{"}@kbd{string}@code{";} ]
+ [ @code{version} @code{"}@kbd{string}@code{";} ]
+ [ @code{nsid} ( @code{"}@kbd{string}@code{"} | @kbd{hex_string} )@code{;} ]
+ [ @code{storage} @code{"}@kbd{string}@code{";} ]
+ [ @code{pidfile} @code{"}@kbd{string}@code{";} ]
+ [ @code{workers} @kbd{integer}@code{;} ]
+ [ @code{user} @kbd{string}[@code{.}@kbd{string}]@code{;} ]
+@code{@}}
+@end example
+
+@node system Statement Definition and Usage
+@subsection Statement Definition and Usage
+
+@menu
+* identity::
+* version::
+* nsid::
+* storage::
+* pidfile::
+* workers::
+* user::
+@end menu
+
+@node identity
+@subsubsection identity
+@vindex identity
+
+Identity of the server (see @url{http://tools.ietf.org/html/rfc4892,RFC 4892}). Not used yet.
+
+@example
+system @{
+ identity "Knot DNS";
+@}
+@end example
+
+@node version
+@subsubsection version
+@vindex version
+
+Version of the server (see @url{http://tools.ietf.org/html/rfc4892,RFC 4892}). Not used yet.
+
+@example
+system @{
+ version "1.1.0";
+@}
+@end example
+
+@node nsid
+@subsubsection nsid
+@vindex nsid
+
+DNS Name Server Identifier (see @url{http://tools.ietf.org/html/rfc5001,RFC 5001}).
+
+Use a string format "text" or a hexstring (e.g. 0x01ab00)
+
+@example
+system @{
+ nsid 0x00cafe;
+@}
+@end example
+
+@node storage
+@subsubsection storage
+@vindex storage
+
+The working directory of Knot DNS, it is used to store compiled zone files and it is also a default location of the PID file.
+
+@example
+system @{
+ storage "/var/lib/knot";
+@}
+@end example
+
+@node pidfile
+@subsubsection pidfile
+@vindex pidfile
+
+Specifies a custom PID file location.
+
+Default value: @file{knot.pid} in @code{storage} directory.
+
+@example
+system @{
+ pidfile "/var/run/knot.pid";
+@}
+@end example
+
+@node workers
+@subsubsection workers
+@vindex workers
+
+Number of workers (threads) per server interface. This option is used to
+force number of threads used per interface.
+
+Default value: unset (auto-estimates optimal value from the number of online CPUs)
+
+@example
+system @{
+ workers 16;
+@}
+@end example
+
+@node user
+@subsubsection user
+@vindex user
+
+System @kbd{user} or @kbd{user}.@kbd{group} under which the Knot DNS
+is run after starting and binding to interfaces.
+Linux capabilities (@pxref{Required libraries}) are employed if supported
+and this configuration option is set.
+
+Default value: @kbd{root.root}
+
+@example
+system @{
+ user knot.knot;
+@}
+@end example
+
+@node system Example
+@subsection system Example
+
+@example
+system @{
+ identity "Knot DNS @value{VERSION}";
+ version "@value{VERSION}";
+ nsid "amaterasu";
+ storage "/var/lib/knot";
+ pidfile "/var/run/knot.pid";
+ workers 16;
+ user knot.knot;
+@}
+@end example
+
+@node keys
+@section @code{keys} Statement
+@stindex keys
+
+The @code{keys} statement sets up the TSIG keys used to authenticate
+zone transfers.
+
+@menu
+* keys Syntax::
+* keys Statement Definition and Usage::
+* Example::
+@end menu
+
+@node keys Syntax
+@subsection keys Syntax
+
+@example
+keys @{
+ key_id algorithm "string"; ]
+ [ key_id algorithm "string"; ... ]
+@}
+@end example
+
+@node keys Statement Definition and Usage
+@subsection Statement Definition and Usage
+
+@menu
+* key_id::
+@end menu
+
+@node key_id
+@subsubsection @code{key_id} Statement
+@vindex key_id
+
+The @kbd{key_id} statement defines a secret shared key for use with
+TSIG. It consists of its @kbd{name}, @kbd{algorithm} and @kbd{key} contents.
+
+Supported algoritms:
+
+@itemize
+@item
+hmac-md5
+@item
+hmac-sha1
+@item
+hmac-sha224
+@item
+hmac-sha256
+@item
+hmac-sha384
+@item
+hmac-sha512
+@end itemize
+
+You need to use bind or ldns utils to generate TSIG keys. Unfortunately, Knot DNS does not have any own generation utilities yet.
+
+@example
+
+$ dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST foobar.example.com
+Kfoobar.example.com.+163+21239
+$ cat Kfoobar.example.com.+163+21239.key
+foobar.example.com. ( IN KEY 512 3 163
+ rqv2WRyDgIUaHcJi03Zssor9jtG1kOpb3dPywxZfTeo= )
+
+@end example
+
+Key generated in previous paragraph would be written as:
+
+@example
+
+keys @{
+ foobar.example.com. hmac-sha256
+ "rqv2WRyDgIUaHcJi03Zssor9jtG1kOpb3dPywxZfTeo=";
+@}
+
+@end example
+
+@node Example
+@subsection keys Example
+
+
+@example
+
+keys @{
+ key0.server0 hmac-md5 "Wg==";
+ foobar.example.com. hmac-sha256 "RQ==";
+@}
+
+@end example
+
+@node interfaces
+@section interfaces
+@stindex interfaces
+
+The @code{interfaces} statement contains IP interfaces where Knot DNS listens for incoming queries.
+
+@menu
+* interfaces Syntax::
+* interfaces Statement Definition and Usage::
+* interfaces Examples::
+@end menu
+
+@node interfaces Syntax
+@subsection Syntax
+
+@example
+@code{interfaces} @code{@{}
+ @kbd{interface_id}
+ ( @kbd{ip_address}[@@@kbd{port_number}] |
+ @code{@{} @code{address} @kbd{ip_address}@code{;} [ @code{port} @kbd{port_number}@code{;} ] @code{@}} )
+ [ @kbd{interface_id ...}@code{;} @kbd{...}@code{;} ]
+@code{@}}
+@end example
+
+@node interfaces Statement Definition and Usage
+@subsection Statement Definition and Usage
+
+@menu
+* interface_id::
+@end menu
+
+@node interface_id
+@subsubsection @kbd{interface_id}
+@vindex interface_id
+
+The @kbd{interface_id} is a textual identifier of an IP interface,
+which consists of an IP address and a port.
+
+The definition of an interface can be written in long or a short form and
+it always contains IP (IPv4 or IPv6) address.
+
+@node interfaces Examples
+@subsection interfaces Examples
+
+Long form:
+
+@example
+
+interfaces @{
+ my_ip @{
+ address 192.0.2.1;
+ port 53;
+ @}
+@}
+
+@end example
+
+Short form:
+
+@example
+
+interfaces @{
+ my_second_ip @{ address 198.51.100.1@@53; @}
+@}
+
+@end example
+
+Short form without port (defaults to 53):
+
+@example
+
+interfaces @{
+ my_third_ip @{ address 203.0.113.1; @}
+@}
+
+@end example
+
+@node remotes
+@section @code{remotes} Statement
+@stindex remotes
+
+The @code{remotes} statement sets up all remote servers for zone
+transfers. Knot DNS does not distinguish between client or server in
+this section. Role of the server is determined at the time of its
+usage in the @code{zones} section. One server may act as a
+client for one zone (e.g. downloading the updates) and as a master
+server for a different zone.
+
+@menu
+* remotes Syntax::
+* remotes Statement Definition and Grammar::
+* remotes Examples::
+@end menu
+
+@node remotes Syntax
+@subsection Syntax
+
+@example
+@code{remotes} @code{@{}
+ @kbd{remote_id}
+ ( @kbd{ip_address}[@code{@@}@kbd{port_number}] |
+ @code{@{} @code{address} @kbd{ip_address};
+ [ @code{port} @kbd{port_number}; ]
+ [ @code{key} @kbd{key_id}; ]
+ [ @code{via} [ @kbd{interface_id} | @kbd{ip_address} ]; ]
+ @code{@}}
+ )
+ [ @kbd{remote_id} @dots{}; @dots{}; ]
+@code{@}}
+@end example
+
+@node remotes Statement Definition and Grammar
+@subsection Statement Definition and Grammar
+
+@menu
+* remote_id::
+* address::
+* port::
+* key::
+* via::
+@end menu
+
+@node remote_id
+@subsubsection @kbd{remote_id}
+@vindex remote_id
+
+@kbd{remote_id} contains a symbolic name for a remote server.
+
+@node address
+@subsubsection address
+@vindex address
+
+@kbd{address} sets an IPv4 or an IPv6 address for this particular @code{remote}.
+
+@node port
+@subsubsection port
+@vindex port
+
+@code{port} section contains a port number for the current @code{remote}. This section is optional with default port set to 53.
+
+@node key
+@subsubsection key
+@vindex key
+
+@code{key} section contains a key associated with this @code{remote}. This section is optional.
+
+
+@node via
+@subsubsection via
+@vindex via
+
+@code{via} section specifies which interface will be used to communicate with this @code{remote}. This section is optional.
+
+@node remotes Examples
+@subsection remotes Examples
+
+@example
+
+remotes @{
+
+ # Format 1:
+ server0 @{
+ address 127.0.0.1;
+ port 53531;
+ key key0.server0;
+ via ipv4; # reference to 'remotes'
+ # via 82.35.64.59; # direct IPv4
+ # via [::cafe]; # direct IPv6
+ @}
+
+ # Format 2:
+ server1 @{
+ address 127.0.0.1@@53001;
+ @}
+@}
+
+@end example
+
+@node zones
+@section @code{zones} Statement
+
+The @code{zones} statement contains definition of zones served by Knot DNS.
+
+@menu
+* zones Syntax::
+* zones Statement Definition and Grammar::
+* zones Example::
+* zones List of zone semantic checks::
+@end menu
+
+@node zones Syntax
+@subsection Syntax
+
+@example
+@code{zones} @code{@{}
+ [ @kbd{zone_options} ]
+ @kbd{zone_id} @code{@{}
+ @code{file} @code{"}@kbd{string}@code{";}
+ [ @code{xfr-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
+ [ @code{xfr-out} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
+ [ @code{notify-in} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
+ [ @code{notify-out} @kbd{remote_id} [, @kbd{remote_id}, @dots{} ]@code{;} ]
+ [ @kbd{zone_options} ]
+ @code{@}}
+@code{@}}
+
+@kbd{zone_options} :=
+ [ @code{semantic-checks} @kbd{boolean}@code{;} ]
+ [ @code{ixfr-from-differences} @kbd{boolean}@code{;} ]
+ [ @code{disable-any} @kbd{boolean}@code{;} ]
+ [ @code{notify-timeout} @kbd{integer}@code{;} ]
+ [ @code{notify-retries} @kbd{integer}@code{;} ]
+ [ @code{zonefile-sync} ( @kbd{integer} | @kbd{integer}(@code{s} | @code{m} | @code{h} | @code{d})@code{;} ) ]
+ [ @code{ixfr-fslimit} ( @kbd{integer} | @kbd{integer}(@code{k} | @code{M} | @code{G}) )@code{;} ]
+ [ @code{ixfr-from-differences} @kbd{boolean}@code{;} ]
+@end example
+
+@node zones Statement Definition and Grammar
+@subsection Statement Definition and Grammar
+
+@menu
+* zone_id::
+* file::
+* xfr-in::
+* xfr-out::
+* notify-in::
+* notify-out::
+* semantic-checks::
+* ixfr-from-differences::
+* disable-any::
+* notify-timeout::
+* notify-retries::
+* zonefile-sync::
+* ixfr-fslimit::
+@end menu
+
+@node zone_id
+@subsubsection @kbd{zone_id}
+@vindex zone_id
+
+@code{zone_id} is a zone origin, and as such is a domain name that may or may not end with a ".".
+If no $ORIGIN directive is found inside actual zone file, this domain name will be used in place of "@@".
+SOA record in the zone must have this name as its owner.
+
+@node file
+@subsubsection file
+@vindex file
+
+The @code{file} statement defines a path to the zone file.
+You can either use an absolute path or a relative path.
+In that case, the zone file path will be relative to the @code{storage} directory (@pxref{storage}).
+
+@node xfr-in
+@subsubsection xfr-in
+@vindex xfr-in
+
+In @code{xfr-in} statement user specifies which remotes will be permitted to perform a zone transfer to update the zone.
+Remotes are defined in @code{remotes} section of configuration file (@pxref{remotes}).
+
+@node xfr-out
+@subsubsection xfr-out
+@vindex xfr-out
+
+In @code{xfr-out} statement user specifies which remotes will be permitted to obtain zone's contents via zone transfer.
+Remotes are defined in @code{remotes} section of configuration file (@pxref{remotes}).
+
+@node notify-in
+@subsubsection notify-in
+@vindex notify-in
+
+@code{notify-in} defines which remotes will be permitted to send NOTIFY for this particular zone.
+
+@node notify-out
+@subsubsection notify-out
+@vindex notify-out
+
+@code{notify-out} defines to which remotes will your server send NOTIFYs about this particular zone.
+
+@node semantic-checks
+@subsubsection semantic-checks
+@vindex semantic-checks
+
+@code{semantic-checks} statement turns on optional semantic checks for this particular zone.
+See @ref{zones List of zone semantic checks} for more information. Possible values are @code{on} and @code{off}.
+Most checks are disabled by default.
+
+@node ixfr-from-differences
+@subsubsection ixfr-from-differences
+@vindex ixfr-from-differences
+
+EXPERIMENTAL: option @code{ixfr-from-differences} is only relevant if you are running Knot DNS as a master for this zone.
+By turning the feature on you tell Knot to create differences from changes you made to a zone file upon server reload.
+See @ref{Controlling running daemon} for more information. Possible values are @code{on} and @code{off}. Disabled by default.
+
+@node disable-any
+@subsubsection disable-any
+@vindex disable-any
+
+If you enable @code{disable-any}, all authoritative ANY queries sent over UDP will be answered with an empty response and with the TC bit set.
+Use to minimize the risk of DNS replay attack. Disabled by default.
+
+@node notify-timeout
+@subsubsection notify-timeout
+@vindex notify-timeout
+
+@code{notify-timeout} in seconds specifies how long will server wait for NOTIFY response. Possible values are 1 to INT_MAX.
+By default, this value is set to 60 seconds.
+
+@node notify-retries
+@subsubsection notify-retries
+@vindex notify-retries
+
+@code{notify-retries} tells the server how many times it can retry to send a NOTIFY. Possible values
+are 1 to INT_MAX and default value is 5.
+
+@node zonefile-sync
+@subsubsection zonefile-sync
+@vindex zonefile-sync
+
+@code{zonefile-sync} is only relevant in a slave server scenario and only after receiving IXFR. It is a time in seconds after which current zone in memory will be synced to its file on a disk (as set in @ref{file}). Knot DNS will serve the latest zone even after restart, but zone file on a disk will only be synced after @code{zonefile-sync} time has expired. Possible values are 1 to INT_MAX, optionally suffixed by unit size (s/m/h/d) - @emph{1s} is one second, @emph{1m} one minute, @emph{1h} one hour and @emph{1d} one day with default value set to @emph{1h}.
+
+@node ixfr-fslimit
+@subsubsection ixfr-fslimit
+@vindex ixfr-fslimit
+
+@code{ixfr-fslimit} sets a maximum file size for zone's journal in bytes. Possible values are 1 to INT_MAX, with optional suffixes k, m and G. I.e. @emph{1k}, @emph{1m} and @emph{1G} with default value not being set, meaning that journal file can grow without limitations.
+
+@node zones Example
+@subsection zones Example
+
+@example
+@group
+zones @{
+
+ # Shared options for all listed zones
+ ixfr-from-differences off;
+ semantic-checks off;
+ disable-any off;
+ notify-timeout 60;
+ notify-retries 5;
+ zonefile-sync 1h;
+ ixfr-fslimit 1G;
+ example.com @{
+ file "samples/example.com.zone";
+ ixfr-from-differences off; #experimental
+ disable-any off;
+ semantic-checks on;
+ notify-timeout 60;
+ notify-retries 5;
+ zonefile-sync 1h;
+ xfr-in server0;
+ xfr-out server0, server1;
+ notify-in server0;
+ notify-out server0, server1;
+ @}
+@}
+
+@end group
+@end example
+
+@node zones List of zone semantic checks
+@subsection List of zone semantic checks
+
+The @code{semantic-checks} statement turns on extra zone file semantic
+checks. Several checks are enabled by default and cannot be turned
+off. If an error is found using these mandatory checks, the zone file
+will not be loaded. Upon loading a zone file, occurred
+errors and counts of their occurrence will be logged to @emph{stderr}.
+These checks are the following:
+
+@example
+- An extra record together with CNAME record (except for RRSIG and DS)
+- CNAME link chain length greater than 10 (including infinite cycles)
+- DNAME and CNAME records under the same owner (RFC 2672)
+- CNAME and DNAME wildcards pointing to themselves
+- SOA record missing in the zone (RFC 1034)
+- DNAME records having records under it (DNAME children) (RFC 2672)
+@end example
+
+Following checks have to be turned on using @code{semantic-checks} and
+a zone containing following errors will be
+loaded even upon discovering an error:
+
+@example
+- Missing NS record at the zone apex
+- Missing glue A or AAAA records
+- Broken or non-cyclic NSEC(3) chain
+- Wrong NSEC(3) type bitmap
+- Multiple NSEC records at the same node
+- Missing NSEC records at authoritative nodes
+- Extra record types under same name as NSEC3 record
+(this is RFC-valid, but Knot will not serve such a zone correctly)
+- NSEC3-unsecured delegation that is not part of Opt-out span
+- Wrong original TTL value in NSEC3 records
+- Wrong RDATA TTL value in RRSIG record
+- Signer name in RRSIG RR not the same as in DNSKEY
+- Signed RRSIG
+- Not all RRs in node are signed
+- Wrong key flags or wrong key in RRSIG record (not the same as ZSK)
+@end example
+
+@node log
+@section @code{log} Statement
+@stindex log
+
+@menu
+* log Syntax::
+* log Statement Definition and Grammar::
+* log Example::
+@end menu
+
+@node log Syntax
+@subsection Syntax
+
+@example
+@code{log} @code{@{}
+ [ @kbd{log_name} @code{@{}
+ [ @kbd{category} @kbd{severity} [ @kbd{severity} @dots{} ]@code{;} ]
+ @code{@}} ]
+ [ @code{log_file} @kbd{filename} @{
+ [ @kbd{category} @kbd{severity} [ @kbd{severity} @dots{} ]@code{;} ]
+ @} ]
+@code{@}}
+
+@end example
+
+@node log Statement Definition and Grammar
+@subsection Statement Definition and Grammar
+
+@menu
+* log_name::
+* category::
+* severity::
+* log_file::
+@end menu
+
+The @code{log} statement configures logging output of Knot DNS. You
+can configure Knot DNS to log into file or system log. There are several
+logging categories to choose from. Each log
+message has its severity and user can configure severities for each
+log destination.
+
+In case of missing log section, severities from @kbd{warning} and more serious
+will be logged to both @kbd{stderr} and @kbd{syslog}. The @kbd{info} and @kbd{notice}
+severities will be logged to the @kbd{stdout}.
+
+@node log_name
+@subsubsection @kbd{log_name}
+@vindex @kbd{log_name}
+
+@kbd{log_name} should be replaced with one of 3 symbolic log names :
+@itemize
+@item @emph{stdout} - logging to standard output
+@item @emph{stderr} - logging to standard error output
+@item @emph{syslog} - logging to syslog
+@end itemize
+
+@node category
+@subsubsection @kbd{category}
+@vindex category
+
+Knot DNS allows user to choose from these logging categories:
+
+@itemize
+@item @emph{server} - Messages related to general operation of the server.
+@item @emph{zone} - Messages related to zones, zone parsing and loading.
+@item @emph{answering} - Messages regarding query processing and response creation.
+@item @emph{any} - All categories.
+@end itemize
+
+@node severity
+@subsubsection @kbd{severity}
+@vindex severity
+
+Knot DNS has the following logging severities:
+@itemize
+@item @emph{debug} - Debug messages, must be turned on at compile time (@pxref{Enabling debug messages in server}).
+@item @emph{info} - Informational message.
+@item @emph{notice} - Server notices and hints.
+@item @emph{warning} - Warnings that might require user action.
+@item @emph{error} - Recoverable error. Action should be taken.
+@item @emph{all} - All severities.
+@end itemize
+
+More severities may be listed for each category, but all severities have to be listed explicitly, i.e. using @emph{warning} severity does not mean that @emph{error} severity messages will be logged as well.
+
+@node log_file
+@subsubsection @kbd{log_file}
+@vindex @kbd{log_file}
+
+@kbd{log_file} is either absolute or relative path to file user wants to log to.
+See following example for clarification.
+
+@node log Example
+@subsection log Example
+
+@example
+
+log @{
+
+ syslog @{
+ any error;
+ zone warning, notice;
+ server info;
+ @}
+
+ stderr @{
+ any error, warning;
+ @}
+
+ file "/tmp/knot-sample/knotd.debug" @{
+ server debug;
+ @}
+@}
+
+@end example
+
diff --git a/doc/requirements.texi b/doc/requirements.texi
new file mode 100755
index 0000000..136f3c6
--- /dev/null
+++ b/doc/requirements.texi
@@ -0,0 +1,52 @@
+@node Knot DNS Resource Requirements, Knot DNS Installation, Introduction, Top
+@chapter Knot DNS Resource Requirements
+
+@menu
+* Hardware requirements::
+* CPU requirements::
+* Memory requirements::
+* Supported operating system::
+@end menu
+
+@node Hardware requirements
+@section Hardware requirements
+
+Knot DNS requirements are not very demanding for typical
+installations, and a commodity server or a virtual solution
+will be sufficient in most cases.
+
+However please note that there are some scenarios that will
+require administrator attention and testing of exact
+requirements before deploying Knot DNS in production. These
+cases include deployment for a large number of zones (DNS
+hosting), a large number of records in one or more zones (TLD)
+or large number of requests.
+
+@node CPU requirements
+@section CPU requirements
+
+Knot DNS scales with processing power and also with the number of available cores/CPUs.
+
+There is no lower bound on the CPU requirements, but it should support memory barriers
+and CAS (i586 and newer).
+
+@node Memory requirements
+@section Memory requirements
+
+Knot DNS implementation focuses on performance and thus can
+be quite demanding for memory. The rough estimate for memory
+requirements is 5-7 times of the size of the zone in text
+format. Again this is only an estimate and you are advised to do
+your own measurements before deploying Knot DNS into production.
+
+Also note that to ensure uninterrupted serving of the zone, Knot DNS employs
+a Read-Copy-Update mechanism instead of locking and thus requires
+twice the amount of memory for the duration of incoming transfers.
+
+@node Supported operating system
+@section Supported operating system
+
+Knot DNS itself is written in a portable way, but it depends on
+several libraries. Namely userspace-rcu, which could be a constraint when it
+comes to the operating system support. As far as we know the
+Knot DNS can be compiled and run on Linux, FreeBSD, OpenBSD, NetBSD and Mac OS X.
diff --git a/doc/running.texi b/doc/running.texi
new file mode 100755
index 0000000..628cd7d
--- /dev/null
+++ b/doc/running.texi
@@ -0,0 +1,187 @@
+@node Running Knot DNS, Troubleshooting, Knot DNS Configuration, Top
+@chapter Running Knot DNS
+
+@menu
+* Running a slave server::
+* Running a master server::
+* Controlling running daemon::
+@end menu
+
+Knot DNS is designed to compile zone files before loading them into server.
+The reason for this is to speed up server startup, but requires a bit of user
+effort, so each time the zone file changes you need to compile it.
+@example
+$ knotc -c knot.conf compile
+@end example
+Or alternatively, you can compile automatically using the @code{-a} flag.
+@example
+$ knotc -a -c knot.conf start|reload|restart
+@end example
+
+The tool @code{knotc} is designed as a front-end for user, making it easier
+to do everything from zone compilation to controlling the server daemon.
+To communicate with the binary, it reads the PID from the @emph{PID file} specified in the configuration @code{pidfile} and sends POSIX signals to it (@pxref{pidfile}).
+If you want to control the daemon directly, use @code{SIGINT} to quit the process or @code{SIGHUP} to reload configuration. Signal @code{SIGUSR2} is currently used to refresh slave zones.
+
+@example
+Usage: knotc [parameters] start|stop|restart|reload|running|compile
+Parameters:
+ -c [file], --config=[file]
+ Select configuration file.
+ -j [num], --jobs=[num]
+ Number of parallel tasks to run when compiling.
+ -f, --force
+ Force operation - override some checks.
+ -v, --verbose
+ Verbose mode - additional runtime information.
+ -V, --version
+ Print knot server version.
+ -w, --wait
+ Wait for the server to finish start/stop operations.
+ -i, --interactive
+ Interactive mode (do not daemonize).
+ -a, --auto
+ Enable automatic recompilation (start or reload).
+ -h, --help
+ Print help and usage.
+
+Actions:
+ start Start knot server zone (no-op if running).
+ stop Stop knot server (no-op if not running).
+ restart Stops and then starts knot server.
+ reload Reload knot configuration and compiled zones.
+ refresh Refresh all slave zones.
+ running Check if server is running.
+ checkconf Check server configuration.
+ checkzone Check zones (accepts specific zones,
+ e.g. 'knotc checkzone example1.com example2.com').
+ compile Compile zones (accepts specific zones, see above).
+@end example
+
+If you want to run Knot DNS daemon directly, you can use @code{knotd} binary
+to do that. It accepts just configuration file and option to run in background.
+@example
+Usage: knotd [parameters]
+
+Parameters:
+ -c, --config [file] Select configuration file.
+ -d, --daemonize Run server as a daemon.
+ -v, --verbose Verbose mode - additional runtime information.
+ -V, --version Print version of the server.
+ -h, --help Print help and usage.
+@end example
+
+Also, the server needs to create several files in order to run properly.
+All files are placed in the directory described by @code{storage} (@pxref{storage}).
+PID file can be placed elsewhere using the @code{pidfile} statement (@pxref{pidfile}).
+Slave zones with relative path specified will be placed in the @code{storage} as well.
+@itemize @bullet
+@item
+@emph{Compiled zones} - preprocessed zones, for example zone @code{example.com} will be
+placed in @file{STORAGE/example.com.db}.
+@item
+@emph{Journal files} - each zone has a journal file to store differences for IXFR and
+dynamic updates. Journal for zone @code{example.com} will be
+placed in @file{STORAGE/example.com.diff.db}.
+@item
+@emph{PID file} - unless specified differently by the @code{pidfile}, it will be placed
+in the @file{STORAGE/knot.pid}.
+@item
+@emph{Checksum files} - in order to identify compiled zone corruption, it
+has a separate checksum file. For @code{example.com} will be
+placed in @file{STORAGE/example.com.db.crc}.
+@end itemize
+
+@node Running a slave server
+@section Running a slave server
+
+Running the server as a slave is very straightforward as you usually bootstrap
+zones over AXFR and thus avoid any manual zone compilation.
+When a zone is transferred over AXFR, both the compiled zone and the zone file is
+updated, so no further compilation is needed.
+However when IXFR transfer finishes, it stores the differences in a journal file
+and doesn't update the zone file nor compiled zone immediately,
+but there is a timer that checks periodically for new differences and
+updates both zone file and the compiled zone. You can configure this timer
+with the @code{zonefile-sync} statement in @code{zones} (@pxref{zones}).
+
+There are two ways to start the server - directly or with the @code{knotc} controller tool.
+First, let us start it directly. If you do not pass any configuration, it will try to
+search configuration in default path that is @code{SYSCONFDIR/knot.conf}. The @code{SYSCONFDIR}
+depends on what you passed to the @code{./configure}, usually @code{/etc}.
+
+@example
+$ knotc -c slave.conf checkconf # check configuration
+$ knotd -c slave.conf
+@end example
+
+However to start it as a daemon, @code{knotc} tool should be used.
+The @code{knotc} tool accepts parameter @code{-w} to wait until the requested operation finishes.
+When the action is "start" for example, it waits until the server starts to serve zones.
+@example
+$ knotc -w -c slave.conf start # start the daemon
+$ knotc -c slave.conf stop # stop the daemon
+@end example
+
+When the server is running, you can control the daemon, see @ref{Controlling running daemon}.
+
+@node Running a master server
+@section Running a master server
+
+Knot DNS first needs to compile the zones before it can load them, therefore you need to
+compile them with the @code{knotc compile} action or use flag @code{-a} to compile the zones automatically.
+
+If you want to just check the zone files first before starting,
+you can use @code{knotc checkzone} action.
+@example
+$ knotc -c master.conf checkzone example.com
+@end example
+
+Starting and stopping the daemon is the same as with the slave server in the previous section.
+@example
+$ knotc -c master.conf compile
+$ knotc -w -c master.conf start
+@end example
+
+Or you can compile it automatically:
+@example
+$ knotc -c master.conf checkconf # check configuration
+$ knotc -a -w -c master.conf start
+@end example
+
+@node Controlling running daemon
+@section Controlling running daemon
+
+Knot DNS was designed to allow server reconfiguration on-the-fly without interrupting
+its operation. Thus it is possible to change both configuration and zone files and
+also add or remove zones without restarting the server. This can be done with the
+@code{knotc reload} action.
+
+@example
+$ knotc -c master.conf compile # compile updated zones
+$ knotc -c master.conf reload # reconfigure and load updated zones
+@end example
+
+Or use the @code{-a} again.
+@example
+$ knotc -a -c master.conf reload # compile zones and reconfigure
+@end example
+
+If you want @emph{IXFR-out} differences created from changes you make to a zone file, enable @code{ixfr-from-differences}
+in @code{zones} statement, then compile the zone and reload your server as seen above.
+If @emph{SOA}'s @emph{serial} is not changed no differences will be created. Please note
+that this feature is in @emph{experimental} stage and should be used with care.
+If you encounter a bug using this feature, please send it to Knot developers (@pxref{Submitting a bugreport}).
+
+You can also choose to tear-down the server fully and restart with the @code{knotc restart} action.
+@example
+$ knotc -c master.conf running # check if running
+$ knotc -c master.conf restart # fully restart
+@end example
+
+If you want to force refresh the slave zones, you can do this with the @code{knotc refresh} action.
+@example
+$ knotc -c slave.conf refresh
+@end example
+
+For a complete list of actions refer to @code{knotc --help} command output.
diff --git a/doc/security.texi b/doc/security.texi
new file mode 100755
index 0000000..3716521
--- /dev/null
+++ b/doc/security.texi
@@ -0,0 +1,15 @@
+@node Security Considerations, Troubleshooting, Running Knot DNS, Top
+@chapter Security Considerations
+
+[TODO]
+- faces the internet
+
+If libcap-ng is available, Knot DNS on Linux takes advantage of
+the POSIX 1003.1e capabilities. This mechanism breaks the a set of privileges
+traditionally associated with the root into groups that can be set per-thread
+and independently enabled or disabled. For more information, look up manual page
+for capabilities(7).
+
+Knot DNS uses strips exposed threads of most capabilities like file access,
+privileged socket operations and such.
+This mitigates potential remote exploits or at least the impact.
diff --git a/doc/texinfo.tex b/doc/texinfo.tex
new file mode 100755
index 0000000..9140826
--- /dev/null
+++ b/doc/texinfo.tex
@@ -0,0 +1,9291 @@
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2009-08-14.15}
+%
+% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+% 2007, 2008, 2009 Free Software Foundation, Inc.
+%
+% This texinfo.tex file is free software: you can redistribute it and/or
+% modify it under the terms of the GNU General Public License as
+% published by the Free Software Foundation, either version 3 of the
+% License, or (at your option) any later version.
+%
+% This texinfo.tex file is distributed in the hope that it will be
+% useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+% General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this program. If not, see <http://www.gnu.org/licenses/>.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction. (This has been our intent since Texinfo was invented.)
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
+% ftp://tug.org/tex/texinfo.tex
+% (and all CTAN mirrors, see http://www.ctan.org).
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org. Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem. Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution. For a simple
+% manual foo.texi, however, you can get away with this:
+% tex foo.texi
+% texindex foo.??
+% tex foo.texi
+% tex foo.texi
+% dvips foo.dvi -o # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent. You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
+
+
+\message{Loading texinfo [version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+ \catcode`+=\active \catcode`\_=\active}
+
+
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros whose names we will redefine.
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
+\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexstar=\*
+\let\ptext=\t
+\let\ptextop=\top
+{\catcode`\'=\active
+\global\let\ptexquoteright'}% Math-mode def from plain.tex.
+\let\ptexraggedright=\raggedright
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Pre-3.0.
+\else
+ \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi
+\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi
+
+% Since the category of space is not known, we have to be careful.
+\chardef\spacecat = 10
+\def\spaceisspace{\catcode`\ =\spacecat}
+
+% sometimes characters are active, so we need control sequences.
+\chardef\colonChar = `\:
+\chardef\commaChar = `\,
+\chardef\dashChar = `\-
+\chardef\dotChar = `\.
+\chardef\exclamChar= `\!
+\chardef\lquoteChar= `\`
+\chardef\questChar = `\?
+\chardef\rquoteChar= `\'
+\chardef\semiChar = `\;
+\chardef\underChar = `\_
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+ ap-pen-dix bit-map bit-maps
+ data-base data-bases eshell fall-ing half-way long-est man-u-script
+ man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+ par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+ spell-ing spell-ings
+ stand-alone strong-est time-stamp time-stamps which-ever white-space
+ wide-spread wrap-around
+}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen\bindingoffset
+\newdimen\normaloffset
+\newdimen\pagewidth \newdimen\pageheight
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+% @| inserts a changebar to the left of the current line. It should
+% surround any changed text. This approach does *not* work if the
+% change spans more than two lines of output. To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+ % \vadjust can only be used in horizontal mode.
+ \leavevmode
+ %
+ % Append this vertical mode material after the current line in the output.
+ \vadjust{%
+ % We want to insert a rule with the height and depth of the current
+ % leading; that is exactly what \strutbox is supposed to record.
+ \vskip-\baselineskip
+ %
+ % \vadjust-items are inserted at the left edge of the type. So
+ % the \llap here moves out into the left-hand margin.
+ \llap{%
+ %
+ % For a thicker or thinner bar, change the `1pt'.
+ \vrule height\baselineskip width1pt
+ %
+ % This is the space between the bar and the text.
+ \hskip 12pt
+ }%
+ }%
+}
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal. We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+ \tracingstats2
+ \tracingpages1
+ \tracinglostchars2 % 2 gives us more in etex
+ \tracingparagraphs1
+ \tracingoutput1
+ \tracingmacros2
+ \tracingrestores1
+ \showboxbreadth\maxdimen \showboxdepth\maxdimen
+ \ifx\eTeXversion\undefined\else % etex gives us more logging
+ \tracingscantokens1
+ \tracingifs1
+ \tracinggroups1
+ \tracingnesting2
+ \tracingassigns1
+ \fi
+ \tracingcommands3 % 3 gives us more in etex
+ \errorcontextlines16
+}%
+
+% add check for \lastpenalty to plain's definitions. If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+ \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+ \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+ \removelastskip\penalty-200\bigskip\fi\fi}
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Output a mark which sets \thischapter, \thissection and \thiscolor.
+% We dump everything together because we only have one kind of mark.
+% This works because we only use \botmark / \topmark, not \firstmark.
+%
+% A mark contains a subexpression of the \ifcase ... \fi construct.
+% \get*marks macros below extract the needed part using \ifcase.
+%
+% Another complication is to let the user choose whether \thischapter
+% (\thissection) refers to the chapter (section) in effect at the top
+% of a page, or that at the bottom of a page. The solution is
+% described on page 260 of The TeXbook. It involves outputting two
+% marks for the sectioning macros, one before the section break, and
+% one after. I won't pretend I can describe this better than DEK...
+\def\domark{%
+ \toks0=\expandafter{\lastchapterdefs}%
+ \toks2=\expandafter{\lastsectiondefs}%
+ \toks4=\expandafter{\prevchapterdefs}%
+ \toks6=\expandafter{\prevsectiondefs}%
+ \toks8=\expandafter{\lastcolordefs}%
+ \mark{%
+ \the\toks0 \the\toks2
+ \noexpand\or \the\toks4 \the\toks6
+ \noexpand\else \the\toks8
+ }%
+}
+% \topmark doesn't work for the very first chapter (after the title
+% page or the contents), so we use \firstmark there -- this gets us
+% the mark with the chapter defs, unless the user sneaks in, e.g.,
+% @setcolor (or @url, or @link, etc.) between @contents and the very
+% first @chapter.
+\def\gettopheadingmarks{%
+ \ifcase0\topmark\fi
+ \ifx\thischapter\empty \ifcase0\firstmark\fi \fi
+}
+\def\getbottomheadingmarks{\ifcase1\botmark\fi}
+\def\getcolormarks{\ifcase2\topmark\fi}
+
+% Avoid "undefined control sequence" errors.
+\def\lastchapterdefs{}
+\def\lastsectiondefs{}
+\def\prevchapterdefs{}
+\def\prevsectiondefs{}
+\def\lastcolordefs{}
+
+% Main output routine.
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+ \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+ %
+ \ifodd\pageno \advance\hoffset by \bindingoffset
+ \else \advance\hoffset by -\bindingoffset\fi
+ %
+ % Do this outside of the \shipout so @code etc. will be expanded in
+ % the headline as they should be, not taken literally (outputting ''code).
+ \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi
+ \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
+ \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi
+ \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
+ %
+ {%
+ % Have to do this stuff outside the \shipout because we want it to
+ % take effect in \write's, yet the group defined by the \vbox ends
+ % before the \shipout runs.
+ %
+ \indexdummies % don't expand commands in the output.
+ \normalturnoffactive % \ in index entries must not stay \, e.g., if
+ % the page break happens to be in the middle of an example.
+ % We don't want .vr (or whatever) entries like this:
+ % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
+ % "\acronym" won't work when it's read back in;
+ % it needs to be
+ % {\code {{\tt \backslashcurfont }acronym}
+ \shipout\vbox{%
+ % Do this early so pdf references go to the beginning of the page.
+ \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+ %
+ \ifcropmarks \vbox to \outervsize\bgroup
+ \hsize = \outerhsize
+ \vskip-\topandbottommargin
+ \vtop to0pt{%
+ \line{\ewtop\hfil\ewtop}%
+ \nointerlineskip
+ \line{%
+ \vbox{\moveleft\cornerthick\nstop}%
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}%
+ }%
+ \vss}%
+ \vskip\topandbottommargin
+ \line\bgroup
+ \hfil % center the page within the outer (page) hsize.
+ \ifodd\pageno\hskip\bindingoffset\fi
+ \vbox\bgroup
+ \fi
+ %
+ \unvbox\headlinebox
+ \pagebody{#1}%
+ \ifdim\ht\footlinebox > 0pt
+ % Only leave this space if the footline is nonempty.
+ % (We lessened \vsize for it in \oddfootingyyy.)
+ % The \baselineskip=24pt in plain's \makefootline has no effect.
+ \vskip 24pt
+ \unvbox\footlinebox
+ \fi
+ %
+ \ifcropmarks
+ \egroup % end of \vbox\bgroup
+ \hfil\egroup % end of (centering) \line\bgroup
+ \vskip\topandbottommargin plus1fill minus1fill
+ \boxmaxdepth = \cornerthick
+ \vbox to0pt{\vss
+ \line{%
+ \vbox{\moveleft\cornerthick\nsbot}%
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}%
+ }%
+ \nointerlineskip
+ \line{\ewbot\hfil\ewbot}%
+ }%
+ \egroup % \vbox from first cropmarks clause
+ \fi
+ }% end of \shipout\vbox
+ }% end of group with \indexdummies
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1\relax \unvbox#1\relax
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+ \def\argtorun{#2}%
+ \begingroup
+ \obeylines
+ \spaceisspace
+ #1%
+ \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ \argremovecomment #1\comment\ArgTerm%
+ }%
+}
+
+% First remove any @comment, then any @c comment.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
+
+% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+% @end itemize @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+ \def\temp{#3}%
+ \ifx\temp\empty
+ % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp:
+ \let\temp\finishparsearg
+ \else
+ \let\temp\argcheckspaces
+ \fi
+ % Put the space token in:
+ \temp#1 #3\ArgTerm
+}
+
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \argtorun.
+% (Similarly, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}}
+
+% \parseargdef\foo{...}
+% is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+%
+% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
+% favourite TeX trick. --kasal, 16nov03
+
+\def\parseargdef#1{%
+ \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+ \def#2{\parsearg#1}%
+ \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
+ \obeyspaces
+ \gdef\obeyedspace{ }
+
+ % Make each space character in the input produce a normal interword
+ % space in the output. Don't allow a line break at this space, as this
+ % is used only in environments like @example, where each line of input
+ % should produce a line of output anyway.
+ %
+ \gdef\sepspaces{\obeyspaces\let =\tie}
+
+ % If an index command is used in an @example environment, any spaces
+ % therein should become regular spaces in the raw index file, not the
+ % expansion of \tie (\leavevmode \penalty \@M \ ).
+ \gdef\unsepspaces{\let =\space}
+}
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+% Define the framework for environments in texinfo.tex. It's used like this:
+%
+% \envdef\foo{...}
+% \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo. \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches. The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as environments; they don't open a group. (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At run-time, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+ \def\temp{#1}%
+ \ifx\thisenv\temp
+ \else
+ \badenverr
+ \fi
+}
+
+% Environment mismatch, #1 expected:
+\def\badenverr{%
+ \errhelp = \EMsimple
+ \errmessage{This command can appear only \inenvironment\temp,
+ not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+ \ifx#1\empty
+ out of any environment%
+ \else
+ in environment \expandafter\string#1%
+ \fi
+}
+
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+ \if 1\csname iscond.#1\endcsname
+ \else
+ % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03
+ \expandafter\checkenv\csname#1\endcsname
+ \csname E#1\endcsname
+ \endgroup
+ \fi
+}
+
+\newhelp\EMsimple{Press RETURN to continue.}
+
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt\char64}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+\def\mylbrace {{\tt\char123}}
+\def\myrbrace {{\tt\char125}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+\begingroup
+ % Definitions to produce \{ and \} commands for indices,
+ % and @{ and @} for the aux/toc files.
+ \catcode`\{ = \other \catcode`\} = \other
+ \catcode`\[ = 1 \catcode`\] = 2
+ \catcode`\! = 0 \catcode`\\ = \other
+ !gdef!lbracecmd[\{]%
+ !gdef!rbracecmd[\}]%
+ !gdef!lbraceatcmd[@{]%
+ !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \c
+\let\dotaccent = \.
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \t
+\let\ubaraccent = \b
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+ \def\temp{#1}%
+ \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi
+ \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi
+ \else \errmessage{@dotless can be used only with i or j}%
+ \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence. (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo. Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+ L\kern-.36em
+ {\setbox0=\hbox{T}%
+ \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}%
+ \kern-.15em
+ \TeX
+}
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @/ allows a line break.
+\let\/=\allowbreak
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
+
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
+
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+ \def\temp{#1}%
+ \ifx\temp\onword \plainfrenchspacing
+ \else\ifx\temp\offword \plainnonfrenchspacing
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @frenchspacing option `\temp', must be on/off}%
+ \fi\fi
+}
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+% Another complication is that the group might be very large. This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material. In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom. The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+ \ifnum\catcode`\^^M=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ \startsavinginserts
+ %
+ \setbox\groupbox = \vtop\bgroup
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it. Thus, space below is not quite equal to space
+% above. But it's pretty close.
+\def\Egroup{%
+ % To get correct interline space between the last line of the group
+ % and the first line afterwards, we have to propagate \prevdepth.
+ \endgraf % Not \par, as it may have been set to \lisppar.
+ \global\dimen1 = \prevdepth
+ \egroup % End the \vtop.
+ % \dimen0 is the vertical size of the group's box.
+ \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox
+ % \dimen2 is how much space is left on the page (more or less).
+ \dimen2 = \pageheight \advance\dimen2 by -\pagetotal
+ % if the group doesn't fit on the current page, and it's a big big
+ % group, force a page break.
+ \ifdim \dimen0 > \dimen2
+ \ifdim \pagetotal < \vfilllimit\pageheight
+ \page
+ \fi
+ \fi
+ \box\groupbox
+ \prevdepth = \dimen1
+ \checkinserts
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+% Old definition--didn't work.
+%\parseargdef\need{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak
+%\prevdepth=-1000pt
+%}}
+
+\parseargdef\need{%
+ % Ensure vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % If the @need value is less than one line space, it's useless.
+ \dimen0 = #1\mil
+ \dimen2 = \ht\strutbox
+ \advance\dimen2 by \dp\strutbox
+ \ifdim\dimen0 > \dimen2
+ %
+ % Do a \strut just to make the height of this box be normal, so the
+ % normal leading is inserted relative to the preceding line.
+ % And a page break here is fine.
+ \vtop to #1\mil{\strut\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+ \fi
+}
+
+% @br forces paragraph break (and is undocumented).
+
+\let\br = \par
+
+% @page forces the start of a new page.
+%
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
+
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+ \leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph. For more general purposes, use the \margin insertion
+% class. WHICH is `l' or `r'.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+ \nobreak
+ \kern-\strutdepth
+ \vtop to \strutdepth{%
+ \baselineskip=\strutdepth
+ \vss
+ % if you have multiple lines of stuff to put here, you'll need to
+ % make the vbox yourself of the appropriate size.
+ \ifx#1l%
+ \llap{\ignorespaces #2\hskip\inmarginspacing}%
+ \else
+ \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+ \fi
+ \null
+ }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \def\lefttext{#1}% have both texts
+ \def\righttext{#2}%
+ \else
+ \def\lefttext{#1}% have only one text
+ \def\righttext{#1}%
+ \fi
+ %
+ \ifodd\pageno
+ \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+ \else
+ \def\temp{\inleftmargin\lefttext}%
+ \fi
+ \temp
+}
+
+% @include FILE -- \input text of FILE.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+ \pushthisfilestack
+ \def\thisfile{#1}%
+ {%
+ \makevalueexpandable % we want to expand any @value in FILE.
+ \turnoffactive % and allow special characters in the expansion
+ \indexnofonts % Allow `@@' and other weird things in file names.
+ \edef\temp{\noexpand\input #1 }%
+ %
+ % This trickery is to read FILE outside of a group, in case it makes
+ % definitions, etc.
+ \expandafter
+ }\temp
+ \popthisfilestack
+}
+\def\filenamecatcodes{%
+ \catcode`\\=\other
+ \catcode`~=\other
+ \catcode`^=\other
+ \catcode`_=\other
+ \catcode`|=\other
+ \catcode`<=\other
+ \catcode`>=\other
+ \catcode`+=\other
+ \catcode`-=\other
+ \catcode`\`=\other
+ \catcode`\'=\other
+}
+
+\def\pushthisfilestack{%
+ \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+ \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+ \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
+
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+ the stack of filenames is empty.}}
+
+\def\thisfile{}
+
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+ \ifhmode
+ \let\next\centerH
+ \else
+ \let\next\centerV
+ \fi
+ \next{\hfil \ignorespaces#1\unskip \hfil}%
+}
+\def\centerH#1{%
+ {%
+ \hfil\break
+ \advance\hsize by -\leftskip
+ \advance\hsize by -\rightskip
+ \line{#1}%
+ \break
+ }%
+}
+\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}}
+
+% @sp n outputs n lines of vertical space
+
+\parseargdef\sp{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\begingroup \catcode`\^^M=\other%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\commentxxx}
+{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
+
+\let\c=\comment
+
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \defaultparindent = 0pt
+ \else
+ \defaultparindent = #1em
+ \fi
+ \fi
+ \parindent = \defaultparindent
+}
+
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \lispnarrowing = 0pt
+ \else
+ \lispnarrowing = #1em
+ \fi
+ \fi
+}
+
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading. If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\noneword
+ \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+ \else\ifx\temp\insertword
+ \let\suppressfirstparagraphindent = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @firstparagraphindent option `\temp'}%
+ \fi\fi
+}
+
+% Here is how we actually suppress indentation. Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+ \gdef\indent{%
+ \restorefirstparagraphindent
+ \indent
+ }%
+ \gdef\noindent{%
+ \restorefirstparagraphindent
+ \noindent
+ }%
+ \global\everypar = {%
+ \kern -\parindent
+ \restorefirstparagraphindent
+ }%
+}
+
+\gdef\restorefirstparagraphindent{%
+ \global \let \indent = \ptexindent
+ \global \let \noindent = \ptexnoindent
+ \global \everypar = {}%
+}
+
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}. So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+ \catcode`\_ = \active
+ \gdef\mathunderscore{%
+ \catcode`\_=\active
+ \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+ }
+}
+% Another complication: we want \\ (and @\) to output a \ character.
+% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
+% this is not advertised and we don't care. Texinfo does not
+% otherwise define @\.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+ \tex
+ \mathunderscore
+ \let\\ = \mathbackslash
+ \mathactive
+ % make the texinfo accent commands work in math mode
+ \let\"=\ddot
+ \let\'=\acute
+ \let\==\bar
+ \let\^=\hat
+ \let\`=\grave
+ \let\u=\breve
+ \let\v=\check
+ \let\~=\tilde
+ \let\dotaccent=\dot
+ $\finishmath
+}
+\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+ \catcode`^ = \active
+ \catcode`< = \active
+ \catcode`> = \active
+ \catcode`+ = \active
+ \catcode`' = \active
+ \gdef\mathactive{%
+ \let^ = \ptexhat
+ \let< = \ptexless
+ \let> = \ptexgtr
+ \let+ = \ptexplus
+ \let' = \ptexquoteright
+ }
+}
+
+% Some math mode symbols.
+\def\bullet{$\ptexbullet$}
+\def\geq{\ifmmode \ge\else $\ge$\fi}
+\def\leq{\ifmmode \le\else $\le$\fi}
+\def\minus{\ifmmode -\else $-$\fi}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in the cm
+% typewriter fonts as three actual period characters; on the other hand,
+% in other typewriter fonts three periods are wider than 1.5em. So do
+% whichever is larger.
+%
+\def\dots{%
+ \leavevmode
+ \setbox0=\hbox{...}% get width of three periods
+ \ifdim\wd0 > 1.5em
+ \dimen0 = \wd0
+ \else
+ \dimen0 = 1.5em
+ \fi
+ \hbox to \dimen0{%
+ \hskip 0pt plus.25fil
+ .\hskip 0pt plus1fil
+ .\hskip 0pt plus1fil
+ .\hskip 0pt plus.5fil
+ }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+ \dots
+ \spacefactor=\endofsentencespacefactor
+}
+
+% @comma{} is so commas can be inserted into text without messing up
+% Texinfo's parsing.
+%
+\let\comma = ,
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate (before @setfilename).
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \iflinks
+ \tryauxfile
+ % Open the new aux file. TeX will close it automatically at exit.
+ \immediate\openout\auxfile=\jobname.aux
+ \fi % \openindices needs to do some work in any case.
+ \openindices
+ \let\setfilename=\comment % Ignore extra @setfilename cmds.
+ %
+ % If texinfo.cnf is present on the system, read it.
+ % Useful for site-wide @afourpaper, etc.
+ \openin 1 texinfo.cnf
+ \ifeof 1 \else \input texinfo.cnf \fi
+ \closein 1
+ %
+ \comment % Ignore the actual filename.
+}
+
+% Called from \setfilename.
+%
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set). So we test for \relax and 0 as well as \undefined,
+% borrowed from ifpdf.sty.
+\ifx\pdfoutput\undefined
+\else
+ \ifx\pdfoutput\relax
+ \else
+ \ifcase\pdfoutput
+ \else
+ \pdftrue
+ \fi
+ \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places. Thus, we have to
+% double any backslashes. Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e. Not good.
+% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html
+% (and related messages, the final outcome is that it is up to the TeX
+% user to double the backslashes and otherwise make the string valid, so
+% that's what we do).
+
+% double active backslashes.
+%
+{\catcode`\@=0 \catcode`\\=\active
+ @gdef@activebackslashdouble{%
+ @catcode`@\=@active
+ @let\=@doublebackslash}
+}
+
+% To handle parens, we must adopt a different approach, since parens are
+% not active characters. hyperref.dtx (which has the same problem as
+% us) handles it with this amazing macro to replace tokens, with minor
+% changes for Texinfo. It is included here under the GPL by permission
+% from the author, Heiko Oberdiek.
+%
+% #1 is the tokens to replace.
+% #2 is the replacement.
+% #3 is the control sequence with the string.
+%
+\def\HyPsdSubst#1#2#3{%
+ \def\HyPsdReplace##1#1##2\END{%
+ ##1%
+ \ifx\\##2\\%
+ \else
+ #2%
+ \HyReturnAfterFi{%
+ \HyPsdReplace##2\END
+ }%
+ \fi
+ }%
+ \xdef#3{\expandafter\HyPsdReplace#3#1\END}%
+}
+\long\def\HyReturnAfterFi#1\fi{\fi#1}
+
+% #1 is a control sequence in which to do the replacements.
+\def\backslashparens#1{%
+ \xdef#1{#1}% redefine it as its expansion; the definition is simply
+ % \lastnode when called from \setref -> \pdfmkdest.
+ \HyPsdSubst{(}{\realbackslash(}{#1}%
+ \HyPsdSubst{)}{\realbackslash)}{#1}%
+}
+
+\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images
+with PDF output, and none of those formats could be found. (.eps cannot
+be supported due to the design of the PDF format; use regular TeX (DVI
+output) for that.)}
+
+\ifpdf
+ %
+ % Color manipulation macros based on pdfcolor.tex,
+ % except using rgb instead of cmyk; the latter is said to render as a
+ % very dark gray on-screen and a very dark halftone in print, instead
+ % of actual black.
+ \def\rgbDarkRed{0.50 0.09 0.12}
+ \def\rgbBlack{0 0 0}
+ %
+ % k sets the color for filling (usual text, etc.);
+ % K sets the color for stroking (thin rules, e.g., normal _'s).
+ \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}}
+ %
+ % Set color, and create a mark which defines \thiscolor accordingly,
+ % so that \makeheadline knows which color to restore.
+ \def\setcolor#1{%
+ \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}%
+ \domark
+ \pdfsetcolor{#1}%
+ }
+ %
+ \def\maincolor{\rgbBlack}
+ \pdfsetcolor{\maincolor}
+ \edef\thiscolor{\maincolor}
+ \def\lastcolordefs{}
+ %
+ \def\makefootline{%
+ \baselineskip24pt
+ \line{\pdfsetcolor{\maincolor}\the\footline}%
+ }
+ %
+ \def\makeheadline{%
+ \vbox to 0pt{%
+ \vskip-22.5pt
+ \line{%
+ \vbox to8.5pt{}%
+ % Extract \thiscolor definition from the marks.
+ \getcolormarks
+ % Typeset the headline with \maincolor, then restore the color.
+ \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}%
+ }%
+ \vss
+ }%
+ \nointerlineskip
+ }
+ %
+ %
+ \pdfcatalog{/PageMode /UseOutlines}
+ %
+ % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto).
+ \def\dopdfimage#1#2#3{%
+ \def\imagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}%
+ \def\imageheight{#3}\setbox2 = \hbox{\ignorespaces #3}%
+ %
+ % pdftex (and the PDF format) support .png, .jpg, .pdf (among
+ % others). Let's try in that order.
+ \let\pdfimgext=\empty
+ \begingroup
+ \openin 1 #1.png \ifeof 1
+ \openin 1 #1.jpg \ifeof 1
+ \openin 1 #1.jpeg \ifeof 1
+ \openin 1 #1.JPG \ifeof 1
+ \openin 1 #1.pdf \ifeof 1
+ \openin 1 #1.PDF \ifeof 1
+ \errhelp = \nopdfimagehelp
+ \errmessage{Could not find image file #1 for pdf}%
+ \else \gdef\pdfimgext{PDF}%
+ \fi
+ \else \gdef\pdfimgext{pdf}%
+ \fi
+ \else \gdef\pdfimgext{JPG}%
+ \fi
+ \else \gdef\pdfimgext{jpeg}%
+ \fi
+ \else \gdef\pdfimgext{jpg}%
+ \fi
+ \else \gdef\pdfimgext{png}%
+ \fi
+ \closein 1
+ \endgroup
+ %
+ % without \immediate, ancient pdftex seg faults when the same image is
+ % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.)
+ \ifnum\pdftexversion < 14
+ \immediate\pdfimage
+ \else
+ \immediate\pdfximage
+ \fi
+ \ifdim \wd0 >0pt width \imagewidth \fi
+ \ifdim \wd2 >0pt height \imageheight \fi
+ \ifnum\pdftexversion<13
+ #1.\pdfimgext
+ \else
+ {#1.\pdfimgext}%
+ \fi
+ \ifnum\pdftexversion < 14 \else
+ \pdfrefximage \pdflastximage
+ \fi}
+ %
+ \def\pdfmkdest#1{{%
+ % We have to set dummies so commands such as @code, and characters
+ % such as \, aren't expanded when present in a section title.
+ \indexnofonts
+ \turnoffactive
+ \activebackslashdouble
+ \makevalueexpandable
+ \def\pdfdestname{#1}%
+ \backslashparens\pdfdestname
+ \safewhatsit{\pdfdest name{\pdfdestname} xyz}%
+ }}
+ %
+ % used to mark target names; must be expandable.
+ \def\pdfmkpgn#1{#1}
+ %
+ % by default, use a color that is dark enough to print on paper as
+ % nearly black, but still distinguishable for online viewing.
+ \def\urlcolor{\rgbDarkRed}
+ \def\linkcolor{\rgbDarkRed}
+ \def\endlink{\setcolor{\maincolor}\pdfendlink}
+ %
+ % Adding outlines to PDF; macros for calculating structure of outlines
+ % come from Petr Olsak
+ \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+ \else \csname#1\endcsname \fi}
+ \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+ \advance\tempnum by 1
+ \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+ %
+ % #1 is the section text, which is what will be displayed in the
+ % outline by the pdf viewer. #2 is the pdf expression for the number
+ % of subentries (or empty, for subsubsections). #3 is the node text,
+ % which might be empty if this toc entry had no corresponding node.
+ % #4 is the page number
+ %
+ \def\dopdfoutline#1#2#3#4{%
+ % Generate a link to the node text if that exists; else, use the
+ % page number. We could generate a destination for the section
+ % text in the case where a section has no node, but it doesn't
+ % seem worth the trouble, since most documents are normally structured.
+ \def\pdfoutlinedest{#3}%
+ \ifx\pdfoutlinedest\empty
+ \def\pdfoutlinedest{#4}%
+ \else
+ % Doubled backslashes in the name.
+ {\activebackslashdouble \xdef\pdfoutlinedest{#3}%
+ \backslashparens\pdfoutlinedest}%
+ \fi
+ %
+ % Also double the backslashes in the display string.
+ {\activebackslashdouble \xdef\pdfoutlinetext{#1}%
+ \backslashparens\pdfoutlinetext}%
+ %
+ \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+ }
+ %
+ \def\pdfmakeoutlines{%
+ \begingroup
+ % Thanh's hack / proper braces in bookmarks
+ \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
+ \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
+ %
+ % Read toc silently, to get counts of subentries for \pdfoutline.
+ \def\numchapentry##1##2##3##4{%
+ \def\thischapnum{##2}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsecentry##1##2##3##4{%
+ \advancenumber{chap\thischapnum}%
+ \def\thissecnum{##2}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsubsecentry##1##2##3##4{%
+ \advancenumber{sec\thissecnum}%
+ \def\thissubsecnum{##2}%
+ }%
+ \def\numsubsubsecentry##1##2##3##4{%
+ \advancenumber{subsec\thissubsecnum}%
+ }%
+ \def\thischapnum{0}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ %
+ % use \def rather than \let here because we redefine \chapentry et
+ % al. a second time, below.
+ \def\appentry{\numchapentry}%
+ \def\appsecentry{\numsecentry}%
+ \def\appsubsecentry{\numsubsecentry}%
+ \def\appsubsubsecentry{\numsubsubsecentry}%
+ \def\unnchapentry{\numchapentry}%
+ \def\unnsecentry{\numsecentry}%
+ \def\unnsubsecentry{\numsubsecentry}%
+ \def\unnsubsubsecentry{\numsubsubsecentry}%
+ \readdatafile{toc}%
+ %
+ % Read toc second time, this time actually producing the outlines.
+ % The `-' means take the \expnumber as the absolute number of
+ % subentries, which we calculated on our first read of the .toc above.
+ %
+ % We use the node names as the destinations.
+ \def\numchapentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+ \def\numsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+ \def\numsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+ \def\numsubsubsecentry##1##2##3##4{% count is always zero
+ \dopdfoutline{##1}{}{##3}{##4}}%
+ %
+ % PDF outlines are displayed using system fonts, instead of
+ % document fonts. Therefore we cannot use special characters,
+ % since the encoding is unknown. For example, the eogonek from
+ % Latin 2 (0xea) gets translated to a | character. Info from
+ % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+ %
+ % xx to do this right, we have to translate 8-bit characters to
+ % their "best" equivalent, based on the @documentencoding. Right
+ % now, I guess we'll just let the pdf reader have its way.
+ \indexnofonts
+ \setupdatafile
+ \catcode`\\=\active \otherbackslash
+ \input \tocreadfilename
+ \endgroup
+ }
+ %
+ \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+ \ifx\PP\D\let\nextsp\relax
+ \else\let\nextsp\skipspaces
+ \ifx\p\space\else\addtokens{\filename}{\PP}%
+ \advance\filenamelength by 1
+ \fi
+ \fi
+ \nextsp}
+ \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax}
+ \ifnum\pdftexversion < 14
+ \let \startlink \pdfannotlink
+ \else
+ \let \startlink \pdfstartlink
+ \fi
+ % make a live url in pdf output.
+ \def\pdfurl#1{%
+ \begingroup
+ % it seems we really need yet another set of dummies; have not
+ % tried to figure out what each command should do in the context
+ % of @url. for now, just make @/ a no-op, that's the only one
+ % people have actually reported a problem with.
+ %
+ \normalturnoffactive
+ \def\@{@}%
+ \let\/=\empty
+ \makevalueexpandable
+ % do we want to go so far as to use \indexnofonts instead of just
+ % special-casing \var here?
+ \def\var##1{##1}%
+ %
+ \leavevmode\setcolor{\urlcolor}%
+ \startlink attr{/Border [0 0 0]}%
+ user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+ \endgroup}
+ \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+ \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+ \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+ \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+ \def\maketoks{%
+ \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+ \ifx\first0\adn0
+ \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+ \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+ \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+ \else
+ \ifnum0=\countA\else\makelink\fi
+ \ifx\first.\let\next=\done\else
+ \let\next=\maketoks
+ \addtokens{\toksB}{\the\toksD}
+ \ifx\first,\addtokens{\toksB}{\space}\fi
+ \fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next}
+ \def\makelink{\addtokens{\toksB}%
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+ \def\pdflink#1{%
+ \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+ \setcolor{\linkcolor}#1\endlink}
+ \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+ % non-pdf mode
+ \let\pdfmkdest = \gobble
+ \let\pdfurl = \gobble
+ \let\endlink = \relax
+ \let\setcolor = \gobble
+ \let\pdfsetcolor = \gobble
+ \let\pdfmakeoutlines = \relax
+\fi % \ifx\pdfoutput
+
+
+\message{fonts,}
+
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+ \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+ \csname ten#1\endcsname % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Unfortunately, we have to override this for titles and the like, since
+% in those cases "rm" is bold. Sigh.
+\def\rmisbold{\rm\def\curfontstyle{bf}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+
+% Default leading.
+\newdimen\textleading \textleading = 13.2pt
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+% can get a sort of poor man's double spacing by redefining this.
+\def\baselinefactor{1}
+%
+\def\setleading#1{%
+ \dimen0 = #1\relax
+ \normalbaselineskip = \baselinefactor\dimen0
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% PDF CMaps. See also LaTeX's t1.cmap.
+%
+% do nothing with this by default.
+\expandafter\let\csname cmapOT1\endcsname\gobble
+\expandafter\let\csname cmapOT1IT\endcsname\gobble
+\expandafter\let\csname cmapOT1TT\endcsname\gobble
+
+% if we are producing pdf, and we have \pdffontattr, then define cmaps.
+% (\pdffontattr was introduced many years ago, but people still run
+% older pdftex's; it's easy to conditionalize, so we do.)
+\ifpdf \ifx\pdffontattr\undefined \else
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1-0)
+%%Title: (TeX-OT1-0 TeX OT1 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<23> <26> <0023>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+40 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+%
+% \cmapOT1IT
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1IT-0)
+%%Title: (TeX-OT1IT-0 TeX OT1IT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1IT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1IT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+8 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<25> <26> <0025>
+<28> <3B> <0028>
+<3F> <5B> <003F>
+<5D> <5E> <005D>
+<61> <7A> <0061>
+<7B> <7C> <2013>
+endbfrange
+42 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <00660066>
+<0C> <00660069>
+<0D> <0066006C>
+<0E> <006600660069>
+<0F> <00660066006C>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<21> <0021>
+<22> <201D>
+<23> <0023>
+<24> <00A3>
+<27> <2019>
+<3C> <00A1>
+<3D> <003D>
+<3E> <00BF>
+<5C> <201C>
+<5F> <02D9>
+<60> <2018>
+<7D> <02DD>
+<7E> <007E>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1IT\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+%
+% \cmapOT1TT
+ \begingroup
+ \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char.
+ \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap
+%%DocumentNeededResources: ProcSet (CIDInit)
+%%IncludeResource: ProcSet (CIDInit)
+%%BeginResource: CMap (TeX-OT1TT-0)
+%%Title: (TeX-OT1TT-0 TeX OT1TT 0)
+%%Version: 1.000
+%%EndComments
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo
+<< /Registry (TeX)
+/Ordering (OT1TT)
+/Supplement 0
+>> def
+/CMapName /TeX-OT1TT-0 def
+/CMapType 2 def
+1 begincodespacerange
+<00> <7F>
+endcodespacerange
+5 beginbfrange
+<00> <01> <0393>
+<09> <0A> <03A8>
+<21> <26> <0021>
+<28> <5F> <0028>
+<61> <7E> <0061>
+endbfrange
+32 beginbfchar
+<02> <0398>
+<03> <039B>
+<04> <039E>
+<05> <03A0>
+<06> <03A3>
+<07> <03D2>
+<08> <03A6>
+<0B> <2191>
+<0C> <2193>
+<0D> <0027>
+<0E> <00A1>
+<0F> <00BF>
+<10> <0131>
+<11> <0237>
+<12> <0060>
+<13> <00B4>
+<14> <02C7>
+<15> <02D8>
+<16> <00AF>
+<17> <02DA>
+<18> <00B8>
+<19> <00DF>
+<1A> <00E6>
+<1B> <0153>
+<1C> <00F8>
+<1D> <00C6>
+<1E> <0152>
+<1F> <00D8>
+<20> <2423>
+<27> <2019>
+<60> <2018>
+<7F> <00A8>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+%%EndResource
+%%EOF
+ }\endgroup
+ \expandafter\edef\csname cmapOT1TT\endcsname#1{%
+ \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}%
+ }%
+\fi\fi
+
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor, #5 is the CMap
+% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass
+% empty to omit).
+\def\setfont#1#2#3#4#5{%
+ \font#1=\fontprefix#2#3 scaled #4
+ \csname cmap#5\endcsname#1%
+}
+% This is what gets called when #5 of \setfont is empty.
+\let\cmap\gobble
+% emacs-page end of cmaps
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx} %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Definitions for a main text size of 11pt. This is the default in
+% Texinfo.
+%
+\def\definetextfontsizexi{%
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1095}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}{OT1}
+\setfont\deftt\ttshape{10}{\magstep1}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}{OT1}
+\setfont\chapit\itbshape{10}{\magstep3}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep3}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT}
+\setfont\chapsf\sfbshape{17}{1000}{OT1}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}{OT1}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+\def\chapecsize{1728}
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}{OT1}
+\setfont\secit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep2}{OT1}
+\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\secsf\sfbshape{12}{\magstep1}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}{OT1}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+\def\sececsize{1440}
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1}
+\setfont\ssecit\itbshape{10}{1315}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1315}{OT1}
+\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}{OT1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+\def\ssececsize{1200}
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}{OT1}
+\setfont\reducedtt\ttshape{10}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{1000}{OT1}
+\setfont\reducedit\itshape{10}{1000}{OT1IT}
+\setfont\reducedsl\slshape{10}{1000}{OT1}
+\setfont\reducedsf\sfshape{10}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{1000}{OT1}
+\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+\def\reducedecsize{1000}
+
+% reset the current fonts
+\textfonts
+\rm
+} % end of 11pt text font size definitions
+
+
+% Definitions to make the main text be 10pt Computer Modern, with
+% section, chapter, etc., sizes following suit. This is for the GNU
+% Press printing of the Emacs 22 manual. Maybe other manuals in the
+% future. Used with @smallbook, which sets the leading to 12pt.
+%
+\def\definetextfontsizex{%
+% Text fonts (10pt).
+\def\textnominalsize{10pt}
+\edef\mainmagstep{1000}
+\setfont\textrm\rmshape{10}{\mainmagstep}{OT1}
+\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT}
+\setfont\textbf\bfshape{10}{\mainmagstep}{OT1}
+\setfont\textit\itshape{10}{\mainmagstep}{OT1IT}
+\setfont\textsl\slshape{10}{\mainmagstep}{OT1}
+\setfont\textsf\sfshape{10}{\mainmagstep}{OT1}
+\setfont\textsc\scshape{10}{\mainmagstep}{OT1}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+\def\textecsize{1000}
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstephalf}{OT1}
+\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT}
+\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}{OT1}
+\setfont\smalltt\ttshape{9}{1000}{OT1TT}
+\setfont\smallbf\bfshape{10}{900}{OT1}
+\setfont\smallit\itshape{9}{1000}{OT1IT}
+\setfont\smallsl\slshape{9}{1000}{OT1}
+\setfont\smallsf\sfshape{9}{1000}{OT1}
+\setfont\smallsc\scshape{10}{900}{OT1}
+\setfont\smallttsl\ttslshape{10}{900}{OT1TT}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+\def\smallecsize{0900}
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}{OT1}
+\setfont\smallertt\ttshape{8}{1000}{OT1TT}
+\setfont\smallerbf\bfshape{10}{800}{OT1}
+\setfont\smallerit\itshape{8}{1000}{OT1IT}
+\setfont\smallersl\slshape{8}{1000}{OT1}
+\setfont\smallersf\sfshape{8}{1000}{OT1}
+\setfont\smallersc\scshape{10}{800}{OT1}
+\setfont\smallerttsl\ttslshape{10}{800}{OT1TT}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+\def\smallerecsize{0800}
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}{OT1}
+\setfont\titleit\itbshape{10}{\magstep4}{OT1IT}
+\setfont\titlesl\slbshape{10}{\magstep4}{OT1}
+\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT}
+\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT}
+\setfont\titlesf\sfbshape{17}{\magstep1}{OT1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}{OT1}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\titleecsize{2074}
+
+% Chapter fonts (14.4pt).
+\def\chapnominalsize{14pt}
+\setfont\chaprm\rmbshape{12}{\magstep1}{OT1}
+\setfont\chapit\itbshape{10}{\magstep2}{OT1IT}
+\setfont\chapsl\slbshape{10}{\magstep2}{OT1}
+\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT}
+\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT}
+\setfont\chapsf\sfbshape{12}{\magstep1}{OT1}
+\let\chapbf\chaprm
+\setfont\chapsc\scbshape{10}{\magstep2}{OT1}
+\font\chapi=cmmi12 scaled \magstep1
+\font\chapsy=cmsy10 scaled \magstep2
+\def\chapecsize{1440}
+
+% Section fonts (12pt).
+\def\secnominalsize{12pt}
+\setfont\secrm\rmbshape{12}{1000}{OT1}
+\setfont\secit\itbshape{10}{\magstep1}{OT1IT}
+\setfont\secsl\slbshape{10}{\magstep1}{OT1}
+\setfont\sectt\ttbshape{12}{1000}{OT1TT}
+\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT}
+\setfont\secsf\sfbshape{12}{1000}{OT1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep1}{OT1}
+\font\seci=cmmi12
+\font\secsy=cmsy10 scaled \magstep1
+\def\sececsize{1200}
+
+% Subsection fonts (10pt).
+\def\ssecnominalsize{10pt}
+\setfont\ssecrm\rmbshape{10}{1000}{OT1}
+\setfont\ssecit\itbshape{10}{1000}{OT1IT}
+\setfont\ssecsl\slbshape{10}{1000}{OT1}
+\setfont\ssectt\ttbshape{10}{1000}{OT1TT}
+\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT}
+\setfont\ssecsf\sfbshape{10}{1000}{OT1}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1000}{OT1}
+\font\sseci=cmmi10
+\font\ssecsy=cmsy10
+\def\ssececsize{1000}
+
+% Reduced fonts for @acro in text (9pt).
+\def\reducednominalsize{9pt}
+\setfont\reducedrm\rmshape{9}{1000}{OT1}
+\setfont\reducedtt\ttshape{9}{1000}{OT1TT}
+\setfont\reducedbf\bfshape{10}{900}{OT1}
+\setfont\reducedit\itshape{9}{1000}{OT1IT}
+\setfont\reducedsl\slshape{9}{1000}{OT1}
+\setfont\reducedsf\sfshape{9}{1000}{OT1}
+\setfont\reducedsc\scshape{10}{900}{OT1}
+\setfont\reducedttsl\ttslshape{10}{900}{OT1TT}
+\font\reducedi=cmmi9
+\font\reducedsy=cmsy9
+\def\reducedecsize{0900}
+
+% reduce space between paragraphs
+\divide\parskip by 2
+
+% reset the current fonts
+\textfonts
+\rm
+} % end of 10pt text font size definitions
+
+
+% We provide the user-level command
+% @fonttextsize 10
+% (or 11) to redefine the text font size. pt is assumed.
+%
+\def\xword{10}
+\def\xiword{11}
+%
+\parseargdef\fonttextsize{%
+ \def\textsizearg{#1}%
+ \wlog{doing @fonttextsize \textsizearg}%
+ %
+ % Set \globaldefs so that documents can use this inside @tex, since
+ % makeinfo 4.8 does not support it, but we need it nonetheless.
+ %
+ \begingroup \globaldefs=1
+ \ifx\textsizearg\xword \definetextfontsizex
+ \else \ifx\textsizearg\xiword \definetextfontsizexi
+ \else
+ \errhelp=\EMsimple
+ \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'}
+ \fi\fi
+ \endgroup
+}
+
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families. Since
+% texinfo doesn't allow for producing subscripts and superscripts except
+% in the main text, we don't bother to reset \scriptfont and
+% \scriptscriptfont (which would also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+ \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
+ \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
+ \textfont\ttfam=\tentt \textfont\sffam=\tensf
+}
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE. We do this because \STYLE needs to also set the
+% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower). These relative commands are used in
+% the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+\def\textfonts{%
+ \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+ \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+ \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
+ \let\tenttsl=\textttsl
+ \def\curfontsize{text}%
+ \def\lsize{reduced}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+ \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+ \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+ \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+ \let\tenttsl=\titlettsl
+ \def\curfontsize{title}%
+ \def\lsize{chap}\def\lllsize{subsec}%
+ \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts\rmisbold #1}}
+\def\chapfonts{%
+ \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+ \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+ \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
+ \let\tenttsl=\chapttsl
+ \def\curfontsize{chap}%
+ \def\lsize{sec}\def\lllsize{text}%
+ \resetmathfonts \setleading{19pt}}
+\def\secfonts{%
+ \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+ \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+ \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
+ \let\tenttsl=\secttsl
+ \def\curfontsize{sec}%
+ \def\lsize{subsec}\def\lllsize{reduced}%
+ \resetmathfonts \setleading{16pt}}
+\def\subsecfonts{%
+ \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+ \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+ \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
+ \let\tenttsl=\ssecttsl
+ \def\curfontsize{ssec}%
+ \def\lsize{text}\def\lllsize{small}%
+ \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+ \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+ \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+ \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+ \let\tenttsl=\reducedttsl
+ \def\curfontsize{reduced}%
+ \def\lsize{small}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+ \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+ \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+ \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+ \let\tenttsl=\smallttsl
+ \def\curfontsize{small}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+ \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+ \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+ \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+ \let\tenttsl=\smallerttsl
+ \def\curfontsize{smaller}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{9.5pt}}
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}{OT1}
+\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}{OT1}
+\setfont\shortconttt\ttshape{12}{1000}{OT1TT}
+
+% Define these just so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+% 8.5x11=86 smallbook=72 a4=90 a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+% 8.5x11=90+ smallbook=80 a4=90+ a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt. So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+% 8.5x11=71 smallbook=60 a4=75 a5=58
+% --karl, 24jan03.
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\definetextfontsizexi
+
+
+\message{markup,}
+
+% Check if we are currently using a typewriter font. Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will
+% define and register \INITMACRO to be called on markup style changes.
+% \INITMACRO can check \currentmarkupstyle for the innermost
+% style and the set of \ifmarkupSTYLE switches for all styles
+% currently in effect.
+\newif\ifmarkupvar
+\newif\ifmarkupsamp
+\newif\ifmarkupkey
+%\newif\ifmarkupfile % @file == @samp.
+%\newif\ifmarkupoption % @option == @samp.
+\newif\ifmarkupcode
+\newif\ifmarkupkbd
+%\newif\ifmarkupenv % @env == @code.
+%\newif\ifmarkupcommand % @command == @code.
+\newif\ifmarkuptex % @tex (and part of @math, for now).
+\newif\ifmarkupexample
+\newif\ifmarkupverb
+\newif\ifmarkupverbatim
+
+\let\currentmarkupstyle\empty
+
+\def\setupmarkupstyle#1{%
+ \csname markup#1true\endcsname
+ \def\currentmarkupstyle{#1}%
+ \markupstylesetup
+}
+
+\let\markupstylesetup\empty
+
+\def\defmarkupstylesetup#1{%
+ \expandafter\def\expandafter\markupstylesetup
+ \expandafter{\markupstylesetup #1}%
+ \def#1%
+}
+
+% Markup style setup for left and right quotes.
+\defmarkupstylesetup\markupsetuplq{%
+ \expandafter\let\expandafter \temp \csname markupsetuplq\currentmarkupstyle\endcsname
+ \ifx\temp\relax \markupsetuplqdefault \else \temp \fi
+}
+
+\defmarkupstylesetup\markupsetuprq{%
+ \expandafter\let\expandafter \temp \csname markupsetuprq\currentmarkupstyle\endcsname
+ \ifx\temp\relax \markupsetuprqdefault \else \temp \fi
+}
+
+{
+\catcode`\'=\active
+\catcode`\`=\active
+
+\gdef\markupsetuplqdefault{\let`\lq}
+\gdef\markupsetuprqdefault{\let'\rq}
+
+\gdef\markupsetcodequoteleft{\let`\codequoteleft}
+\gdef\markupsetcodequoteright{\let'\codequoteright}
+
+\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft}
+}
+
+\let\markupsetuplqcode \markupsetcodequoteleft
+\let\markupsetuprqcode \markupsetcodequoteright
+\let\markupsetuplqexample \markupsetcodequoteleft
+\let\markupsetuprqexample \markupsetcodequoteright
+\let\markupsetuplqverb \markupsetcodequoteleft
+\let\markupsetuprqverb \markupsetcodequoteright
+\let\markupsetuplqverbatim \markupsetcodequoteleft
+\let\markupsetuprqverbatim \markupsetcodequoteright
+
+\let\markupsetuplqsamp \markupsetnoligaturesquoteleft
+\let\markupsetuplqkbd \markupsetnoligaturesquoteleft
+
+% Allow an option to not replace quotes with a regular directed right
+% quote/apostrophe (char 0x27), but instead use the undirected quote
+% from cmtt (char 0x0d). The undirected quote is ugly, so don't make it
+% the default, but it works for pasting with more pdf viewers (at least
+% evince), the lilypond developers report. xpdf does work with the
+% regular 0x27.
+%
+\def\codequoteright{%
+ \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax
+ \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax
+ '%
+ \else \char'15 \fi
+ \else \char'15 \fi
+}
+%
+% and a similar option for the left quote char vs. a grave accent.
+% Modern fonts display ASCII 0x60 as a grave accent, so some people like
+% the code environments to do likewise.
+%
+\def\codequoteleft{%
+ \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax
+ \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax
+ % [Knuth] pp. 380,381,391
+ % \relax disables Spanish ligatures ?` and !` of \tt font.
+ \relax`%
+ \else \char'22 \fi
+ \else \char'22 \fi
+}
+
+% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font.
+\def\noligaturesquoteleft{\relax\lq}
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else
+ \ptexslash\fi\fi\fi}
+\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally uses \ttsl.
+% @var is set to this for defun arguments.
+\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
+
+% @cite is like \smartslanted except unconditionally use \sl. We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\def\var#1{{\setupmarkupstyle{var}\smartslanted{#1}}}
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}} % roman font
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+% @b, explicit bold. Also @strong.
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\catcode`@=11
+ \def\plainfrenchspacing{%
+ \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
+ \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m
+ \def\endofsentencespacefactor{1000}% for @. and friends
+ }
+ \def\plainnonfrenchspacing{%
+ \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+ \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+ \def\endofsentencespacefactor{3000}% for @. and friends
+ }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+% @t, explicit typewriter.
+\def\t#1{%
+ {\tt \rawbackslash \plainfrenchspacing #1}%
+ \null
+}
+
+% @samp.
+\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}}
+
+% definition of @key that produces a lozenge. Doesn't adjust to text size.
+%\setfont\keyrm\rmshape{8}{1000}{OT1}
+%\font\keysy=cmsy9
+%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+% \vbox{\hrule\kern-0.4pt
+% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+% \kern-0.4pt\hrule}%
+% \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+
+% definition of @key with no lozenge. If the current font is already
+% monospace, don't change it; that way, we respect @kbdinputstyle. But
+% if it isn't monospace, then use \tt.
+%
+\def\key#1{{\setupmarkupstyle{key}%
+ \nohyphenation
+ \ifmonospace\else\tt\fi
+ #1}\null}
+
+% ctrl is no longer a Texinfo command.
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+% @file, @option are the same as @samp.
+\let\file=\samp
+\let\option=\samp
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \rawbackslash
+ \plainfrenchspacing
+ #1%
+ }%
+ \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash.
+% -- rms.
+{
+ \catcode`\-=\active \catcode`\_=\active
+ \catcode`\'=\active \catcode`\`=\active
+ \global\let'=\rq \global\let`=\lq % default definitions
+ %
+ \global\def\code{\begingroup
+ \setupmarkupstyle{code}%
+ % The following should really be moved into \setupmarkupstyle handlers.
+ \catcode\dashChar=\active \catcode\underChar=\active
+ \ifallowcodebreaks
+ \let-\codedash
+ \let_\codeunder
+ \else
+ \let-\realdash
+ \let_\realunder
+ \fi
+ \codex
+ }
+}
+
+\def\realdash{-}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{%
+ % this is all so @math{@code{var_name}+1} can work. In math mode, _
+ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+ % will therefore expand the active definition of _, which is us
+ % (inside @code that is), therefore an endless loop.
+ \ifusingtt{\ifmmode
+ \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+ \else\normalunderscore \fi
+ \discretionary{}{}{}}%
+ {\_}%
+}
+\def\codex #1{\tclose{#1}\endgroup}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__. This is undesirable in
+% some manuals, especially if they don't have long identifiers in
+% general. @allowcodebreaks provides a way to control this.
+%
+\newif\ifallowcodebreaks \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\keywordtrue
+ \allowcodebreakstrue
+ \else\ifx\txiarg\keywordfalse
+ \allowcodebreaksfalse
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @allowcodebreaks option `\txiarg'}%
+ \fi\fi
+}
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}}
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+% `example' (@kbd uses ttsl only inside of @example and friends),
+% or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\worddistinct
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+ \else\ifx\txiarg\wordexample
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+ \else\ifx\txiarg\wordcode
+ \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @kbdinputstyle option `\txiarg'}%
+ \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct'.
+\kbdinputstyle distinct
+
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi
+\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi}
+
+% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
+\let\indicateurl=\code
+\let\env=\code
+\let\command=\code
+
+% @clicksequence{File @click{} Open ...}
+\def\clicksequence#1{\begingroup #1\endgroup}
+
+% @clickstyle @arrow (by default)
+\parseargdef\clickstyle{\def\click{#1}}
+\def\click{\arrow}
+
+% @uref (abbreviation for `urlref') takes an optional (comma-separated)
+% second argument specifying the text to display and an optional third
+% arg as text to display instead of (rather than in addition to) the url
+% itself. First (mandatory) arg is the url. Perhaps eventually put in
+% a hypertex \special here.
+%
+\def\uref#1{\douref #1,,,\finish}
+\def\douref#1,#2,#3,#4\finish{\begingroup
+ \unsepspaces
+ \pdfurl{#1}%
+ \setbox0 = \hbox{\ignorespaces #3}%
+ \ifdim\wd0 > 0pt
+ \unhbox0 % third arg given, show only that
+ \else
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \ifpdf
+ \unhbox0 % PDF: 2nd arg given, show only it
+ \else
+ \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
+ \fi
+ \else
+ \code{#1}% only url given, so show it
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+ \def\email#1{\doemail#1,,\finish}
+ \def\doemail#1,#2,#3\finish{\begingroup
+ \unsepspaces
+ \pdfurl{mailto:#1}%
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+ \endlink
+ \endgroup}
+\else
+ \let\email=\uref
+\fi
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+% @l was never documented to mean ``switch to the Lisp font'',
+% and it is not used as such in any manual I can find. We need it for
+% Polish suppressed-l. --karl, 22sep96.
+%\def\l#1{{\li #1}\null}
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+%
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+ {\selectfonts\lsize #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+%
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+ {\plainfrenchspacing #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+
+\message{glyphs,}
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% Since these characters are used in examples, they should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+%
+\def\point{$\star$}
+\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}}
+\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \reducedsf error\kern-1.5pt}
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{%
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+%
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+%
+% Although only regular is the truly official Euro symbol, we ignore
+% that. The Euro is designed to be slightly taller than the regular
+% font height.
+%
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+%
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+%
+% Also doesn't work in math. Do we need to do math with euro symbols?
+% Hope not.
+%
+%
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+ % We set the font at each command, rather than predefining it in
+ % \textfonts and the other font-switching commands, so that
+ % installations which never need the symbol don't have to have the
+ % font installed.
+ %
+ % There is only one designed size (nominal 10pt), so we always scale
+ % that to the current nominal size.
+ %
+ % By the way, simply using "at 1em" works for cmr10 and the like, but
+ % does not work for cmbx10 and other extended/shrunken fonts.
+ %
+ \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+ %
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+ \else
+ % regular:
+ \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+ \fi
+ \thiseurofont
+}
+
+% Glyphs from the EC fonts. We don't use \let for the aliases, because
+% sometimes we redefine the original macro, and the alias should reflect
+% the redefinition.
+%
+% Use LaTeX names for the Icelandic letters.
+\def\DH{{\ecfont \char"D0}} % Eth
+\def\dh{{\ecfont \char"F0}} % eth
+\def\TH{{\ecfont \char"DE}} % Thorn
+\def\th{{\ecfont \char"FE}} % thorn
+%
+\def\guillemetleft{{\ecfont \char"13}}
+\def\guillemotleft{\guillemetleft}
+\def\guillemetright{{\ecfont \char"14}}
+\def\guillemotright{\guillemetright}
+\def\guilsinglleft{{\ecfont \char"0E}}
+\def\guilsinglright{{\ecfont \char"0F}}
+\def\quotedblbase{{\ecfont \char"12}}
+\def\quotesinglbase{{\ecfont \char"0D}}
+%
+% This positioning is not perfect (see the ogonek LaTeX package), but
+% we have the precomposed glyphs for the most common cases. We put the
+% tests to use those glyphs in the single \ogonek macro so we have fewer
+% dummy definitions to worry about for index entries, etc.
+%
+% ogonek is also used with other letters in Lithuanian (IOU), but using
+% the precomposed glyphs for those is not so easy since they aren't in
+% the same EC font.
+\def\ogonek#1{{%
+ \def\temp{#1}%
+ \ifx\temp\macrocharA\Aogonek
+ \else\ifx\temp\macrochara\aogonek
+ \else\ifx\temp\macrocharE\Eogonek
+ \else\ifx\temp\macrochare\eogonek
+ \else
+ \ecfont \setbox0=\hbox{#1}%
+ \ifdim\ht0=1ex\accent"0C #1%
+ \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}%
+ \fi
+ \fi\fi\fi\fi
+ }%
+}
+\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A}
+\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a}
+\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E}
+\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e}
+%
+% Use the ec* fonts (cm-super in outline format) for non-CM glyphs.
+\def\ecfont{%
+ % We can't distinguish serif/sans and italic/slanted, but this
+ % is used for crude hacks anyway (like adding French and German
+ % quotes to documents typeset with CM, where we lose kerning), so
+ % hopefully nobody will notice/care.
+ \edef\ecsize{\csname\curfontsize ecsize\endcsname}%
+ \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}%
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize
+ \else
+ % regular:
+ \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize
+ \fi
+ \thisecfont
+}
+
+% @registeredsymbol - R in a circle. The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+ $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+ \hfil\crcr\Orb}}%
+ }$%
+}
+
+% @textdegree - the normal degrees sign.
+%
+\def\textdegree{$^\circ$}
+
+% Laurent Siebenmann reports \Orb undefined with:
+% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38
+% so we'll define it if necessary.
+%
+\ifx\Orb\undefined
+\def\Orb{\mathhexbox20D}
+\fi
+
+% Quotes.
+\chardef\quotedblleft="5C
+\chardef\quotedblright=`\"
+\chardef\quoteleft=`\`
+\chardef\quoteright=`\'
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+ % Open one extra group, as we want to close it in the middle of \Etitlepage.
+ \begingroup
+ \parindent=0pt \textfonts
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \let\page = \oldpage
+ \page
+ \null
+ }%
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ %
+ % Need this before the \...aftertitlepage checks so that if they are
+ % in effect the toc pages will come out with page numbers.
+ \HEADINGSon
+ %
+ % If they want short, they certainly want long too.
+ \ifsetshortcontentsaftertitlepage
+ \shortcontents
+ \contents
+ \global\let\shortcontents = \relax
+ \global\let\contents = \relax
+ \fi
+ %
+ \ifsetcontentsaftertitlepage
+ \contents
+ \global\let\contents = \relax
+ \global\let\shortcontents = \relax
+ \fi
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\parseargdef\title{%
+ \checkenv\titlepage
+ \leftline{\titlefonts\rmisbold #1}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+ \checkenv\titlepage
+ {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+ \def\temp{\quotation}%
+ \ifx\thisenv\temp
+ \def\quotationauthor{#1}% printed in \Equotation.
+ \else
+ \checkenv\titlepage
+ \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+ {\secfonts\rmisbold \leftline{#1}}%
+ \fi
+}
+
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks\evenheadline % headline on even pages
+\newtoks\oddheadline % headline on odd pages
+\newtoks\evenfootline % footline on even pages
+\newtoks\oddfootline % footline on odd pages
+
+% Now make TeX use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+ \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+ %
+ % Leave some space for the footline. Hopefully ok to assume
+ % @evenfooting will not be used by itself.
+ \global\advance\pageheight by -12pt
+ \global\advance\vsize by -12pt
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+
+% @evenheadingmarks top \thischapter <- chapter at the top of a page
+% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page
+%
+% The same set of arguments for:
+%
+% @oddheadingmarks
+% @evenfootingmarks
+% @oddfootingmarks
+% @everyheadingmarks
+% @everyfootingmarks
+
+\def\evenheadingmarks{\headingmarks{even}{heading}}
+\def\oddheadingmarks{\headingmarks{odd}{heading}}
+\def\evenfootingmarks{\headingmarks{even}{footing}}
+\def\oddfootingmarks{\headingmarks{odd}{footing}}
+\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1}
+ \headingmarks{odd}{heading}{#1} }
+\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1}
+ \headingmarks{odd}{footing}{#1} }
+% #1 = even/odd, #2 = heading/footing, #3 = top/bottom.
+\def\headingmarks#1#2#3 {%
+ \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname
+ \global\expandafter\let\csname get#1#2marks\endcsname \temp
+}
+
+\everyheadingmarks bottom
+\everyfootingmarks bottom
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{%
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\undefined
+\def\today{%
+ \number\day\space
+ \ifcase\month
+ \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+ \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+ \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+ \fi
+ \space\number\year}
+\fi
+
+% @settitle line... specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
+
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemindicate{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. However, if
+ % what follows is an environment such as @example, there will be no
+ % \parskip glue; then the negative vskip we just inserted would
+ % cause the example and the item to crash together. So we use this
+ % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+ % \parskip glue after all. Section titles are handled this way also.
+ %
+ \penalty 10001
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line.
+ \noindent
+ % Do this with kerns and \unhbox so that if there is a footnote in
+ % the item text, it can migrate to the main vertical list and
+ % eventually be printed.
+ \nobreak\kern-\tableindent
+ \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+ \unhbox0
+ \nobreak\kern\dimen0
+ \endgroup
+ \itemxneedsnegativevskiptrue
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+ \let\itemindex\gobble
+ \tablecheck{table}%
+}
+\envdef\ftable{%
+ \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+ \tablecheck{ftable}%
+}
+\envdef\vtable{%
+ \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+ \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+ \ifnum \the\catcode`\^^M=\active
+ \endgroup
+ \errmessage{This command won't work in this context; perhaps the problem is
+ that we are \inenvironment\thisenv}%
+ \def\next{\doignore{#1}}%
+ \else
+ \let\next\tablex
+ \fi
+ \next
+}
+\def\tablex#1{%
+ \def\itemindicate{#1}%
+ \parsearg\tabley
+}
+\def\tabley#1{%
+ {%
+ \makevalueexpandable
+ \edef\temp{\noexpand\tablez #1\space\space\space}%
+ \expandafter
+ }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+ \aboveenvbreak
+ \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+ \ifnum 0#2>0 \tableindent=#2\mil \fi
+ \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+ \itemmax=\tableindent
+ \advance \itemmax by -\itemmargin
+ \advance \leftskip by \tableindent
+ \exdentamount=\tableindent
+ \parindent = 0pt
+ \parskip = \smallskipamount
+ \ifdim \parskip=0pt \parskip=2pt \fi
+ \let\item = \internalBitem
+ \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+ \aboveenvbreak
+ \itemmax=\itemindent
+ \advance\itemmax by -\itemmargin
+ \advance\leftskip by \itemindent
+ \exdentamount=\itemindent
+ \parindent=0pt
+ \parskip=\smallskipamount
+ \ifdim\parskip=0pt \parskip=2pt \fi
+ %
+ % Try typesetting the item mark that if the document erroneously says
+ % something like @itemize @samp (intending @table), there's an error
+ % right away at the @itemize. It's not the best error message in the
+ % world, but it's better than leaving it to the @item. This means if
+ % the user wants an empty mark, they have to say @w{} not just @w.
+ \def\itemcontents{#1}%
+ \setbox0 = \hbox{\itemcontents}%
+ %
+ % @itemize with no arg is equivalent to @itemize @bullet.
+ \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+ %
+ \let\item=\itemizeitem
+}
+
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+ \advance\itemno by 1 % for enumerations
+ {\let\par=\endgraf \smallbreak}% reasonable place to break
+ {%
+ % If the document has an @itemize directly after a section title, a
+ % \nobreak will be last on the list, and \sectionheading will have
+ % done a \vskip-\parskip. In that case, we don't want to zero
+ % parskip, or the item text will crash with the heading. On the
+ % other hand, when there is normal text preceding the item (as there
+ % usually is), we do want to zero parskip, or there would be too much
+ % space. In that case, we won't have a \nobreak before. At least
+ % that's the theory.
+ \ifnum\lastpenalty<10000 \parskip=0in \fi
+ \noindent
+ \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+ %
+ \vadjust{\penalty 1200}}% not good to break after first line of item.
+ \flushcr
+}
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\envparseargdef\enumerate{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call \doitemize, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \doitemize{#1.}\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @columnfractions .25 .3 .45
+% @item ...
+%
+% Numbers following @columnfractions are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+% to baseline.
+% 0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+% Macros used to set up halign preamble:
+%
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1. We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+ \global\advance\colcount by 1
+ \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+ \setuptable
+}
+
+\newcount\colcount
+\def\setuptable#1{%
+ \def\firstarg{#1}%
+ \ifx\firstarg\xendsetuptable
+ \let\go = \relax
+ \else
+ \ifx\firstarg\xcolumnfractions
+ \global\setpercenttrue
+ \else
+ \ifsetpercent
+ \let\go\pickupwholefraction
+ \else
+ \global\advance\colcount by 1
+ \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+ % separator; typically that is always in the input, anyway.
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi
+ \fi
+ \ifx\go\pickupwholefraction
+ % Put the argument back for the \pickupwholefraction call, so
+ % we'll always have a period there to be parsed.
+ \def\go{\pickupwholefraction#1}%
+ \else
+ \let\go = \setuptable
+ \fi%
+ \fi
+ \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold.
+% Assignments have to be global since we are inside the implicit group
+% of an alignment entry. \everycr resets \everytab so we don't have to
+% undo it ourselves.
+\def\headitemfont{\b}% for people to use in the template row; not changeable
+\def\headitem{%
+ \checkenv\multitable
+ \crcr
+ \global\everytab={\bf}% can't use \headitemfont since the parsing differs
+ \the\everytab % for the first item
+}%
+%
+% A \tab used to include \hskip1sp. But then the space in a template
+% line is not enough. That is bad. So let's go back to just `&' until
+% we again encounter the problem the 1sp was intended to solve.
+% --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab % insert after every tab.
+%
+\envdef\multitable{%
+ \vskip\parskip
+ \startsavinginserts
+ %
+ % @item within a multitable starts a normal row.
+ % We use \def instead of \let so that if one of the multitable entries
+ % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+ % \endtemplate) expanding \doitemize.
+ \def\item{\crcr}%
+ %
+ \tolerance=9500
+ \hbadness=9500
+ \setmultitablespacing
+ \parskip=\multitableparskip
+ \parindent=\multitableparindent
+ \overfullrule=0pt
+ \global\colcount=0
+ %
+ \everycr = {%
+ \noalign{%
+ \global\everytab={}%
+ \global\colcount=0 % Reset the column counter.
+ % Check for saved footnotes, etc.
+ \checkinserts
+ % Keeps underfull box messages off when table breaks over pages.
+ %\filbreak
+ % Maybe so, but it also creates really weird page breaks when the
+ % table breaks over pages. Wouldn't \vfil be better? Wait until the
+ % problem manifests itself, so it can be fixed for real --karl.
+ }%
+ }%
+ %
+ \parsearg\domultitable
+}
+\def\domultitable#1{%
+ % To parse everything between @multitable and @item:
+ \setuptable#1 \endsetuptable
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+ \halign\bgroup &%
+ \global\advance\colcount by 1
+ \multistrut
+ \vtop{%
+ % Use the current \colcount to find the correct column width:
+ \hsize=\expandafter\csname col\the\colcount\endcsname
+ %
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \multitablecolspace to all columns after
+ % the first one.
+ %
+ % If a template has been used, we will add \multitablecolspace
+ % to the width of each template entry.
+ %
+ % If the user has set preamble in terms of percent of \hsize we will
+ % use that dimension as the width of the column, and the \leftskip
+ % will keep entries from bumping into each other. Table will start at
+ % left margin and final column will justify at right margin.
+ %
+ % Make sure we don't inherit \rightskip from the outer environment.
+ \rightskip=0pt
+ \ifnum\colcount=1
+ % The first column will be indented with the surrounding text.
+ \advance\hsize by\leftskip
+ \else
+ \ifsetpercent \else
+ % If user has not set preamble in terms of percent of \hsize
+ % we will advance \hsize by \multitablecolspace.
+ \advance\hsize by \multitablecolspace
+ \fi
+ % In either case we will make \leftskip=\multitablecolspace:
+ \leftskip=\multitablecolspace
+ \fi
+ % Ignoring space at the beginning and end avoids an occasional spurious
+ % blank line, when TeX decides to break the line at the space before the
+ % box from the multistrut, so the strut ends up on a line by itself.
+ % For example:
+ % @multitable @columnfractions .11 .89
+ % @item @code{#}
+ % @tab Legal holiday which is valid in major parts of the whole country.
+ % Is automatically provided with highlighting sequences respectively
+ % marking characters.
+ \noindent\ignorespaces##\unskip\multistrut
+ }\cr
+}
+\def\Emultitable{%
+ \crcr
+ \egroup % end the \halign
+ \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+ \def\multistrut{\strut}% just use the standard line spacing
+ %
+ % Compute \multitablelinespace (if not defined by user) for use in
+ % \multitableparskip calculation. We used define \multistrut based on
+ % this, but (ironically) that caused the spacing to be off.
+ % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%% If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed. They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested. But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+ \expandafter\let\csname #1\endcsname = \relax
+ \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+ % Scan in ``verbatim'' mode:
+ \obeylines
+ \catcode`\@ = \other
+ \catcode`\{ = \other
+ \catcode`\} = \other
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \spaceisspace
+ %
+ % Count number of #1's that we've seen.
+ \doignorecount = 0
+ %
+ % Swallow text until we reach the matching `@end #1'.
+ \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+ \obeylines %
+ %
+ \gdef\dodoignore#1{%
+ % #1 contains the command name as a string, e.g., `ifinfo'.
+ %
+ % Define a command to find the next `@end #1'.
+ \long\def\doignoretext##1^^M@end #1{%
+ \doignoretextyyy##1^^M@#1\_STOP_}%
+ %
+ % And this command to find another #1 command, at the beginning of a
+ % line. (Otherwise, we would consider a line `@c @ifset', for
+ % example, to count as an @ifset for nesting.)
+ \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+ %
+ % And now expand that command.
+ \doignoretext ^^M%
+ }%
+}
+
+\def\doignoreyyy#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty % Nothing found.
+ \let\next\doignoretextzzz
+ \else % Found a nested condition, ...
+ \advance\doignorecount by 1
+ \let\next\doignoretextyyy % ..., look for another.
+ % If we're here, #1 ends with ^^M\ifinfo (for example).
+ \fi
+ \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+ \ifnum\doignorecount = 0 % We have just found the outermost @end.
+ \let\next\enddoignore
+ \else % Still inside a nested condition.
+ \advance\doignorecount by -1
+ \let\next\doignoretext % Look for the next @end.
+ \fi
+ \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+ % Ignore anything after the last `@end #1'; this matters in verbatim
+ % environments, where otherwise the newline after an ignored conditional
+ % would result in a blank line in the output.
+ \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ {%
+ \makevalueexpandable
+ \def\temp{#2}%
+ \edef\next{\gdef\makecsname{SET#1}}%
+ \ifx\temp\empty
+ \next{}%
+ \else
+ \setzzz#2\endsetzzz
+ \fi
+ }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+ {%
+ \makevalueexpandable
+ \global\expandafter\let\csname SET#1\endcsname=\relax
+ }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+ \catcode`\- = \active \catcode`\_ = \active
+ %
+ \gdef\makevalueexpandable{%
+ \let\value = \expandablevalue
+ % We don't want these characters active, ...
+ \catcode`\-=\other \catcode`\_=\other
+ % ..., but we might end up with active ones in the argument if
+ % we're called from @code, as @code{@value{foo-bar_}}, though.
+ % So \let them to their normal equivalents.
+ \let-\realdash \let_\normalunderscore
+ }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file. This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+%
+\def\expandablevalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ {[No value for ``#1'']}%
+ \message{Variable `#1', used in @value, is not set.}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get special treatment of `@end ifset,' call \makeond and the redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+ {%
+ \makevalueexpandable
+ \let\next=\empty
+ \expandafter\ifx\csname SET#2\endcsname\relax
+ #1% If not set, redefine \next.
+ \fi
+ \expandafter
+ }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+%
+\def\newindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{% % Define @#1index
+ \noexpand\doindex{#1}}
+}
+
+% @defindex foo == \newindex{foo}
+%
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{%
+ \noexpand\docodeindex{#1}}%
+}
+
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+%
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+ % Only do \closeout if we haven't already done it, else we'll end up
+ % closing the target index.
+ \expandafter \ifx\csname donesynindex#2\endcsname \relax
+ % The \closeout helps reduce unnecessary open files; the limit on the
+ % Acorn RISC OS is a mere 16 files.
+ \expandafter\closeout\csname#2indfile\endcsname
+ \expandafter\let\csname donesynindex#2\endcsname = 1
+ \fi
+ % redefine \fooindfile:
+ \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+ \expandafter\let\csname#2indfile\endcsname=\temp
+ % redefine \fooindex:
+ \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+% Take care of Texinfo commands that can appear in an index entry.
+% Since there are some commands we want to expand, and others we don't,
+% we have to laboriously prevent expansion for those that we don't.
+%
+\def\indexdummies{%
+ \escapechar = `\\ % use backslash in output files.
+ \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+ \def\ {\realbackslash\space }%
+ %
+ % Need these in case \tex is in effect and \{ is a \delimiter again.
+ % But can't use \lbracecmd and \rbracecmd because texindex assumes
+ % braces and backslashes are used only as delimiters.
+ \let\{ = \mylbrace
+ \let\} = \myrbrace
+ %
+ % I don't entirely understand this, but when an index entry is
+ % generated from a macro call, the \endinput which \scanmacro inserts
+ % causes processing to be prematurely terminated. This is,
+ % apparently, because \indexsorttmp is fully expanded, and \endinput
+ % is an expandable command. The redefinition below makes \endinput
+ % disappear altogether for that purpose -- although logging shows that
+ % processing continues to some further point. On the other hand, it
+ % seems \endinput does not hurt in the printed index arg, since that
+ % is still getting written without apparent harm.
+ %
+ % Sample source (mac-idx3.tex, reported by Graham Percival to
+ % help-texinfo, 22may06):
+ % @macro funindex {WORD}
+ % @findex xyz
+ % @end macro
+ % ...
+ % @funindex commtest
+ %
+ % The above is not enough to reproduce the bug, but it gives the flavor.
+ %
+ % Sample whatsit resulting:
+ % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}}
+ %
+ % So:
+ \let\endinput = \empty
+ %
+ % Do the redefinitions.
+ \commondummies
+}
+
+% For the aux and toc files, @ is the escape character. So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files). When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+ \def\@{@@}%
+ \def\ {@ }%
+ \let\{ = \lbraceatcmd
+ \let\} = \rbraceatcmd
+ %
+ % Do the redefinitions.
+ \commondummies
+ \otherbackslash
+}
+
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+ %
+ % \definedummyword defines \#1 as \string\#1\space, thus effectively
+ % preventing its expansion. This is used only for control% words,
+ % not control letters, because the \space would be incorrect for
+ % control characters, but is needed to separate the control word
+ % from whatever follows.
+ %
+ % For control letters, we have \definedummyletter, which omits the
+ % space.
+ %
+ % These can be used both for control words that take an argument and
+ % those that do not. If it is followed by {arg} in the input, then
+ % that will dutifully get written to the index (or wherever).
+ %
+ \def\definedummyword ##1{\def##1{\string##1\space}}%
+ \def\definedummyletter##1{\def##1{\string##1}}%
+ \let\definedummyaccent\definedummyletter
+ %
+ \commondummiesnofonts
+ %
+ \definedummyletter\_%
+ %
+ % Non-English letters.
+ \definedummyword\AA
+ \definedummyword\AE
+ \definedummyword\DH
+ \definedummyword\L
+ \definedummyword\O
+ \definedummyword\OE
+ \definedummyword\TH
+ \definedummyword\aa
+ \definedummyword\ae
+ \definedummyword\dh
+ \definedummyword\exclamdown
+ \definedummyword\l
+ \definedummyword\o
+ \definedummyword\oe
+ \definedummyword\ordf
+ \definedummyword\ordm
+ \definedummyword\questiondown
+ \definedummyword\ss
+ \definedummyword\th
+ %
+ % Although these internal commands shouldn't show up, sometimes they do.
+ \definedummyword\bf
+ \definedummyword\gtr
+ \definedummyword\hat
+ \definedummyword\less
+ \definedummyword\sf
+ \definedummyword\sl
+ \definedummyword\tclose
+ \definedummyword\tt
+ %
+ \definedummyword\LaTeX
+ \definedummyword\TeX
+ %
+ % Assorted special characters.
+ \definedummyword\bullet
+ \definedummyword\comma
+ \definedummyword\copyright
+ \definedummyword\registeredsymbol
+ \definedummyword\dots
+ \definedummyword\enddots
+ \definedummyword\equiv
+ \definedummyword\error
+ \definedummyword\euro
+ \definedummyword\guillemetleft
+ \definedummyword\guillemetright
+ \definedummyword\guilsinglleft
+ \definedummyword\guilsinglright
+ \definedummyword\expansion
+ \definedummyword\minus
+ \definedummyword\ogonek
+ \definedummyword\pounds
+ \definedummyword\point
+ \definedummyword\print
+ \definedummyword\quotedblbase
+ \definedummyword\quotedblleft
+ \definedummyword\quotedblright
+ \definedummyword\quoteleft
+ \definedummyword\quoteright
+ \definedummyword\quotesinglbase
+ \definedummyword\result
+ \definedummyword\textdegree
+ %
+ % We want to disable all macros so that they are not expanded by \write.
+ \macrolist
+ %
+ \normalturnoffactive
+ %
+ % Handle some cases of @value -- where it does not contain any
+ % (non-fully-expandable) commands.
+ \makevalueexpandable
+}
+
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+%
+\def\commondummiesnofonts{%
+ % Control letters and accents.
+ \definedummyletter\!%
+ \definedummyaccent\"%
+ \definedummyaccent\'%
+ \definedummyletter\*%
+ \definedummyaccent\,%
+ \definedummyletter\.%
+ \definedummyletter\/%
+ \definedummyletter\:%
+ \definedummyaccent\=%
+ \definedummyletter\?%
+ \definedummyaccent\^%
+ \definedummyaccent\`%
+ \definedummyaccent\~%
+ \definedummyword\u
+ \definedummyword\v
+ \definedummyword\H
+ \definedummyword\dotaccent
+ \definedummyword\ogonek
+ \definedummyword\ringaccent
+ \definedummyword\tieaccent
+ \definedummyword\ubaraccent
+ \definedummyword\udotaccent
+ \definedummyword\dotless
+ %
+ % Texinfo font commands.
+ \definedummyword\b
+ \definedummyword\i
+ \definedummyword\r
+ \definedummyword\sc
+ \definedummyword\t
+ %
+ % Commands that take arguments.
+ \definedummyword\acronym
+ \definedummyword\cite
+ \definedummyword\code
+ \definedummyword\command
+ \definedummyword\dfn
+ \definedummyword\email
+ \definedummyword\emph
+ \definedummyword\env
+ \definedummyword\file
+ \definedummyword\kbd
+ \definedummyword\key
+ \definedummyword\math
+ \definedummyword\option
+ \definedummyword\pxref
+ \definedummyword\ref
+ \definedummyword\samp
+ \definedummyword\strong
+ \definedummyword\tie
+ \definedummyword\uref
+ \definedummyword\url
+ \definedummyword\var
+ \definedummyword\verb
+ \definedummyword\w
+ \definedummyword\xref
+}
+
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names. It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+ % Accent commands should become @asis.
+ \def\definedummyaccent##1{\let##1\asis}%
+ % We can just ignore other control letters.
+ \def\definedummyletter##1{\let##1\empty}%
+ % Hopefully, all control words can become @asis.
+ \let\definedummyword\definedummyaccent
+ %
+ \commondummiesnofonts
+ %
+ % Don't no-op \tt, since it isn't a user-level command
+ % and is used in the definitions of the active chars like <, >, |, etc.
+ % Likewise with the other plain tex font commands.
+ %\let\tt=\asis
+ %
+ \def\ { }%
+ \def\@{@}%
+ % how to handle braces?
+ \def\_{\normalunderscore}%
+ %
+ % Non-English letters.
+ \def\AA{AA}%
+ \def\AE{AE}%
+ \def\DH{DZZ}%
+ \def\L{L}%
+ \def\OE{OE}%
+ \def\O{O}%
+ \def\TH{ZZZ}%
+ \def\aa{aa}%
+ \def\ae{ae}%
+ \def\dh{dzz}%
+ \def\exclamdown{!}%
+ \def\l{l}%
+ \def\oe{oe}%
+ \def\ordf{a}%
+ \def\ordm{o}%
+ \def\o{o}%
+ \def\questiondown{?}%
+ \def\ss{ss}%
+ \def\th{zzz}%
+ %
+ \def\LaTeX{LaTeX}%
+ \def\TeX{TeX}%
+ %
+ % Assorted special characters.
+ % (The following {} will end up in the sort string, but that's ok.)
+ \def\bullet{bullet}%
+ \def\comma{,}%
+ \def\copyright{copyright}%
+ \def\dots{...}%
+ \def\enddots{...}%
+ \def\equiv{==}%
+ \def\error{error}%
+ \def\euro{euro}%
+ \def\expansion{==>}%
+ \def\guillemetleft{<<}%
+ \def\guillemetright{>>}%
+ \def\guilsinglleft{<}%
+ \def\guilsinglright{>}%
+ \def\minus{-}%
+ \def\point{.}%
+ \def\pounds{pounds}%
+ \def\print{-|}%
+ \def\quotedblbase{"}%
+ \def\quotedblleft{"}%
+ \def\quotedblright{"}%
+ \def\quoteleft{`}%
+ \def\quoteright{'}%
+ \def\quotesinglbase{,}%
+ \def\registeredsymbol{R}%
+ \def\result{=>}%
+ \def\textdegree{o}%
+ %
+ % We need to get rid of all macros, leaving only the arguments (if present).
+ % Of course this is not nearly correct, but it is the best we can do for now.
+ % makeinfo does not expand macros in the argument to @deffn, which ends up
+ % writing an index entry, and texindex isn't prepared for an index sort entry
+ % that starts with \.
+ %
+ % Since macro invocations are followed by braces, we can just redefine them
+ % to take a single TeX argument. The case of a macro invocation that
+ % goes to end-of-line is not handled.
+ %
+ \macrolist
+}
+
+\let\indexbackslash=0 %overridden during \printindex.
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
+
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% Workhorse for all \fooindexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+ \iflinks
+ {%
+ % Store the main index entry text (including the third arg).
+ \toks0 = {#2}%
+ % If third arg is present, precede it with a space.
+ \def\thirdarg{#3}%
+ \ifx\thirdarg\empty \else
+ \toks0 = \expandafter{\the\toks0 \space #3}%
+ \fi
+ %
+ \edef\writeto{\csname#1indfile\endcsname}%
+ %
+ \safewhatsit\dosubindwrite
+ }%
+ \fi
+}
+
+% Write the entry in \toks0 to the index file:
+%
+\def\dosubindwrite{%
+ % Put the index entry in the margin if desired.
+ \ifx\SETmarginindex\relax\else
+ \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+ \fi
+ %
+ % Remember, we are within a group.
+ \indexdummies % Must do this here, since \bf, etc expand at this stage
+ \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
+ % so it will be output as is; and it will print as backslash.
+ %
+ % Process the index entry with all font commands turned off, to
+ % get the string to sort by.
+ {\indexnofonts
+ \edef\temp{\the\toks0}% need full expansion
+ \xdef\indexsorttmp{\temp}%
+ }%
+ %
+ % Set up the complete index entry, with both the sort key and
+ % the original text, including any font commands. We write
+ % three arguments to \entry to the .?? file (four in the
+ % subentry case), texindex reduces to two when writing the .??s
+ % sorted result.
+ \edef\temp{%
+ \write\writeto{%
+ \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
+ }%
+ \temp
+}
+
+% Take care of unwanted page breaks/skips around a whatsit:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again. Otherwise, the whatsit generated by the
+% \write or \pdfdest will make \lastskip zero. The result is that
+% sequences like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode. We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip. \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip. The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+\newskip\whatsitskip
+\newcount\whatsitpenalty
+%
+% ..., ready, GO:
+%
+\def\safewhatsit#1{%
+\ifhmode
+ #1%
+\else
+ % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+ \whatsitskip = \lastskip
+ \edef\lastskipmacro{\the\lastskip}%
+ \whatsitpenalty = \lastpenalty
+ %
+ % If \lastskip is nonzero, that means the last item was a
+ % skip. And since a skip is discardable, that means this
+ % -\whatsitskip glue we're inserting is preceded by a
+ % non-discardable item, therefore it is not a potential
+ % breakpoint, therefore no \nobreak needed.
+ \ifx\lastskipmacro\zeroskipmacro
+ \else
+ \vskip-\whatsitskip
+ \fi
+ %
+ #1%
+ %
+ \ifx\lastskipmacro\zeroskipmacro
+ % If \lastskip was zero, perhaps the last item was a penalty, and
+ % perhaps it was >=10000, e.g., a \nobreak. In that case, we want
+ % to re-insert the same penalty (values >10000 are used for various
+ % signals); since we just inserted a non-discardable item, any
+ % following glue (such as a \parskip) would be a breakpoint. For example:
+ %
+ % @deffn deffn-whatever
+ % @vindex index-whatever
+ % Description.
+ % would allow a break between the index-whatever whatsit
+ % and the "Description." paragraph.
+ \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi
+ \else
+ % On the other hand, if we had a nonzero \lastskip,
+ % this make-up glue would be preceded by a non-discardable item
+ % (the whatsit from the \write), so we must insert a \nobreak.
+ \nobreak\vskip\whatsitskip
+ \fi
+\fi
+}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+ \dobreak \chapheadingskip{10000}%
+ %
+ \smallfonts \rm
+ \tolerance = 9500
+ \plainfrenchspacing
+ \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+ %
+ % See if the index file exists and is nonempty.
+ % Change catcode of @ here so that if the index file contains
+ % \initial {@}
+ % as its first line, TeX doesn't complain about mismatched braces
+ % (because it thinks @} is a control sequence).
+ \catcode`\@ = 11
+ \openin 1 \jobname.#1s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ \putwordIndexNonexistent
+ \else
+ %
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \temp
+ \ifeof 1
+ \putwordIndexIsEmpty
+ \else
+ % Index files are almost Texinfo source, but we use \ as the escape
+ % character. It would be better to use @, but that's too big a change
+ % to make right now.
+ \def\indexbackslash{\backslashcurfont}%
+ \catcode`\\ = 0
+ \escapechar = `\\
+ \begindoublecolumns
+ \input \jobname.#1s
+ \enddoublecolumns
+ \fi
+ \fi
+ \closein 1
+\endgroup}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+\def\initial#1{{%
+ % Some minor font changes for the special characters.
+ \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+ %
+ % Remove any glue we may have, we'll be inserting our own.
+ \removelastskip
+ %
+ % We like breaks before the index initials, so insert a bonus.
+ \nobreak
+ \vskip 0pt plus 3\baselineskip
+ \penalty 0
+ \vskip 0pt plus -3\baselineskip
+ %
+ % Typeset the initial. Making this add up to a whole number of
+ % baselineskips increases the chance of the dots lining up from column
+ % to column. It still won't often be perfect, because of the stretch
+ % we need before each entry, but it's better.
+ %
+ % No shrink because it confuses \balancecolumns.
+ \vskip 1.67\baselineskip plus .5\baselineskip
+ \leftline{\secbf #1}%
+ % Do our best not to break after the initial.
+ \nobreak
+ \vskip .33\baselineskip plus .1\baselineskip
+}}
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin. It is used for index
+% and table of contents entries. The paragraph is indented by \leftskip.
+%
+% A straightforward implementation would start like this:
+% \def\entry#1#2{...
+% But this freezes the catcodes in the argument, and can cause problems to
+% @code, which sets - active. This problem was fixed by a kludge---
+% ``-'' was active throughout whole index, but this isn't really right.
+%
+% The right solution is to prevent \entry from swallowing the whole text.
+% --kasal, 21nov03
+\def\entry{%
+ \begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % Do not fill out the last line with white space.
+ \parfillskip = 0in
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % \hangindent is only relevant when the entry text and page number
+ % don't both fit on one line. In that case, bob suggests starting the
+ % dots pretty far over on the line. Unfortunately, a large
+ % indentation looks wrong when the entry text itself is broken across
+ % lines. So we use a small indentation and put up with long leaders.
+ %
+ % \hangafter is reset to 1 (which is the value we want) at the start
+ % of each paragraph, so we need not do anything with that.
+ \hangindent = 2em
+ %
+ % When the entry text needs to be broken, just fill out the first line
+ % with blank space.
+ \rightskip = 0pt plus1fil
+ %
+ % A bit of stretch before each entry for the benefit of balancing
+ % columns.
+ \vskip 0pt plus1pt
+ %
+ % Swallow the left brace of the text (first parameter):
+ \afterassignment\doentry
+ \let\temp =
+}
+\def\doentry{%
+ \bgroup % Instead of the swallowed brace.
+ \noindent
+ \aftergroup\finishentry
+ % And now comes the text of the entry.
+}
+\def\finishentry#1{%
+ % #1 is the page number.
+ %
+ % The following is kludged to not output a line of dots in the index if
+ % there are no page numbers. The next person who breaks this will be
+ % cursed by a Unix daemon.
+ \setbox\boxA = \hbox{#1}%
+ \ifdim\wd\boxA = 0pt
+ \ %
+ \else
+ %
+ % If we must, put the page number on a line of its own, and fill out
+ % this line with blank space. (The \hfil is overwhelmed with the
+ % fill leaders glue in \indexdotfill if the page number does fit.)
+ \hfil\penalty50
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ % The `\ ' here is removed by the implicit \unskip that TeX does as
+ % part of (the primitive) \par. Without it, a spurious underfull
+ % \hbox ensues.
+ \ifpdf
+ \pdfgettoks#1.%
+ \ \the\toksA
+ \else
+ \ #1%
+ \fi
+ \fi
+ \par
+ \endgroup
+}
+
+% Like plain.tex's \dotfill, except uses up at least 1 em.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+\def\secondary#1#2{{%
+ \parfillskip=0in
+ \parskip=0in
+ \hangindent=1in
+ \hangafter=1
+ \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+ \ifpdf
+ \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+ \else
+ #2
+ \fi
+ \par
+}}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+ % Grab any single-column material above us.
+ \output = {%
+ %
+ % Here is a possibility not foreseen in manmac: if we accumulate a
+ % whole lot of material, we might end up calling this \output
+ % routine twice in a row (see the doublecol-lose test, which is
+ % essentially a couple of indexes with @setchapternewpage off). In
+ % that case we just ship out what is in \partialpage with the normal
+ % output routine. Generally, \partialpage will be empty when this
+ % runs and this will be a no-op. See the indexspread.tex test case.
+ \ifvoid\partialpage \else
+ \onepageout{\pagecontents\partialpage}%
+ \fi
+ %
+ \global\setbox\partialpage = \vbox{%
+ % Unvbox the main output page.
+ \unvbox\PAGE
+ \kern-\topskip \kern\baselineskip
+ }%
+ }%
+ \eject % run that output routine to set \partialpage
+ %
+ % Use the double-column output routine for subsequent pages.
+ \output = {\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it in one place.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +-<1pt)
+ % as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Double the \vsize as well. (We don't need a separate register here,
+ % since nobody clobbers \vsize.)
+ \vsize = 2\vsize
+}
+
+% The double-column output routine for all double-column pages except
+% the last.
+%
+\def\doublecolumnout{%
+ \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ % Get the available space for the double columns -- the normal
+ % (undoubled) page height minus any material left over from the
+ % previous page.
+ \dimen@ = \vsize
+ \divide\dimen@ by 2
+ \advance\dimen@ by -\ht\partialpage
+ %
+ % box0 will be the left-hand column, box2 the right.
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar
+ \unvbox255
+ \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+ \unvbox\partialpage
+ %
+ \hsize = \doublecolumnhsize
+ \wd0=\hsize \wd2=\hsize
+ \hbox to\pagewidth{\box0\hfil\box2}%
+}
+%
+% All done with double columns.
+\def\enddoublecolumns{%
+ % The following penalty ensures that the page builder is exercised
+ % _before_ we change the output routine. This is necessary in the
+ % following situation:
+ %
+ % The last section of the index consists only of a single entry.
+ % Before this section, \pagetotal is less than \pagegoal, so no
+ % break occurs before the last section starts. However, the last
+ % section, consisting of \initial and the single \entry, does not
+ % fit on the page and has to be broken off. Without the following
+ % penalty the page builder will not be exercised until \eject
+ % below, and by that time we'll already have changed the output
+ % routine to the \balancecolumns version, so the next-to-last
+ % double-column page will be processed with \balancecolumns, which
+ % is wrong: The two columns will go to the main vertical list, with
+ % the broken-off section in the recent contributions. As soon as
+ % the output routine finishes, TeX starts reconsidering the page
+ % break. The two columns and the broken-off section both fit on the
+ % page, because the two columns now take up only half of the page
+ % goal. When TeX sees \eject from below which follows the final
+ % section, it invokes the new output routine that we've set after
+ % \balancecolumns below; \onepageout will try to fit the two columns
+ % and the final section into the vbox of \pageheight (see
+ % \pagebody), causing an overfull box.
+ %
+ % Note that glue won't work here, because glue does not exercise the
+ % page builder, unlike penalties (see The TeXbook, pp. 280-281).
+ \penalty0
+ %
+ \output = {%
+ % Split the last of the double-column material. Leave it on the
+ % current page, no automatic page break.
+ \balancecolumns
+ %
+ % If we end up splitting too much material for the current page,
+ % though, there will be another page break right after this \output
+ % invocation ends. Having called \balancecolumns once, we do not
+ % want to call it again. Therefore, reset \output to its normal
+ % definition right away. (We hope \balancecolumns will never be
+ % called on to balance too much material, but if it is, this makes
+ % the output somewhat more palatable.)
+ \global\output = {\onepageout{\pagecontents\PAGE}}%
+ }%
+ \eject
+ \endgroup % started in \begindoublecolumns
+ %
+ % \pagegoal was set to the doubled \vsize above, since we restarted
+ % the current page. We're now back to normal single-column
+ % typesetting, so reset \pagegoal to the normal \vsize (after the
+ % \endgroup where \vsize got restored).
+ \pagegoal = \vsize
+}
+%
+% Called at the end of the double column material.
+\def\balancecolumns{%
+ \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+ \dimen@ = \ht0
+ \advance\dimen@ by \topskip
+ \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by 2 % target to split to
+ %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
+ \splittopskip = \topskip
+ % Loop until we get a decent breakpoint.
+ {%
+ \vbadness = 10000
+ \loop
+ \global\setbox3 = \copy0
+ \global\setbox1 = \vsplit3 to \dimen@
+ \ifdim\ht3>\dimen@
+ \global\advance\dimen@ by 1pt
+ \repeat
+ }%
+ %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
+ \setbox0=\vbox to\dimen@{\unvbox1}%
+ \setbox2=\vbox to\dimen@{\unvbox3}%
+ %
+ \pagesofar
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% \unnumberedno is an oxymoron, of course. But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number". We avoid collisions with chapter
+% numbers by starting them at 10000. (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno \secno=0
+\newcount\subsecno \subsecno=0
+\newcount\subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+ \ifnum\appendixno=`A A%
+ \else\ifnum\appendixno=`B B%
+ \else\ifnum\appendixno=`C C%
+ \else\ifnum\appendixno=`D D%
+ \else\ifnum\appendixno=`E E%
+ \else\ifnum\appendixno=`F F%
+ \else\ifnum\appendixno=`G G%
+ \else\ifnum\appendixno=`H H%
+ \else\ifnum\appendixno=`I I%
+ \else\ifnum\appendixno=`J J%
+ \else\ifnum\appendixno=`K K%
+ \else\ifnum\appendixno=`L L%
+ \else\ifnum\appendixno=`M M%
+ \else\ifnum\appendixno=`N N%
+ \else\ifnum\appendixno=`O O%
+ \else\ifnum\appendixno=`P P%
+ \else\ifnum\appendixno=`Q Q%
+ \else\ifnum\appendixno=`R R%
+ \else\ifnum\appendixno=`S S%
+ \else\ifnum\appendixno=`T T%
+ \else\ifnum\appendixno=`U U%
+ \else\ifnum\appendixno=`V V%
+ \else\ifnum\appendixno=`W W%
+ \else\ifnum\appendixno=`X X%
+ \else\ifnum\appendixno=`Y Y%
+ \else\ifnum\appendixno=`Z Z%
+ % The \the is necessary, despite appearances, because \appendixletter is
+ % expanded while writing the .toc file. \char\appendixno is not
+ % expandable, thus it is written literally, thus all appendixes come out
+ % with the same letter (or @) in the toc without it.
+ \else\char\the\appendixno
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+% Each @chapter defines these (using marks) as the number+name, number
+% and name of the chapter. Page headings and footings can use
+% these. @section does likewise.
+\def\thischapter{}
+\def\thischapternum{}
+\def\thischaptername{}
+\def\thissection{}
+\def\thissectionnum{}
+\def\thissectionname{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achive this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unmlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+ % Compute the abs. sec. level:
+ \absseclevel=#2
+ \advance\absseclevel by \secbase
+ % Make sure \absseclevel doesn't fall outside the range:
+ \ifnum \absseclevel < 0
+ \absseclevel = 0
+ \else
+ \ifnum \absseclevel > 3
+ \absseclevel = 3
+ \fi
+ \fi
+ % The heading type:
+ \def\headtype{#1}%
+ \if \headtype U%
+ \ifnum \absseclevel < \unmlevel
+ \chardef\unmlevel = \absseclevel
+ \fi
+ \else
+ % Check for appendix sections:
+ \ifnum \absseclevel = 0
+ \edef\chapheadtype{\headtype}%
+ \else
+ \if \headtype A\if \chapheadtype N%
+ \errmessage{@appendix... within a non-appendix chapter}%
+ \fi\fi
+ \fi
+ % Check for numbered within unnumbered:
+ \ifnum \absseclevel > \unmlevel
+ \def\headtype{U}%
+ \else
+ \chardef\unmlevel = 3
+ \fi
+ \fi
+ % Now print the heading:
+ \if \headtype U%
+ \ifcase\absseclevel
+ \unnumberedzzz{#3}%
+ \or \unnumberedseczzz{#3}%
+ \or \unnumberedsubseczzz{#3}%
+ \or \unnumberedsubsubseczzz{#3}%
+ \fi
+ \else
+ \if \headtype A%
+ \ifcase\absseclevel
+ \appendixzzz{#3}%
+ \or \appendixsectionzzz{#3}%
+ \or \appendixsubseczzz{#3}%
+ \or \appendixsubsubseczzz{#3}%
+ \fi
+ \else
+ \ifcase\absseclevel
+ \chapterzzz{#3}%
+ \or \seczzz{#3}%
+ \or \numberedsubseczzz{#3}%
+ \or \numberedsubsubseczzz{#3}%
+ \fi
+ \fi
+ \fi
+ \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered. Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v. By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+ % section resetting is \global in case the chapter is in a group, such
+ % as an @include file.
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\chapno by 1
+ %
+ % Used for \float.
+ \gdef\chaplevelprefix{\the\chapno.}%
+ \resetallfloatnos
+ %
+ % \putwordChapter can contain complex things in translations.
+ \toks0=\expandafter{\putwordChapter}%
+ \message{\the\toks0 \space \the\chapno}%
+ %
+ % Write the actual heading.
+ \chapmacro{#1}{Ynumbered}{\the\chapno}%
+ %
+ % So @section and the like are numbered underneath this chapter.
+ \global\let\section = \numberedsec
+ \global\let\subsection = \numberedsubsec
+ \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz
+%
+\def\appendixzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\appendixno by 1
+ \gdef\chaplevelprefix{\appendixletter.}%
+ \resetallfloatnos
+ %
+ % \putwordAppendix can contain complex things in translations.
+ \toks0=\expandafter{\putwordAppendix}%
+ \message{\the\toks0 \space \appendixletter}%
+ %
+ \chapmacro{#1}{Yappendix}{\appendixletter}%
+ %
+ \global\let\section = \appendixsec
+ \global\let\subsection = \appendixsubsec
+ \global\let\subsubsection = \appendixsubsubsec
+}
+
+\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\unnumberedno by 1
+ %
+ % Since an unnumbered has no number, no prefix for figures.
+ \global\let\chaplevelprefix = \empty
+ \resetallfloatnos
+ %
+ % This used to be simply \message{#1}, but TeX fully expands the
+ % argument to \message. Therefore, if #1 contained @-commands, TeX
+ % expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+ % expanded @cite (which turns out to cause errors because \cite is meant
+ % to be executed, not expanded).
+ %
+ % Anyway, we don't want the fully-expanded definition of @cite to appear
+ % as a result of the \message, we just want `@cite' itself. We use
+ % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+ % simply yielding the contents of <toks register>. (We also do this for
+ % the toc entries.)
+ \toks0 = {#1}%
+ \message{(\the\toks0)}%
+ %
+ \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+ %
+ \global\let\section = \unnumberedsec
+ \global\let\subsection = \unnumberedsubsec
+ \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+ % Well, we could do the following in a group, but that would break
+ % an assumption that \chapmacro is called at the outermost level.
+ % Thus we are safer this way: --kasal, 24feb04
+ \let\centerparametersmaybe = \centerparameters
+ \unnmhead0{#1}%
+ \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynumbered}%
+ {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and such:
+% 1) We use \vbox rather than the earlier \line to permit
+% overlong headings to fold.
+% 2) \hyphenpenalty is set to 10000 because hyphenation in a
+% heading is obnoxious; this forbids it.
+% 3) Likewise, headings look best if no \parindent is used, and
+% if justification is not attempted. Hence \raggedright.
+
+\def\majorheading{%
+ {\advance\chapheadingskip by 10pt \chapbreak }%
+ \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+ {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\ptexraggedright
+ \rmisbold #1\hfill}}%
+ \bigskip \par\penalty 200\relax
+ \suppressfirstparagraphindent
+}
+
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip\chapheadingskip
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+% Because \domark is called before \chapoddpage, the filler page will
+% get the headings for the next chapter, which is wrong. But we don't
+% care -- we just disable all headings on the filler page.
+\def\chapoddpage{%
+ \chappager
+ \ifodd\pageno \else
+ \begingroup
+ \evenheadline={\hfil}\evenfootline={\hfil}%
+ \oddheadline={\hfil}\oddfootline={\hfil}%
+ \hbox to 0pt{}%
+ \chappager
+ \endgroup
+ \fi
+}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+% Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+\def\Yappendixkeyword{Yappendix}
+%
+\def\chapmacro#1#2#3{%
+ % Insert the first mark before the heading break (see notes for \domark).
+ \let\prevchapterdefs=\lastchapterdefs
+ \let\prevsectiondefs=\lastsectiondefs
+ \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}%
+ \gdef\thissection{}}%
+ %
+ \def\temptype{#2}%
+ \ifx\temptype\Ynothingkeyword
+ \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+ \gdef\thischapter{\thischaptername}}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}%
+ \gdef\thischapter{}}%
+ \else\ifx\temptype\Yappendixkeyword
+ \toks0={#1}%
+ \xdef\lastchapterdefs{%
+ \gdef\noexpand\thischaptername{\the\toks0}%
+ \gdef\noexpand\thischapternum{\appendixletter}%
+ % \noexpand\putwordAppendix avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thischapter{\noexpand\putwordAppendix{}
+ \noexpand\thischapternum:
+ \noexpand\thischaptername}%
+ }%
+ \else
+ \toks0={#1}%
+ \xdef\lastchapterdefs{%
+ \gdef\noexpand\thischaptername{\the\toks0}%
+ \gdef\noexpand\thischapternum{\the\chapno}%
+ % \noexpand\putwordChapter avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thischapter{\noexpand\putwordChapter{}
+ \noexpand\thischapternum:
+ \noexpand\thischaptername}%
+ }%
+ \fi\fi\fi
+ %
+ % Output the mark. Pass it through \safewhatsit, to take care of
+ % the preceding space.
+ \safewhatsit\domark
+ %
+ % Insert the chapter heading break.
+ \pchapsepmacro
+ %
+ % Now the second mark, after the heading break. No break points
+ % between here and the heading.
+ \let\prevchapterdefs=\lastchapterdefs
+ \let\prevsectiondefs=\lastsectiondefs
+ \domark
+ %
+ {%
+ \chapfonts \rmisbold
+ %
+ % Have to define \lastsection before calling \donoderef, because the
+ % xref code eventually uses it. On the other hand, it has to be called
+ % after \pchapsepmacro, or the headline will change too soon.
+ \gdef\lastsection{#1}%
+ %
+ % Only insert the separating space if we have a chapter/appendix
+ % number, and don't print the unnumbered ``number''.
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unnchap}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+ \def\toctype{omit}%
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+ \def\toctype{app}%
+ \else
+ \setbox0 = \hbox{#3\enspace}%
+ \def\toctype{numchap}%
+ \fi\fi\fi
+ %
+ % Write the toc entry for this chapter. Must come before the
+ % \donoderef, because we include the current node name in the toc
+ % entry, and \donoderef resets it to empty.
+ \writetocentry{\toctype}{#1}{#3}%
+ %
+ % For pdftex, we have to write out the node definition (aka, make
+ % the pdfdest) after any page break, but before the actual text has
+ % been typeset. If the destination for the pdf outline is after the
+ % text, then jumping from the outline may wind up with the text not
+ % being visible, for instance under high magnification.
+ \donoderef{#2}%
+ %
+ % Typeset the actual heading.
+ \nobreak % Avoid page breaks at the interline glue.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright
+ \hangindent=\wd0 \centerparametersmaybe
+ \unhbox0 #1\par}%
+ }%
+ \nobreak\bigskip % no page break after a chapter title
+ \nobreak
+}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+ \advance\rightskip by 3\rightskip
+ \leftskip = \rightskip
+ \parfillskip = 0pt
+}
+
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff. We'll see. --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\ptexraggedright
+ \rmisbold #1\hfill}}\bigskip \par\nobreak
+}
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+\def\centerchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt
+ \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak
+}
+\def\CHAPFopen{%
+ \global\let\chapmacro=\chfopen
+ \global\let\centerchapmacro=\centerchfopen}
+
+
+% Section titles. These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
+
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
+
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
+
+
+% Print any size, any type, section title.
+%
+% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is
+% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
+% section number.
+%
+\def\seckeyword{sec}
+%
+\def\sectionheading#1#2#3#4{%
+ {%
+ % Switch to the right set of fonts.
+ \csname #2fonts\endcsname \rmisbold
+ %
+ \def\sectionlevel{#2}%
+ \def\temptype{#3}%
+ %
+ % Insert first mark before the heading break (see notes for \domark).
+ \let\prevsectiondefs=\lastsectiondefs
+ \ifx\temptype\Ynothingkeyword
+ \ifx\sectionlevel\seckeyword
+ \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}%
+ \gdef\thissection{\thissectionname}}%
+ \fi
+ \else\ifx\temptype\Yomitfromtockeyword
+ % Don't redefine \thissection.
+ \else\ifx\temptype\Yappendixkeyword
+ \ifx\sectionlevel\seckeyword
+ \toks0={#1}%
+ \xdef\lastsectiondefs{%
+ \gdef\noexpand\thissectionname{\the\toks0}%
+ \gdef\noexpand\thissectionnum{#4}%
+ % \noexpand\putwordSection avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thissection{\noexpand\putwordSection{}
+ \noexpand\thissectionnum:
+ \noexpand\thissectionname}%
+ }%
+ \fi
+ \else
+ \ifx\sectionlevel\seckeyword
+ \toks0={#1}%
+ \xdef\lastsectiondefs{%
+ \gdef\noexpand\thissectionname{\the\toks0}%
+ \gdef\noexpand\thissectionnum{#4}%
+ % \noexpand\putwordSection avoids expanding indigestible
+ % commands in some of the translations.
+ \gdef\noexpand\thissection{\noexpand\putwordSection{}
+ \noexpand\thissectionnum:
+ \noexpand\thissectionname}%
+ }%
+ \fi
+ \fi\fi\fi
+ %
+ % Go into vertical mode. Usually we'll already be there, but we
+ % don't want the following whatsit to end up in a preceding paragraph
+ % if the document didn't happen to have a blank line.
+ \par
+ %
+ % Output the mark. Pass it through \safewhatsit, to take care of
+ % the preceding space.
+ \safewhatsit\domark
+ %
+ % Insert space above the heading.
+ \csname #2headingbreak\endcsname
+ %
+ % Now the second mark, after the heading break. No break points
+ % between here and the heading.
+ \let\prevsectiondefs=\lastsectiondefs
+ \domark
+ %
+ % Only insert the space after the number if we have a section number.
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unn}%
+ \gdef\lastsection{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ % for @headings -- no section number, don't include in toc,
+ % and don't redefine \lastsection.
+ \setbox0 = \hbox{}%
+ \def\toctype{omit}%
+ \let\sectionlevel=\empty
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{app}%
+ \gdef\lastsection{#1}%
+ \else
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{num}%
+ \gdef\lastsection{#1}%
+ \fi\fi\fi
+ %
+ % Write the toc entry (before \donoderef). See comments in \chapmacro.
+ \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+ %
+ % Write the node reference (= pdf destination for pdftex).
+ % Again, see comments in \chapmacro.
+ \donoderef{#3}%
+ %
+ % Interline glue will be inserted when the vbox is completed.
+ % That glue will be a valid breakpoint for the page, since it'll be
+ % preceded by a whatsit (usually from the \donoderef, or from the
+ % \writetocentry if there was no node). We don't want to allow that
+ % break, since then the whatsits could end up on page n while the
+ % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000.
+ \nobreak
+ %
+ % Output the actual section heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright
+ \hangindent=\wd0 % zero if no section number
+ \unhbox0 #1}%
+ }%
+ % Add extra space after the heading -- half of whatever came above it.
+ % Don't allow stretch, though.
+ \kern .5 \csname #2headingskip\endcsname
+ %
+ % Do not let the kern be a potential breakpoint, as it would be if it
+ % was followed by glue.
+ \nobreak
+ %
+ % We'll almost certainly start a paragraph next, so don't let that
+ % glue accumulate. (Not a breakpoint because it's preceded by a
+ % discardable item.)
+ \vskip-\parskip
+ %
+ % This is purely so the last item on the list is a known \penalty >
+ % 10000. This is so \startdefun can avoid allowing breakpoints after
+ % section headings. Otherwise, it would insert a valid breakpoint between:
+ %
+ % @section sec-whatever
+ % @deffn def-whatever
+ \penalty 10001
+}
+
+
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
+
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this. The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything. This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+ \edef\writetoctype{#1}%
+ \ifx\writetoctype\omitkeyword \else
+ \iftocfileopened\else
+ \immediate\openout\tocfile = \jobname.toc
+ \global\tocfileopenedtrue
+ \fi
+ %
+ \iflinks
+ {\atdummies
+ \edef\temp{%
+ \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+ \temp
+ }%
+ \fi
+ \fi
+ %
+ % Tell \shipout to create a pdf destination on each page, if we're
+ % writing pdf. These are used in the table of contents. We can't
+ % just write one on every page because the title pages are numbered
+ % 1 and 2 (the page numbers aren't printed), and so are the first
+ % two pages of the document. Thus, we'd have two destinations named
+ % `1', and two named `2'.
+ \ifpdf \global\pdfmakepagedesttrue \fi
+}
+
+
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care. This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+%
+\def\activecatcodes{%
+ \catcode`\"=\active
+ \catcode`\$=\active
+ \catcode`\<=\active
+ \catcode`\>=\active
+ \catcode`\\=\active
+ \catcode`\^=\active
+ \catcode`\_=\active
+ \catcode`\|=\active
+ \catcode`\~=\active
+}
+
+
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+ \setupdatafile
+ \activecatcodes
+ \input \tocreadfilename
+}
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
+\def\startcontents#1{%
+ % If @setchapternewpage on, and @headings double, the contents should
+ % start on an odd page, unlike chapters. Thus, we maintain
+ % \contentsalignmacro in parallel with \pagealignmacro.
+ % From: Torbjorn Granlund <tege@matematik.su.se>
+ \contentsalignmacro
+ \immediate\closeout\tocfile
+ %
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \chapmacro{#1}{Yomitfromtoc}{}%
+ %
+ \savepageno = \pageno
+ \begingroup % Set up to handle contents files properly.
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+ %
+ % Roman numerals for page numbers.
+ \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
+}
+
+% redefined for the two-volume lispref. We always output on
+% \jobname.toc even if this is redefined.
+%
+\def\tocreadfilename{\jobname.toc}
+
+% Normal (long) toc.
+%
+\def\contents{%
+ \startcontents{\putwordTOC}%
+ \openin 1 \tocreadfilename\space
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \ifeof 1 \else
+ \pdfmakeoutlines
+ \fi
+ \closein 1
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+
+% And just the chapters.
+\def\summarycontents{%
+ \startcontents{\putwordShortTOC}%
+ %
+ \let\numchapentry = \shortchapentry
+ \let\appentry = \shortchapentry
+ \let\unnchapentry = \shortunnchapentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf
+ \let\sl=\shortcontsl \let\tt=\shortconttt
+ \rm
+ \hyphenpenalty = 10000
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\numsecentry##1##2##3##4{}
+ \let\appsecentry = \numsecentry
+ \let\unnsecentry = \numsecentry
+ \let\numsubsecentry = \numsecentry
+ \let\appsubsecentry = \numsecentry
+ \let\unnsubsecentry = \numsecentry
+ \let\numsubsubsecentry = \numsecentry
+ \let\appsubsubsecentry = \numsecentry
+ \let\unnsubsubsecentry = \numsecentry
+ \openin 1 \tocreadfilename\space
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \closein 1
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+\let\shortcontents = \summarycontents
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+ % This space should be enough, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % But use \hss just in case.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in by \shortchapentry above.)
+ %
+ % We'd like to right-justify chapter numbers, but that looks strange
+ % with appendix letters. And right-justifying numbers and
+ % left-justifying letters looks strange when there is less than 10
+ % chapters. Have to read the whole toc once to know how many chapters
+ % there are before deciding ...
+ \hbox to 1em{#1\hss}%
+}
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+%
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
+}
+
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+ % We use M since it's probably the widest letter.
+ \setbox0 = \hbox{\putwordAppendix{} M}%
+ \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+ \begingroup
+ \chapentryfonts
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
+\message{environments,}
+% @foo ... @end foo.
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\envdef\tex{%
+ \setupmarkupstyle{tex}%
+ \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+ \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+ \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+ \catcode `\%=14
+ \catcode `\+=\other
+ \catcode `\"=\other
+ \catcode `\|=\other
+ \catcode `\<=\other
+ \catcode `\>=\other
+ \catcode`\`=\other
+ \catcode`\'=\other
+ \escapechar=`\\
+ %
+ \let\b=\ptexb
+ \let\bullet=\ptexbullet
+ \let\c=\ptexc
+ \let\,=\ptexcomma
+ \let\.=\ptexdot
+ \let\dots=\ptexdots
+ \let\equiv=\ptexequiv
+ \let\!=\ptexexclam
+ \let\i=\ptexi
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \let\{=\ptexlbrace
+ \let\+=\tabalign
+ \let\}=\ptexrbrace
+ \let\/=\ptexslash
+ \let\*=\ptexstar
+ \let\t=\ptext
+ \expandafter \let\csname top\endcsname=\ptextop % outer
+ \let\frenchspacing=\plainfrenchspacing
+ %
+ \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+ \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+ \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip.
+%
+\def\aboveenvbreak{{%
+ % =10000 instead of <10000 because of a special case in \itemzzz and
+ % \sectionheading, q.v.
+ \ifnum \lastpenalty=10000 \else
+ \advance\envskipamount by \parskip
+ \endgraf
+ \ifdim\lastskip<\envskipamount
+ \removelastskip
+ % it's not a good place to break if the last penalty was \nobreak
+ % or better ...
+ \ifnum\lastpenalty<10000 \penalty-50 \fi
+ \vskip\envskipamount
+ \fi
+ \fi
+}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\envdef\cartouche{%
+ \ifhmode\par\fi % can't be in the midst of a paragraph.
+ \startsavinginserts
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt % we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+ % side, and for 6pt waste from
+ % each corner char, and rule thickness
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing = t%
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \kern3pt
+ \hsize=\cartinner
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+ \comment % For explanation, see the end of \def\group.
+}
+\def\Ecartouche{%
+ \ifhmode\par\fi
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+ \checkinserts
+}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\newdimen\nonfillparindent
+\def\nonfillstart{%
+ \aboveenvbreak
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ % Turn off paragraph indentation but redefine \indent to emulate
+ % the normal \indent.
+ \nonfillparindent=\parindent
+ \parindent = 0pt
+ \let\indent\nonfillindent
+ %
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \let\exdent=\nofillexdent
+}
+
+\begingroup
+\obeyspaces
+% We want to swallow spaces (but not other tokens) after the fake
+% @indent in our nonfill-environments, where spaces are normally
+% active and set to @tie, resulting in them not being ignored after
+% @indent.
+\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}%
+\gdef\nonfillindentcheck{%
+\ifx\temp %
+\expandafter\nonfillindentgobble%
+\else%
+\leavevmode\nonfillindentbox%
+\fi%
+}%
+\endgroup
+\def\nonfillindentgobble#1{\nonfillindent}
+\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}}
+
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+% @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+ \ifx\SETdispenvsize\smallword
+ % end paragraph for sake of leading, in case document has no blank
+ % line. This is redundant with what happens in \aboveenvbreak, but
+ % we need to do it before changing the fonts, and it's inconvenient
+ % to change the fonts afterward.
+ \ifnum \lastpenalty=10000 \else \endgraf \fi
+ \smallexamplefonts \rm
+ \fi
+}
+\def\setsmalldispenv{%
+ \ifx\SETdispenvsize\nosmallword
+ \else
+ \ifnum \lastpenalty=10000 \else \endgraf \fi
+ \smallexamplefonts \rm
+ \fi
+}
+
+% We often define two environments, @foo and @smallfoo.
+% Let's do it by one command:
+\def\makedispenv #1#2{
+ \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}
+ \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}
+ \expandafter\let\csname E#1\endcsname \afterenvbreak
+ \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two synonyms:
+\def\maketwodispenvs #1#2#3{
+ \makedispenv{#1}{#3}
+ \makedispenv{#2}{#3}
+}
+
+% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvs {lisp}{example}{%
+ \nonfillstart
+ \tt\setupmarkupstyle{example}%
+ \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+ \gobble % eat return
+}
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenv {display}{%
+ \nonfillstart
+ \gobble
+}
+
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenv{format}{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+\let\Eflushleft = \afterenvbreak
+
+% @flushright.
+%
+\envdef\flushright{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \advance\leftskip by 0pt plus 1fill
+ \gobble
+}
+\let\Eflushright = \afterenvbreak
+
+
+% @raggedright does more-or-less normal line breaking but no right
+% justification. From plain.tex.
+\envdef\raggedright{%
+ \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax
+}
+\let\Eraggedright\par
+
+\envdef\raggedleft{%
+ \parindent=0pt \leftskip0pt plus2em
+ \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+ \hbadness=10000 % Last line will usually be underfull, so turn off
+ % badness reporting.
+}
+\let\Eraggedleft\par
+
+\envdef\raggedcenter{%
+ \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em
+ \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt
+ \hbadness=10000 % Last line will usually be underfull, so turn off
+ % badness reporting.
+}
+\let\Eraggedcenter\par
+
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins. We keep \parskip nonzero in general, since
+% we're doing normal filling. So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\def\quotationstart{%
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \parindent=0pt
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \parsearg\quotationlabel
+}
+
+\envdef\quotation{%
+ \setnormaldispenv
+ \quotationstart
+}
+
+\envdef\smallquotation{%
+ \setsmalldispenv
+ \quotationstart
+}
+\let\Esmallquotation = \Equotation
+
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+ \par
+ \ifx\quotationauthor\undefined\else
+ % indent a bit.
+ \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+ \fi
+ {\parskip=0pt \afterenvbreak}%
+}
+
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty \else
+ {\bf #1: }%
+ \fi
+}
+
+
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too. Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+ \do\ \do\\\do\{\do\}\do\$\do\&%
+ \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+ \do\<\do\>\do\|\do\@\do+\do\"%
+ % Don't do the quotes -- if we do, @set txicodequoteundirected and
+ % @set txicodequotebacktick will not have effect on @verb and
+ % @verbatim, and ?` and !` ligatures won't get disabled.
+ %\do\`\do\'%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+ \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+ \tt % easiest (and conventionally used) font for verbatim
+ \def\par{\leavevmode\endgraf}%
+ \setupmarkupstyle{verb}%
+ \tabeightspaces
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+}
+
+% Setup for the @verbatim environment
+%
+% Real tab expansion
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+\def\starttabbox{\setbox0=\hbox\bgroup}
+%
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabexpand{%
+ \catcode`\^^I=\active
+ \def^^I{\leavevmode\egroup
+ \dimen0=\wd0 % the width so far, or since the previous tab
+ \divide\dimen0 by\tabw
+ \multiply\dimen0 by\tabw % compute previous multiple of \tabw
+ \advance\dimen0 by\tabw % advance to next multiple of \tabw
+ \wd0=\dimen0 \box0 \starttabbox
+ }%
+ }
+\endgroup
+
+% start the verbatim environment.
+\def\setupverbatim{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ % Easiest (and conventionally used) font for verbatim
+ \tt
+ \def\par{\leavevmode\egroup\box0\endgraf}%
+ \tabexpand
+ \setupmarkupstyle{verbatim}%
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+ \everypar{\starttabbox}%
+}
+
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters. Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+% \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+ \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+ \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+% \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+ \catcode`\ =\active
+ \obeylines %
+ % ignore everything up to the first ^^M, that's the newline at the end
+ % of the @verbatim input line itself. Otherwise we get an extra blank
+ % line in the output.
+ \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+ % We really want {...\end verbatim} in the body of the macro, but
+ % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+ \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
+
+
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+ {%
+ \makevalueexpandable
+ \setupverbatim
+ \indexnofonts % Allow `@@' and other weird things in file names.
+ \input #1
+ \afterenvbreak
+ }%
+}
+
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is very desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+ \begingroup
+ \parindent = 0pt % paragraph indentation looks wrong on title page
+ \scanexp\copyingtext
+ \endgroup
+}
+
+
+\message{defuns,}
+% @defun etc.
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+\newcount\defunpenalty
+
+% Start the processing of @deffn:
+\def\startdefun{%
+ \ifnum\lastpenalty<10000
+ \medbreak
+ \defunpenalty=10003 % Will keep this @deffn together with the
+ % following @def command, see below.
+ \else
+ % If there are two @def commands in a row, we'll have a \nobreak,
+ % which is there to keep the function description together with its
+ % header. But if there's nothing but headers, we need to allow a
+ % break somewhere. Check specifically for penalty 10002, inserted
+ % by \printdefunline, instead of 10000, since the sectioning
+ % commands also insert a nobreak penalty, and we don't want to allow
+ % a break between a section heading and a defun.
+ %
+ % As a minor refinement, we avoid "club" headers by signalling
+ % with penalty of 10003 after the very first @deffn in the
+ % sequence (see above), and penalty of 10002 after any following
+ % @def command.
+ \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi
+ %
+ % Similarly, after a section heading, do not allow a break.
+ % But do insert the glue.
+ \medskip % preceded by discardable penalty, so not a breakpoint
+ \fi
+ %
+ \parindent=0in
+ \advance\leftskip by \defbodyindent
+ \exdentamount=\defbodyindent
+}
+
+\def\dodefunx#1{%
+ % First, check whether we are in the right environment:
+ \checkenv#1%
+ %
+ % As above, allow line break if we have multiple x headers in a row.
+ % It's not a great place, though.
+ \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi
+ %
+ % And now, it's time to reuse the body of the original defun:
+ \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
+
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+ \begingroup
+ % call \deffnheader:
+ #1#2 \endheader
+ % common ending:
+ \interlinepenalty = 10000
+ \advance\rightskip by 0pt plus 1fil
+ \endgraf
+ \nobreak\vskip -\parskip
+ \penalty\defunpenalty % signal to \startdefun and \dodefunx
+ % Some of the @defun-type tags do not enable magic parentheses,
+ % rendering the following check redundant. But we don't optimize.
+ \checkparencounts
+ \endgroup
+}
+
+\def\Edefun{\endgraf\medbreak}
+
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remaining is to define \deffnheader.
+%
+\def\makedefun#1{%
+ \expandafter\let\csname E#1\endcsname = \Edefun
+ \edef\temp{\noexpand\domakedefun
+ \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+ \temp
+}
+
+% \domakedefun \deffn \deffnx \deffnheader
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+ \envdef#1{%
+ \startdefun
+ \parseargusing\activeparens{\printdefunline#3}%
+ }%
+ \def#2{\dodefunx#1}%
+ \def#3%
+}
+
+%%% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
+
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
+
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+ % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+ \dosubind{fn}{\code{#3}}{#1}%
+ \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
+}
+
+%%% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
+
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{fn}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
+
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{vr}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Untyped variables:
+
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
+
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
+
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
+
+%%% Type:
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+ \doind{tp}{\code{#2}}%
+ \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
+
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+ % Get the values of \leftskip and \rightskip as they were outside the @def...
+ \advance\leftskip by -\defbodyindent
+ %
+ % How we'll format the type name. Putting it in brackets helps
+ % distinguish it from the body text that may end up on the next line
+ % just below it.
+ \def\temp{#1}%
+ \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+ %
+ % Figure out line sizes for the paragraph shape.
+ % The first line needs space for \box0; but if \rightskip is nonzero,
+ % we need only space for the part of \box0 which exceeds it:
+ \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip
+ % The continuations:
+ \dimen2=\hsize \advance\dimen2 by -\defargsindent
+ % (plain.tex says that \dimen1 should be used only as global.)
+ \parshape 2 0in \dimen0 \defargsindent \dimen2
+ %
+ % Put the type name to the right margin.
+ \noindent
+ \hbox to 0pt{%
+ \hfil\box0 \kern-\hsize
+ % \hsize has to be shortened this way:
+ \kern\leftskip
+ % Intentionally do not respect \rightskip, since we need the space.
+ }%
+ %
+ % Allow all lines to be underfull without complaint:
+ \tolerance=10000 \hbadness=10000
+ \exdentamount=\defbodyindent
+ {%
+ % defun fonts. We use typewriter by default (used to be bold) because:
+ % . we're printing identifiers, they should be in tt in principle.
+ % . in languages with many accents, such as Czech or French, it's
+ % common to leave accents off identifiers. The result looks ok in
+ % tt, but exceedingly strange in rm.
+ % . we don't want -- and --- to be treated as ligatures.
+ % . this still does not fix the ?` and !` ligatures, but so far no
+ % one has made identifiers using them :).
+ \df \tt
+ \def\temp{#2}% return value type
+ \ifx\temp\empty\else \tclose{\temp} \fi
+ #3% output function name
+ }%
+ {\rm\enskip}% hskip 0.5 em of \tenrm
+ %
+ \boldbrax
+ % arguments will be output next, if any.
+}
+
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name. This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable. Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+ % use sl by default (not ttsl),
+ % tt for the names.
+ \df \sl \hyphenchar\font=0
+ %
+ % On the other hand, if an argument has two dashes (for instance), we
+ % want a way to get ttsl. Let's try @var for that.
+ \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}%
+ #1%
+ \sl\hyphenchar\font=45
+}
+
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+ \catcode`\(=\active \catcode`\)=\active
+ \catcode`\[=\active \catcode`\]=\active
+ \catcode`\&=\active
+}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+ \activeparens
+ \global\let(=\lparen \global\let)=\rparen
+ \global\let[=\lbrack \global\let]=\rbrack
+ \global\let& = \&
+
+ \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+ \gdef\magicamp{\let&=\amprm}
+}
+
+\newcount\parencount
+
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+ \ifampseen
+ % At the first level, print parens in roman,
+ % otherwise use the default font.
+ \ifnum \parencount=1 \rm \fi
+ \else
+ % The \sf parens (in \boldbrax) actually are a little bolder than
+ % the contained text. This is especially needed for [ and ] .
+ \sf
+ \fi
+}
+\def\infirstlevel#1{%
+ \ifampseen
+ \ifnum\parencount=1
+ #1%
+ \fi
+ \fi
+}
+\def\bfafterword#1 {#1 \bf}
+
+\def\opnr{%
+ \global\advance\parencount by 1
+ {\parenfont(}%
+ \infirstlevel \bfafterword
+}
+\def\clnr{%
+ {\parenfont)}%
+ \infirstlevel \sl
+ \global\advance\parencount by -1
+}
+
+\newcount\brackcount
+\def\lbrb{%
+ \global\advance\brackcount by 1
+ {\bf[}%
+}
+\def\rbrb{%
+ {\bf]}%
+ \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+ \ifnum\parencount=0 \else \badparencount \fi
+ \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+% these should not use \errmessage; the glibc manual, at least, actually
+% has such constructs (when documenting function pointers).
+\def\badparencount{%
+ \message{Warning: unbalanced parentheses in @def...}%
+ \global\parencount=0
+}
+\def\badbrackcount{%
+ \message{Warning: unbalanced square brackets in @def...}%
+ \global\brackcount=0
+}
+
+
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\undefined
+ \newwrite\macscribble
+ \def\scantokens#1{%
+ \toks0={#1}%
+ \immediate\openout\macscribble=\jobname.tmp
+ \immediate\write\macscribble{\the\toks0}%
+ \immediate\closeout\macscribble
+ \input \jobname.tmp
+ }
+\fi
+
+\def\scanmacro#1{%
+ \begingroup
+ \newlinechar`\^^M
+ \let\xeatspaces\eatspaces
+ % Undo catcode changes of \startcontents and \doprintindex
+ % When called from @insertcopying or (short)caption, we need active
+ % backslash to get it printed correctly. Previously, we had
+ % \catcode`\\=\other instead. We'll see whether a problem appears
+ % with macro expansion. --kasal, 19aug04
+ \catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+ % ... and \example
+ \spaceisspace
+ %
+ % Append \endinput to make sure that TeX does not see the ending newline.
+ % I've verified that it is necessary both for e-TeX and for ordinary TeX
+ % --kasal, 29nov03
+ \scantokens{#1\endinput}%
+ \endgroup
+}
+
+\def\scanexp#1{%
+ \edef\temp{\noexpand\scanmacro{#1}}%
+ \temp
+}
+
+\newcount\paramno % Count of parameters
+\newtoks\macname % Macro name
+\newif\ifrecursive % Is it recursive?
+
+% List of all defined macros in the form
+% \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+ \toks0 = \expandafter{\macrolist\definedummyword#1}%
+ \xdef\macrolist{\the\toks0}%
+}
+
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+% \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+%
+\def\cslet#1#2{%
+ \expandafter\let
+ \csname#1\expandafter\endcsname
+ \csname#2\endcsname
+}
+
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
+
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
+
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \.
+
+% Non-ASCII encodings make 8-bit characters active, so un-activate
+% them to avoid their expansion. Must do this non-globally, to
+% confine the change to the current group.
+
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+
+\def\scanctxt{%
+ \catcode`\"=\other
+ \catcode`\+=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\@=\other
+ \catcode`\^=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\~=\other
+ \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi
+}
+
+\def\scanargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+ \catcode`\^^M=\other
+}
+
+\def\macrobodyctxt{%
+ \scanctxt
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\^^M=\other
+ \usembodybackslash
+}
+
+\def\macroargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+}
+
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
+
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+ \getargs{#1}% now \macname is the macname and \argl the arglist
+ \ifx\argl\empty % no arguments
+ \paramno=0%
+ \else
+ \expandafter\parsemargdef \argl;%
+ \fi
+ \if1\csname ismacro.\the\macname\endcsname
+ \message{Warning: redefining \the\macname}%
+ \else
+ \expandafter\ifx\csname \the\macname\endcsname \relax
+ \else \errmessage{Macro name \the\macname\space already defined}\fi
+ \global\cslet{macsave.\the\macname}{\the\macname}%
+ \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+ \addtomacrolist{\the\macname}%
+ \fi
+ \begingroup \macrobodyctxt
+ \ifrecursive \expandafter\parsermacbody
+ \else \expandafter\parsemacbody
+ \fi}
+
+\parseargdef\unmacro{%
+ \if1\csname ismacro.#1\endcsname
+ \global\cslet{#1}{macsave.#1}%
+ \global\expandafter\let \csname ismacro.#1\endcsname=0%
+ % Remove the macro name from \macrolist:
+ \begingroup
+ \expandafter\let\csname#1\endcsname \relax
+ \let\definedummyword\unmacrodo
+ \xdef\macrolist{\macrolist}%
+ \endgroup
+ \else
+ \errmessage{Macro #1 not defined}%
+ \fi
+}
+
+% Called by \do from \dounmacro on each macro. The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+ \ifx #1\relax
+ % remove this
+ \else
+ \noexpand\definedummyword \noexpand#1%
+ \fi
+}
+
+% This makes use of the obscure feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname #1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+
+% Parse the optional {params} list. Set up \paramno and \paramlist
+% so \defmacro knows what to do. Define \macarg.blah for each blah
+% in the params list, to be ##N where N is the position in that list.
+% That gets used by \mbodybackslash (above).
+
+% We need to get `macro parameter char #' into several definitions.
+% The technique used is stolen from LaTeX: let \hash be something
+% unexpandable, insert that wherever you need a #, and then redefine
+% it to # just before using the token list produced.
+%
+% The same technique is used to protect \eatspaces till just before
+% the macro is used.
+
+\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
+ \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
+\def\parsemargdefxxx#1,{%
+ \if#1;\let\next=\relax
+ \else \let\next=\parsemargdefxxx
+ \advance\paramno by 1%
+ \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+ {\xeatspaces{\hash\the\paramno}}%
+ \edef\paramlist{\paramlist\hash\the\paramno,}%
+ \fi\next}
+
+% These two commands read recursive and nonrecursive macro bodies.
+% (They're different since rec and nonrec macros end differently.)
+
+\long\def\parsemacbody#1@end macro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+\long\def\parsermacbody#1@end rmacro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+
+% This defines the macro itself. There are six cases: recursive and
+% nonrecursive macros of zero, one, and many arguments.
+% Much magic with \expandafter here.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in; @include reads the file inside a group.
+\def\defmacro{%
+ \let\hash=##% convert placeholders to macro parameter chars
+ \ifrecursive
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\scanmacro{\temp}}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup\noexpand\scanmacro{\temp}}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{\egroup\noexpand\scanmacro{\temp}}%
+ \fi
+ \else
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \expandafter\noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \fi
+ \fi}
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+% \braceorline decides whether the next nonwhitespace character is a
+% {. If so it reads up to the closing }, if not, it reads the whole
+% line. Whatever was read is then fed to the next control sequence
+% as an argument (by \parsebrace or \parsearg)
+\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+ \ifx\nchar\bgroup\else
+ \expandafter\parsearg
+ \fi \macnamexxx}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign. Just make them active and then expand them all to nothing.
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+ {%
+ \expandafter\let\obeyedspace=\empty
+ \addtomacrolist{#1}%
+ \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+ }%
+ \next
+}
+
+
+\message{cross references,}
+
+\newwrite\auxfile
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references. The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross, , , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
+
+\let\nwnode=\node
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node. #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+ \ifx\lastnode\empty\else
+ \setref{\lastnode}{#1}%
+ \global\let\lastnode=\empty
+ \fi
+}
+
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \lastsection,
+% or the anchor name.
+% 2) NAME-snt - section number and type, passed as the SNT arg, or
+% empty for anchors.
+% 3) NAME-pg - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat. In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+ \pdfmkdest{#1}%
+ \iflinks
+ {%
+ \atdummies % preserve commands, but don't expand them
+ \edef\writexrdef##1##2{%
+ \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+ ##1}{##2}}% these are parameters of \writexrdef
+ }%
+ \toks0 = \expandafter{\lastsection}%
+ \immediate \writexrdef{title}{\the\toks0 }%
+ \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+ \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, during \shipout
+ }%
+ \fi
+}
+
+% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual. All but the node name can be omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \unsepspaces
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printedrefname{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual\unskip}%
+ \setbox0=\hbox{\printedrefname\unskip}%
+ \ifdim \wd0 = 0pt
+ % No printed node name was explicitly given.
+ \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+ % Use the node name inside the square brackets.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ % Use the actual chapter/section title appear inside
+ % the square brackets. Use the real section title if we have it.
+ \ifdim \wd1 > 0pt
+ % It is in another manual, so we don't have it.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printedrefname{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printedrefname{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % Make link in pdf output.
+ \ifpdf
+ {\indexnofonts
+ \turnoffactive
+ % This expands tokens, so do it after making catcode changes, so _
+ % etc. don't get their TeX definitions.
+ \getfilename{#4}%
+ %
+ % See comments at \activebackslashdouble.
+ {\activebackslashdouble \xdef\pdfxrefdest{#1}%
+ \backslashparens\pdfxrefdest}%
+ %
+ \leavevmode
+ \startlink attr{/Border [0 0 0]}%
+ \ifnum\filenamelength>0
+ goto file{\the\filename.pdf} name{\pdfxrefdest}%
+ \else
+ goto name{\pdfmkpgn{\pdfxrefdest}}%
+ \fi
+ }%
+ \setcolor{\linkcolor}%
+ \fi
+ %
+ % Float references are printed completely differently: "Figure 1.2"
+ % instead of "[somenode], p.3". We distinguish them by the
+ % LABEL-title being set to a magic string.
+ {%
+ % Have to otherify everything special to allow the \csname to
+ % include an _ in the xref name, etc.
+ \indexnofonts
+ \turnoffactive
+ \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+ \csname XR#1-title\endcsname
+ }%
+ \iffloat\Xthisreftitle
+ % If the user specified the print name (third arg) to the ref,
+ % print it instead of our usual "Figure 1.2".
+ \ifdim\wd0 = 0pt
+ \refx{#1-snt}{}%
+ \else
+ \printedrefname
+ \fi
+ %
+ % if the user also gave the printed manual name (fifth arg), append
+ % "in MANUALNAME".
+ \ifdim \wd1 > 0pt
+ \space \putwordin{} \cite{\printedmanual}%
+ \fi
+ \else
+ % node/anchor (non-float) references.
+ %
+ % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+ % insert empty discretionaries after hyphens, which means that it will
+ % not find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens, this
+ % is a loss. Therefore, we give the text of the node name again, so it
+ % is as if TeX is seeing it for the first time.
+ \ifdim \wd1 > 0pt
+ \putwordSection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}%
+ \else
+ % _ (for example) has to be the character _ for the purposes of the
+ % control sequence corresponding to the node, but it has to expand
+ % into the usual \leavevmode...\vrule stuff for purposes of
+ % printing. So we \turnoffactive for the \refx-snt, back on for the
+ % printing, back off for the \refx-pg.
+ {\turnoffactive
+ % Only output a following space if the -snt ref is nonempty; for
+ % @unnumbered and @anchor, it won't be.
+ \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+ \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+ }%
+ % output the `[mynode]' via a macro so it can be overridden.
+ \xrefprintnodename\printedrefname
+ %
+ % But we always want a comma and a space:
+ ,\space
+ %
+ % output the `page 3'.
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output. It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents. Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
+
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+ \ifnum\secno=0
+ \putwordChapter@tie \the\chapno
+ \else \ifnum\subsecno=0
+ \putwordSection@tie \the\chapno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+\def\Yappendix{%
+ \ifnum\secno=0
+ \putwordAppendix@tie @char\the\appendixno{}%
+ \else \ifnum\subsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie
+ @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+%
+\def\refx#1#2{%
+ {%
+ \indexnofonts
+ \otherbackslash
+ \expandafter\global\expandafter\let\expandafter\thisrefX
+ \csname XR#1\endcsname
+ }%
+ \ifx\thisrefX\relax
+ % If not defined, say something at least.
+ \angleleft un\-de\-fined\angleright
+ \iflinks
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \thisrefX
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file. Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions). But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+ {% The node name might contain 8-bit characters, which in our current
+ % implementation are changed to commands like @'e. Don't let these
+ % mess up the control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safexrefname{#1}%
+ }%
+ %
+ \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref
+ %
+ % Was that xref control sequence that we just defined for a float?
+ \expandafter\iffloat\csname XR\safexrefname\endcsname
+ % it was a float, and we have the (safe) float type in \iffloattype.
+ \expandafter\let\expandafter\floatlist
+ \csname floatlist\iffloattype\endcsname
+ %
+ % Is this the first time we've seen this float type?
+ \expandafter\ifx\floatlist\relax
+ \toks0 = {\do}% yes, so just \do
+ \else
+ % had it before, so preserve previous elements in list.
+ \toks0 = \expandafter{\floatlist\do}%
+ \fi
+ %
+ % Remember this xref in the control sequence \floatlistFLOATTYPE,
+ % for later use in \listoffloats.
+ \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0
+ {\safexrefname}}%
+ \fi
+}
+
+% Read the last existing aux file, if any. No error if none exists.
+%
+\def\tryauxfile{%
+ \openin 1 \jobname.aux
+ \ifeof 1 \else
+ \readdatafile{aux}%
+ \global\havexrefstrue
+ \fi
+ \closein 1
+}
+
+\def\setupdatafile{%
+ \catcode`\^^@=\other
+ \catcode`\^^A=\other
+ \catcode`\^^B=\other
+ \catcode`\^^C=\other
+ \catcode`\^^D=\other
+ \catcode`\^^E=\other
+ \catcode`\^^F=\other
+ \catcode`\^^G=\other
+ \catcode`\^^H=\other
+ \catcode`\^^K=\other
+ \catcode`\^^L=\other
+ \catcode`\^^N=\other
+ \catcode`\^^P=\other
+ \catcode`\^^Q=\other
+ \catcode`\^^R=\other
+ \catcode`\^^S=\other
+ \catcode`\^^T=\other
+ \catcode`\^^U=\other
+ \catcode`\^^V=\other
+ \catcode`\^^W=\other
+ \catcode`\^^X=\other
+ \catcode`\^^Z=\other
+ \catcode`\^^[=\other
+ \catcode`\^^\=\other
+ \catcode`\^^]=\other
+ \catcode`\^^^=\other
+ \catcode`\^^_=\other
+ % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
+ % in xref tags, i.e., node names. But since ^^e4 notation isn't
+ % supported in the main text, it doesn't seem desirable. Furthermore,
+ % that is not enough: for node names that actually contain a ^
+ % character, we would end up writing a line like this: 'xrdef {'hat
+ % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+ % argument, and \hat is not an expandable control sequence. It could
+ % all be worked out, but why? Either we support ^^ or we don't.
+ %
+ % The other change necessary for this was to define \auxhat:
+ % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+ % and then to call \auxhat in \setq.
+ %
+ \catcode`\^=\other
+ %
+ % Special characters. Should be turned off anyway, but...
+ \catcode`\~=\other
+ \catcode`\[=\other
+ \catcode`\]=\other
+ \catcode`\"=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\$=\other
+ \catcode`\#=\other
+ \catcode`\&=\other
+ \catcode`\%=\other
+ \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+ %
+ % This is to support \ in node names and titles, since the \
+ % characters end up in a \csname. It's easier than
+ % leaving it active and making its active definition an actual \
+ % character. What I don't understand is why it works in the *value*
+ % of the xrdef. Seems like it should be a catcode12 \, and that
+ % should not typeset properly. But it works, so I'm moving on for
+ % now. --karl, 15jan04.
+ \catcode`\\=\other
+ %
+ % Make the characters 128-255 be printing characters.
+ {%
+ \count1=128
+ \def\loop{%
+ \catcode\count1=\other
+ \advance\count1 by 1
+ \ifnum \count1<256 \loop \fi
+ }%
+ }%
+ %
+ % @ is our escape character in .aux files, and we need braces.
+ \catcode`\{=1
+ \catcode`\}=2
+ \catcode`\@=0
+}
+
+\def\readdatafile#1{%
+\begingroup
+ \setupdatafile
+ \input\jobname.#1
+\endgroup}
+
+
+\message{insertions,}
+% including footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only.
+\let\footnotestyle=\comment
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \dofootnote
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read. --karl, 16nov96.
+%
+\gdef\dofootnote{%
+ \insert\footins\bgroup
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \hsize=\pagewidth
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ \smallfonts \rm
+ %
+ % Because we use hanging indentation in footnotes, a @noindent appears
+ % to exdent this text, so make it be a no-op. makeinfo does not use
+ % hanging indentation so @noindent can still be needed within footnote
+ % text after an @example or the like (not that this is good style).
+ \let\noindent = \relax
+ %
+ % Hang the footnote text off the number. Use \everypar in case the
+ % footnote extends for more than one paragraph.
+ \everypar = {\hang}%
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ \futurelet\next\fo@t
+}
+}%end \catcode `\@=11
+
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished. Otherwise, the insertion
+% would be lost.
+% Similarly, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes. --kasal, 16nov03.
+
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+ \ifx \insert\ptexinsert
+ \let\insert\saveinsert
+ \else
+ \let\checkinserts\relax
+ \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+ \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+ \afterassignment\next
+ % swallow the left brace
+ \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+ \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+ {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+ \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-)
+ \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+ \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+ \next
+}
+\def\newsaveinsX #1{%
+ \csname newbox\endcsname #1%
+ \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+ \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image. We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front. If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+ % Do not bother showing banner with epsf.tex v2.7k (available in
+ % doc/epsf.tex and on ctan).
+ \def\epsfannounce{\toks0 = }%
+ \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+ work. It is also included in the Texinfo distribution, or you can get
+ it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+ \ifx\epsfbox\undefined
+ \ifwarnednoepsf \else
+ \errhelp = \noepsfhelp
+ \errmessage{epsf.tex not found, images will be ignored}%
+ \global\warnednoepsftrue
+ \fi
+ \else
+ \imagexxx #1,,,,,\finish
+ \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing this stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+ \catcode`\^^M = 5 % in case we're inside an example
+ \normalturnoffactive % allow _ et al. in names
+ % If the image is by itself, center it.
+ \ifvmode
+ \imagevmodetrue
+ \nobreak\medskip
+ % Usually we'll have text after the image which will insert
+ % \parskip glue, so insert it here too to equalize the space
+ % above and below.
+ \nobreak\vskip\parskip
+ \nobreak
+ \fi
+ %
+ % Leave vertical mode so that indentation from an enclosing
+ % environment such as @quotation is respected. On the other hand, if
+ % it's at the top level, we don't want the normal paragraph indentation.
+ \noindent
+ %
+ % Output the image.
+ \ifpdf
+ \dopdfimage{#1}{#2}{#3}%
+ \else
+ % \epsfbox itself resets \epsf?size at each figure.
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+ \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+ \epsfbox{#1.eps}%
+ \fi
+ %
+ \ifimagevmode \medskip \fi % space after the standalone image
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc. We don't actually implement floating yet, we always include the
+% float "here". But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc. Can't contain commas. If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label. Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored. It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+ \let\thiscaption=\empty
+ \let\thisshortcaption=\empty
+ %
+ % don't lose footnotes inside @float.
+ %
+ % BEWARE: when the floats start float, we have to issue warning whenever an
+ % insert appears inside a float which could possibly float. --kasal, 26may04
+ %
+ \startsavinginserts
+ %
+ % We can't be used inside a paragraph.
+ \par
+ %
+ \vtop\bgroup
+ \def\floattype{#1}%
+ \def\floatlabel{#2}%
+ \def\floatloc{#3}% we do nothing with this yet.
+ %
+ \ifx\floattype\empty
+ \let\safefloattype=\empty
+ \else
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ \fi
+ %
+ % If label is given but no type, we handle that as the empty type.
+ \ifx\floatlabel\empty \else
+ % We want each FLOATTYPE to be numbered separately (Figure 1,
+ % Table 1, Figure 2, ...). (And if no label, no number.)
+ %
+ \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+ \global\advance\floatno by 1
+ %
+ {%
+ % This magic value for \lastsection is output by \setref as the
+ % XREFLABEL-title value. \xrefX uses it to distinguish float
+ % labels (which have a completely different output format) from
+ % node and anchor labels. And \xrdef uses it to construct the
+ % lists of floats.
+ %
+ \edef\lastsection{\floatmagic=\safefloattype}%
+ \setref{\floatlabel}{Yfloat}%
+ }%
+ \fi
+ %
+ % start with \parskip glue, I guess.
+ \vskip\parskip
+ %
+ % Don't suppress indentation if a float happens to start a section.
+ \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption: Foo 1.1
+% @float Foo & @caption{Cap}: Foo: Cap
+% @float Foo & no caption: Foo
+% @float ,lbl & Caption{Cap}: 1.1: Cap
+% @float ,lbl & no caption: 1.1
+% @float & @caption{Cap}: Cap
+% @float & no caption:
+%
+\def\Efloat{%
+ \let\floatident = \empty
+ %
+ % In all cases, if we have a float type, it comes first.
+ \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+ %
+ % If we have an xref label, the number comes next.
+ \ifx\floatlabel\empty \else
+ \ifx\floattype\empty \else % if also had float type, need tie first.
+ \appendtomacro\floatident{\tie}%
+ \fi
+ % the number.
+ \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+ \fi
+ %
+ % Start the printed caption with what we've constructed in
+ % \floatident, but keep it separate; we need \floatident again.
+ \let\captionline = \floatident
+ %
+ \ifx\thiscaption\empty \else
+ \ifx\floatident\empty \else
+ \appendtomacro\captionline{: }% had ident, so need a colon between
+ \fi
+ %
+ % caption text.
+ \appendtomacro\captionline{\scanexp\thiscaption}%
+ \fi
+ %
+ % If we have anything to print, print it, with space before.
+ % Eventually this needs to become an \insert.
+ \ifx\captionline\empty \else
+ \vskip.5\parskip
+ \captionline
+ %
+ % Space below caption.
+ \vskip\parskip
+ \fi
+ %
+ % If have an xref label, write the list of floats info. Do this
+ % after the caption, to avoid chance of it being a breakpoint.
+ \ifx\floatlabel\empty \else
+ % Write the text that goes in the lof to the aux file as
+ % \floatlabel-lof. Besides \floatident, we include the short
+ % caption if specified, else the full caption if specified, else nothing.
+ {%
+ \atdummies
+ %
+ % since we read the caption text in the macro world, where ^^M
+ % is turned into a normal character, we have to scan it back, so
+ % we don't write the literal three characters "^^M" into the aux file.
+ \scanexp{%
+ \xdef\noexpand\gtemp{%
+ \ifx\thisshortcaption\empty
+ \thiscaption
+ \else
+ \thisshortcaption
+ \fi
+ }%
+ }%
+ \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+ \ifx\gtemp\empty \else : \gtemp \fi}}%
+ }%
+ \fi
+ \egroup % end of \vtop
+ %
+ % place the captured inserts
+ %
+ % BEWARE: when the floats start floating, we have to issue warning
+ % whenever an insert appears inside a float which could possibly
+ % float. --kasal, 26may04
+ %
+ \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+ \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use. Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+ \ifx#1\relax
+ % Haven't seen this figure type before.
+ \csname newcount\endcsname #1%
+ %
+ % Remember to reset this floatno at the next chap.
+ \expandafter\gdef\expandafter\resetallfloatnos
+ \expandafter{\resetallfloatnos #1=0 }%
+ \fi
+ \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value. We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref. That is, the magic
+% \lastsection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string. If so, #2 will be the
+% (safe) float type for this float. We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+ \def\temp{#1}%
+ \def\iffloattype{#2}%
+ \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+ \def\floattype{#1}% floattype
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ %
+ % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+ \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+ \ifhavexrefs
+ % if the user said @listoffloats foo but never @float foo.
+ \message{\linenumber No `\safefloattype' floats to list.}%
+ \fi
+ \else
+ \begingroup
+ \leftskip=\tocindent % indent these entries like a toc
+ \let\do=\listoffloatsdo
+ \csname floatlist\safefloattype\endcsname
+ \endgroup
+ \fi
+}
+
+% This is called on each entry in a list of floats. We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file. We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+ % Can't fully expand XR#1-lof because it can contain anything. Just
+ % pass the control sequence. On the other hand, XR#1-pg is just the
+ % page number, and we want to fully expand that so we can get a link
+ % in pdf output.
+ \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+ %
+ % use the same \entry macro we use to generate the TOC and index.
+ \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+ \writeentry
+}}
+
+
+\message{localization,}
+
+% For single-language documents, @documentlanguage is usually given very
+% early, just after @documentencoding. Single argument is the language
+% (de) or locale (de_DE) abbreviation.
+%
+{
+ \catcode`\_ = \active
+ \globaldefs=1
+\parseargdef\documentlanguage{\begingroup
+ \let_=\normalunderscore % normal _ character for filenames
+ \tex % read txi-??.tex file in plain TeX.
+ % Read the file by the name they passed if it exists.
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \documentlanguagetrywithoutunderscore{#1_\finish}%
+ \else
+ \globaldefs = 1 % everything in the txi-LL files needs to persist
+ \input txi-#1.tex
+ \fi
+ \closein 1
+ \endgroup % end raw TeX
+\endgroup}
+%
+% If they passed de_DE, and txi-de_DE.tex doesn't exist,
+% try txi-de.tex.
+%
+\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{%
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \errhelp = \nolanghelp
+ \errmessage{Cannot read language file txi-#1.tex}%
+ \else
+ \globaldefs = 1 % everything in the txi-LL files needs to persist
+ \input txi-#1.tex
+ \fi
+ \closein 1
+}
+}% end of special _ catcode
+%
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty. Maybe you need to install it? Putting it in the current
+directory should work if nowhere else does.}
+
+% This macro is called from txi-??.tex files; the first argument is the
+% \language name to set (without the "\lang@" prefix), the second and
+% third args are \{left,right}hyphenmin.
+%
+% The language names to pass are determined when the format is built.
+% See the etex.log file created at that time, e.g.,
+% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log.
+%
+% With TeX Live 2008, etex now includes hyphenation patterns for all
+% available languages. This means we can support hyphenation in
+% Texinfo, at least to some extent. (This still doesn't solve the
+% accented characters problem.)
+%
+\catcode`@=11
+\def\txisetlanguage#1#2#3{%
+ % do not set the language if the name is undefined in the current TeX.
+ \expandafter\ifx\csname lang@#1\endcsname \relax
+ \message{no patterns for #1}%
+ \else
+ \global\language = \csname lang@#1\endcsname
+ \fi
+ % but there is no harm in adjusting the hyphenmin values regardless.
+ \global\lefthyphenmin = #2\relax
+ \global\righthyphenmin = #3\relax
+}
+
+% Helpers for encodings.
+% Set the catcode of characters 128 through 255 to the specified number.
+%
+\def\setnonasciicharscatcode#1{%
+ \count255=128
+ \loop\ifnum\count255<256
+ \global\catcode\count255=#1\relax
+ \advance\count255 by 1
+ \repeat
+}
+
+\def\setnonasciicharscatcodenonglobal#1{%
+ \count255=128
+ \loop\ifnum\count255<256
+ \catcode\count255=#1\relax
+ \advance\count255 by 1
+ \repeat
+}
+
+% @documentencoding sets the definition of non-ASCII characters
+% according to the specified encoding.
+%
+\parseargdef\documentencoding{%
+ % Encoding being declared for the document.
+ \def\declaredencoding{\csname #1.enc\endcsname}%
+ %
+ % Supported encodings: names converted to tokens in order to be able
+ % to compare them with \ifx.
+ \def\ascii{\csname US-ASCII.enc\endcsname}%
+ \def\latnine{\csname ISO-8859-15.enc\endcsname}%
+ \def\latone{\csname ISO-8859-1.enc\endcsname}%
+ \def\lattwo{\csname ISO-8859-2.enc\endcsname}%
+ \def\utfeight{\csname UTF-8.enc\endcsname}%
+ %
+ \ifx \declaredencoding \ascii
+ \asciichardefs
+ %
+ \else \ifx \declaredencoding \lattwo
+ \setnonasciicharscatcode\active
+ \lattwochardefs
+ %
+ \else \ifx \declaredencoding \latone
+ \setnonasciicharscatcode\active
+ \latonechardefs
+ %
+ \else \ifx \declaredencoding \latnine
+ \setnonasciicharscatcode\active
+ \latninechardefs
+ %
+ \else \ifx \declaredencoding \utfeight
+ \setnonasciicharscatcode\active
+ \utfeightchardefs
+ %
+ \else
+ \message{Unknown document encoding #1, ignoring.}%
+ %
+ \fi % utfeight
+ \fi % latnine
+ \fi % latone
+ \fi % lattwo
+ \fi % ascii
+}
+
+% A message to be logged when using a character that isn't available
+% the default font encoding (OT1).
+%
+\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}}
+
+% Take account of \c (plain) vs. \, (Texinfo) difference.
+\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi}
+
+% First, make active non-ASCII characters in order for them to be
+% correctly categorized when TeX reads the replacement text of
+% macros containing the character definitions.
+\setnonasciicharscatcode\active
+%
+% Latin1 (ISO-8859-1) character definitions.
+\def\latonechardefs{%
+ \gdef^^a0{~}
+ \gdef^^a1{\exclamdown}
+ \gdef^^a2{\missingcharmsg{CENT SIGN}}
+ \gdef^^a3{{\pounds}}
+ \gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
+ \gdef^^a5{\missingcharmsg{YEN SIGN}}
+ \gdef^^a6{\missingcharmsg{BROKEN BAR}}
+ \gdef^^a7{\S}
+ \gdef^^a8{\"{}}
+ \gdef^^a9{\copyright}
+ \gdef^^aa{\ordf}
+ \gdef^^ab{\guillemetleft}
+ \gdef^^ac{$\lnot$}
+ \gdef^^ad{\-}
+ \gdef^^ae{\registeredsymbol}
+ \gdef^^af{\={}}
+ %
+ \gdef^^b0{\textdegree}
+ \gdef^^b1{$\pm$}
+ \gdef^^b2{$^2$}
+ \gdef^^b3{$^3$}
+ \gdef^^b4{\'{}}
+ \gdef^^b5{$\mu$}
+ \gdef^^b6{\P}
+ %
+ \gdef^^b7{$^.$}
+ \gdef^^b8{\cedilla\ }
+ \gdef^^b9{$^1$}
+ \gdef^^ba{\ordm}
+ %
+ \gdef^^bb{\guilletright}
+ \gdef^^bc{$1\over4$}
+ \gdef^^bd{$1\over2$}
+ \gdef^^be{$3\over4$}
+ \gdef^^bf{\questiondown}
+ %
+ \gdef^^c0{\`A}
+ \gdef^^c1{\'A}
+ \gdef^^c2{\^A}
+ \gdef^^c3{\~A}
+ \gdef^^c4{\"A}
+ \gdef^^c5{\ringaccent A}
+ \gdef^^c6{\AE}
+ \gdef^^c7{\cedilla C}
+ \gdef^^c8{\`E}
+ \gdef^^c9{\'E}
+ \gdef^^ca{\^E}
+ \gdef^^cb{\"E}
+ \gdef^^cc{\`I}
+ \gdef^^cd{\'I}
+ \gdef^^ce{\^I}
+ \gdef^^cf{\"I}
+ %
+ \gdef^^d0{\DH}
+ \gdef^^d1{\~N}
+ \gdef^^d2{\`O}
+ \gdef^^d3{\'O}
+ \gdef^^d4{\^O}
+ \gdef^^d5{\~O}
+ \gdef^^d6{\"O}
+ \gdef^^d7{$\times$}
+ \gdef^^d8{\O}
+ \gdef^^d9{\`U}
+ \gdef^^da{\'U}
+ \gdef^^db{\^U}
+ \gdef^^dc{\"U}
+ \gdef^^dd{\'Y}
+ \gdef^^de{\TH}
+ \gdef^^df{\ss}
+ %
+ \gdef^^e0{\`a}
+ \gdef^^e1{\'a}
+ \gdef^^e2{\^a}
+ \gdef^^e3{\~a}
+ \gdef^^e4{\"a}
+ \gdef^^e5{\ringaccent a}
+ \gdef^^e6{\ae}
+ \gdef^^e7{\cedilla c}
+ \gdef^^e8{\`e}
+ \gdef^^e9{\'e}
+ \gdef^^ea{\^e}
+ \gdef^^eb{\"e}
+ \gdef^^ec{\`{\dotless i}}
+ \gdef^^ed{\'{\dotless i}}
+ \gdef^^ee{\^{\dotless i}}
+ \gdef^^ef{\"{\dotless i}}
+ %
+ \gdef^^f0{\dh}
+ \gdef^^f1{\~n}
+ \gdef^^f2{\`o}
+ \gdef^^f3{\'o}
+ \gdef^^f4{\^o}
+ \gdef^^f5{\~o}
+ \gdef^^f6{\"o}
+ \gdef^^f7{$\div$}
+ \gdef^^f8{\o}
+ \gdef^^f9{\`u}
+ \gdef^^fa{\'u}
+ \gdef^^fb{\^u}
+ \gdef^^fc{\"u}
+ \gdef^^fd{\'y}
+ \gdef^^fe{\th}
+ \gdef^^ff{\"y}
+}
+
+% Latin9 (ISO-8859-15) encoding character definitions.
+\def\latninechardefs{%
+ % Encoding is almost identical to Latin1.
+ \latonechardefs
+ %
+ \gdef^^a4{\euro}
+ \gdef^^a6{\v S}
+ \gdef^^a8{\v s}
+ \gdef^^b4{\v Z}
+ \gdef^^b8{\v z}
+ \gdef^^bc{\OE}
+ \gdef^^bd{\oe}
+ \gdef^^be{\"Y}
+}
+
+% Latin2 (ISO-8859-2) character definitions.
+\def\lattwochardefs{%
+ \gdef^^a0{~}
+ \gdef^^a1{\ogonek{A}}
+ \gdef^^a2{\u{}}
+ \gdef^^a3{\L}
+ \gdef^^a4{\missingcharmsg{CURRENCY SIGN}}
+ \gdef^^a5{\v L}
+ \gdef^^a6{\'S}
+ \gdef^^a7{\S}
+ \gdef^^a8{\"{}}
+ \gdef^^a9{\v S}
+ \gdef^^aa{\cedilla S}
+ \gdef^^ab{\v T}
+ \gdef^^ac{\'Z}
+ \gdef^^ad{\-}
+ \gdef^^ae{\v Z}
+ \gdef^^af{\dotaccent Z}
+ %
+ \gdef^^b0{\textdegree}
+ \gdef^^b1{\ogonek{a}}
+ \gdef^^b2{\ogonek{ }}
+ \gdef^^b3{\l}
+ \gdef^^b4{\'{}}
+ \gdef^^b5{\v l}
+ \gdef^^b6{\'s}
+ \gdef^^b7{\v{}}
+ \gdef^^b8{\cedilla\ }
+ \gdef^^b9{\v s}
+ \gdef^^ba{\cedilla s}
+ \gdef^^bb{\v t}
+ \gdef^^bc{\'z}
+ \gdef^^bd{\H{}}
+ \gdef^^be{\v z}
+ \gdef^^bf{\dotaccent z}
+ %
+ \gdef^^c0{\'R}
+ \gdef^^c1{\'A}
+ \gdef^^c2{\^A}
+ \gdef^^c3{\u A}
+ \gdef^^c4{\"A}
+ \gdef^^c5{\'L}
+ \gdef^^c6{\'C}
+ \gdef^^c7{\cedilla C}
+ \gdef^^c8{\v C}
+ \gdef^^c9{\'E}
+ \gdef^^ca{\ogonek{E}}
+ \gdef^^cb{\"E}
+ \gdef^^cc{\v E}
+ \gdef^^cd{\'I}
+ \gdef^^ce{\^I}
+ \gdef^^cf{\v D}
+ %
+ \gdef^^d0{\DH}
+ \gdef^^d1{\'N}
+ \gdef^^d2{\v N}
+ \gdef^^d3{\'O}
+ \gdef^^d4{\^O}
+ \gdef^^d5{\H O}
+ \gdef^^d6{\"O}
+ \gdef^^d7{$\times$}
+ \gdef^^d8{\v R}
+ \gdef^^d9{\ringaccent U}
+ \gdef^^da{\'U}
+ \gdef^^db{\H U}
+ \gdef^^dc{\"U}
+ \gdef^^dd{\'Y}
+ \gdef^^de{\cedilla T}
+ \gdef^^df{\ss}
+ %
+ \gdef^^e0{\'r}
+ \gdef^^e1{\'a}
+ \gdef^^e2{\^a}
+ \gdef^^e3{\u a}
+ \gdef^^e4{\"a}
+ \gdef^^e5{\'l}
+ \gdef^^e6{\'c}
+ \gdef^^e7{\cedilla c}
+ \gdef^^e8{\v c}
+ \gdef^^e9{\'e}
+ \gdef^^ea{\ogonek{e}}
+ \gdef^^eb{\"e}
+ \gdef^^ec{\v e}
+ \gdef^^ed{\'\i}
+ \gdef^^ee{\^\i}
+ \gdef^^ef{\v d}
+ %
+ \gdef^^f0{\dh}
+ \gdef^^f1{\'n}
+ \gdef^^f2{\v n}
+ \gdef^^f3{\'o}
+ \gdef^^f4{\^o}
+ \gdef^^f5{\H o}
+ \gdef^^f6{\"o}
+ \gdef^^f7{$\div$}
+ \gdef^^f8{\v r}
+ \gdef^^f9{\ringaccent u}
+ \gdef^^fa{\'u}
+ \gdef^^fb{\H u}
+ \gdef^^fc{\"u}
+ \gdef^^fd{\'y}
+ \gdef^^fe{\cedilla t}
+ \gdef^^ff{\dotaccent{}}
+}
+
+% UTF-8 character definitions.
+%
+% This code to support UTF-8 is based on LaTeX's utf8.def, with some
+% changes for Texinfo conventions. It is included here under the GPL by
+% permission from Frank Mittelbach and the LaTeX team.
+%
+\newcount\countUTFx
+\newcount\countUTFy
+\newcount\countUTFz
+
+\gdef\UTFviiiTwoOctets#1#2{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\endcsname}
+%
+\gdef\UTFviiiThreeOctets#1#2#3{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname}
+%
+\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter
+ \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname}
+
+\gdef\UTFviiiDefined#1{%
+ \ifx #1\relax
+ \message{\linenumber Unicode char \string #1 not defined for Texinfo}%
+ \else
+ \expandafter #1%
+ \fi
+}
+
+\begingroup
+ \catcode`\~13
+ \catcode`\"12
+
+ \def\UTFviiiLoop{%
+ \global\catcode\countUTFx\active
+ \uccode`\~\countUTFx
+ \uppercase\expandafter{\UTFviiiTmp}%
+ \advance\countUTFx by 1
+ \ifnum\countUTFx < \countUTFy
+ \expandafter\UTFviiiLoop
+ \fi}
+
+ \countUTFx = "C2
+ \countUTFy = "E0
+ \def\UTFviiiTmp{%
+ \xdef~{\noexpand\UTFviiiTwoOctets\string~}}
+ \UTFviiiLoop
+
+ \countUTFx = "E0
+ \countUTFy = "F0
+ \def\UTFviiiTmp{%
+ \xdef~{\noexpand\UTFviiiThreeOctets\string~}}
+ \UTFviiiLoop
+
+ \countUTFx = "F0
+ \countUTFy = "F4
+ \def\UTFviiiTmp{%
+ \xdef~{\noexpand\UTFviiiFourOctets\string~}}
+ \UTFviiiLoop
+\endgroup
+
+\begingroup
+ \catcode`\"=12
+ \catcode`\<=12
+ \catcode`\.=12
+ \catcode`\,=12
+ \catcode`\;=12
+ \catcode`\!=12
+ \catcode`\~=13
+
+ \gdef\DeclareUnicodeCharacter#1#2{%
+ \countUTFz = "#1\relax
+ \wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}%
+ \begingroup
+ \parseXMLCharref
+ \def\UTFviiiTwoOctets##1##2{%
+ \csname u8:##1\string ##2\endcsname}%
+ \def\UTFviiiThreeOctets##1##2##3{%
+ \csname u8:##1\string ##2\string ##3\endcsname}%
+ \def\UTFviiiFourOctets##1##2##3##4{%
+ \csname u8:##1\string ##2\string ##3\string ##4\endcsname}%
+ \expandafter\expandafter\expandafter\expandafter
+ \expandafter\expandafter\expandafter
+ \gdef\UTFviiiTmp{#2}%
+ \endgroup}
+
+ \gdef\parseXMLCharref{%
+ \ifnum\countUTFz < "A0\relax
+ \errhelp = \EMsimple
+ \errmessage{Cannot define Unicode char value < 00A0}%
+ \else\ifnum\countUTFz < "800\relax
+ \parseUTFviiiA,%
+ \parseUTFviiiB C\UTFviiiTwoOctets.,%
+ \else\ifnum\countUTFz < "10000\relax
+ \parseUTFviiiA;%
+ \parseUTFviiiA,%
+ \parseUTFviiiB E\UTFviiiThreeOctets.{,;}%
+ \else
+ \parseUTFviiiA;%
+ \parseUTFviiiA,%
+ \parseUTFviiiA!%
+ \parseUTFviiiB F\UTFviiiFourOctets.{!,;}%
+ \fi\fi\fi
+ }
+
+ \gdef\parseUTFviiiA#1{%
+ \countUTFx = \countUTFz
+ \divide\countUTFz by 64
+ \countUTFy = \countUTFz
+ \multiply\countUTFz by 64
+ \advance\countUTFx by -\countUTFz
+ \advance\countUTFx by 128
+ \uccode `#1\countUTFx
+ \countUTFz = \countUTFy}
+
+ \gdef\parseUTFviiiB#1#2#3#4{%
+ \advance\countUTFz by "#10\relax
+ \uccode `#3\countUTFz
+ \uppercase{\gdef\UTFviiiTmp{#2#3#4}}}
+\endgroup
+
+\def\utfeightchardefs{%
+ \DeclareUnicodeCharacter{00A0}{\tie}
+ \DeclareUnicodeCharacter{00A1}{\exclamdown}
+ \DeclareUnicodeCharacter{00A3}{\pounds}
+ \DeclareUnicodeCharacter{00A8}{\"{ }}
+ \DeclareUnicodeCharacter{00A9}{\copyright}
+ \DeclareUnicodeCharacter{00AA}{\ordf}
+ \DeclareUnicodeCharacter{00AB}{\guillemetleft}
+ \DeclareUnicodeCharacter{00AD}{\-}
+ \DeclareUnicodeCharacter{00AE}{\registeredsymbol}
+ \DeclareUnicodeCharacter{00AF}{\={ }}
+
+ \DeclareUnicodeCharacter{00B0}{\ringaccent{ }}
+ \DeclareUnicodeCharacter{00B4}{\'{ }}
+ \DeclareUnicodeCharacter{00B8}{\cedilla{ }}
+ \DeclareUnicodeCharacter{00BA}{\ordm}
+ \DeclareUnicodeCharacter{00BB}{\guillemetright}
+ \DeclareUnicodeCharacter{00BF}{\questiondown}
+
+ \DeclareUnicodeCharacter{00C0}{\`A}
+ \DeclareUnicodeCharacter{00C1}{\'A}
+ \DeclareUnicodeCharacter{00C2}{\^A}
+ \DeclareUnicodeCharacter{00C3}{\~A}
+ \DeclareUnicodeCharacter{00C4}{\"A}
+ \DeclareUnicodeCharacter{00C5}{\AA}
+ \DeclareUnicodeCharacter{00C6}{\AE}
+ \DeclareUnicodeCharacter{00C7}{\cedilla{C}}
+ \DeclareUnicodeCharacter{00C8}{\`E}
+ \DeclareUnicodeCharacter{00C9}{\'E}
+ \DeclareUnicodeCharacter{00CA}{\^E}
+ \DeclareUnicodeCharacter{00CB}{\"E}
+ \DeclareUnicodeCharacter{00CC}{\`I}
+ \DeclareUnicodeCharacter{00CD}{\'I}
+ \DeclareUnicodeCharacter{00CE}{\^I}
+ \DeclareUnicodeCharacter{00CF}{\"I}
+
+ \DeclareUnicodeCharacter{00D0}{\DH}
+ \DeclareUnicodeCharacter{00D1}{\~N}
+ \DeclareUnicodeCharacter{00D2}{\`O}
+ \DeclareUnicodeCharacter{00D3}{\'O}
+ \DeclareUnicodeCharacter{00D4}{\^O}
+ \DeclareUnicodeCharacter{00D5}{\~O}
+ \DeclareUnicodeCharacter{00D6}{\"O}
+ \DeclareUnicodeCharacter{00D8}{\O}
+ \DeclareUnicodeCharacter{00D9}{\`U}
+ \DeclareUnicodeCharacter{00DA}{\'U}
+ \DeclareUnicodeCharacter{00DB}{\^U}
+ \DeclareUnicodeCharacter{00DC}{\"U}
+ \DeclareUnicodeCharacter{00DD}{\'Y}
+ \DeclareUnicodeCharacter{00DE}{\TH}
+ \DeclareUnicodeCharacter{00DF}{\ss}
+
+ \DeclareUnicodeCharacter{00E0}{\`a}
+ \DeclareUnicodeCharacter{00E1}{\'a}
+ \DeclareUnicodeCharacter{00E2}{\^a}
+ \DeclareUnicodeCharacter{00E3}{\~a}
+ \DeclareUnicodeCharacter{00E4}{\"a}
+ \DeclareUnicodeCharacter{00E5}{\aa}
+ \DeclareUnicodeCharacter{00E6}{\ae}
+ \DeclareUnicodeCharacter{00E7}{\cedilla{c}}
+ \DeclareUnicodeCharacter{00E8}{\`e}
+ \DeclareUnicodeCharacter{00E9}{\'e}
+ \DeclareUnicodeCharacter{00EA}{\^e}
+ \DeclareUnicodeCharacter{00EB}{\"e}
+ \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}}
+ \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}}
+ \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}}
+ \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}}
+
+ \DeclareUnicodeCharacter{00F0}{\dh}
+ \DeclareUnicodeCharacter{00F1}{\~n}
+ \DeclareUnicodeCharacter{00F2}{\`o}
+ \DeclareUnicodeCharacter{00F3}{\'o}
+ \DeclareUnicodeCharacter{00F4}{\^o}
+ \DeclareUnicodeCharacter{00F5}{\~o}
+ \DeclareUnicodeCharacter{00F6}{\"o}
+ \DeclareUnicodeCharacter{00F8}{\o}
+ \DeclareUnicodeCharacter{00F9}{\`u}
+ \DeclareUnicodeCharacter{00FA}{\'u}
+ \DeclareUnicodeCharacter{00FB}{\^u}
+ \DeclareUnicodeCharacter{00FC}{\"u}
+ \DeclareUnicodeCharacter{00FD}{\'y}
+ \DeclareUnicodeCharacter{00FE}{\th}
+ \DeclareUnicodeCharacter{00FF}{\"y}
+
+ \DeclareUnicodeCharacter{0100}{\=A}
+ \DeclareUnicodeCharacter{0101}{\=a}
+ \DeclareUnicodeCharacter{0102}{\u{A}}
+ \DeclareUnicodeCharacter{0103}{\u{a}}
+ \DeclareUnicodeCharacter{0104}{\ogonek{A}}
+ \DeclareUnicodeCharacter{0105}{\ogonek{a}}
+ \DeclareUnicodeCharacter{0106}{\'C}
+ \DeclareUnicodeCharacter{0107}{\'c}
+ \DeclareUnicodeCharacter{0108}{\^C}
+ \DeclareUnicodeCharacter{0109}{\^c}
+ \DeclareUnicodeCharacter{0118}{\ogonek{E}}
+ \DeclareUnicodeCharacter{0119}{\ogonek{e}}
+ \DeclareUnicodeCharacter{010A}{\dotaccent{C}}
+ \DeclareUnicodeCharacter{010B}{\dotaccent{c}}
+ \DeclareUnicodeCharacter{010C}{\v{C}}
+ \DeclareUnicodeCharacter{010D}{\v{c}}
+ \DeclareUnicodeCharacter{010E}{\v{D}}
+
+ \DeclareUnicodeCharacter{0112}{\=E}
+ \DeclareUnicodeCharacter{0113}{\=e}
+ \DeclareUnicodeCharacter{0114}{\u{E}}
+ \DeclareUnicodeCharacter{0115}{\u{e}}
+ \DeclareUnicodeCharacter{0116}{\dotaccent{E}}
+ \DeclareUnicodeCharacter{0117}{\dotaccent{e}}
+ \DeclareUnicodeCharacter{011A}{\v{E}}
+ \DeclareUnicodeCharacter{011B}{\v{e}}
+ \DeclareUnicodeCharacter{011C}{\^G}
+ \DeclareUnicodeCharacter{011D}{\^g}
+ \DeclareUnicodeCharacter{011E}{\u{G}}
+ \DeclareUnicodeCharacter{011F}{\u{g}}
+
+ \DeclareUnicodeCharacter{0120}{\dotaccent{G}}
+ \DeclareUnicodeCharacter{0121}{\dotaccent{g}}
+ \DeclareUnicodeCharacter{0124}{\^H}
+ \DeclareUnicodeCharacter{0125}{\^h}
+ \DeclareUnicodeCharacter{0128}{\~I}
+ \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}}
+ \DeclareUnicodeCharacter{012A}{\=I}
+ \DeclareUnicodeCharacter{012B}{\={\dotless{i}}}
+ \DeclareUnicodeCharacter{012C}{\u{I}}
+ \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}}
+
+ \DeclareUnicodeCharacter{0130}{\dotaccent{I}}
+ \DeclareUnicodeCharacter{0131}{\dotless{i}}
+ \DeclareUnicodeCharacter{0132}{IJ}
+ \DeclareUnicodeCharacter{0133}{ij}
+ \DeclareUnicodeCharacter{0134}{\^J}
+ \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}}
+ \DeclareUnicodeCharacter{0139}{\'L}
+ \DeclareUnicodeCharacter{013A}{\'l}
+
+ \DeclareUnicodeCharacter{0141}{\L}
+ \DeclareUnicodeCharacter{0142}{\l}
+ \DeclareUnicodeCharacter{0143}{\'N}
+ \DeclareUnicodeCharacter{0144}{\'n}
+ \DeclareUnicodeCharacter{0147}{\v{N}}
+ \DeclareUnicodeCharacter{0148}{\v{n}}
+ \DeclareUnicodeCharacter{014C}{\=O}
+ \DeclareUnicodeCharacter{014D}{\=o}
+ \DeclareUnicodeCharacter{014E}{\u{O}}
+ \DeclareUnicodeCharacter{014F}{\u{o}}
+
+ \DeclareUnicodeCharacter{0150}{\H{O}}
+ \DeclareUnicodeCharacter{0151}{\H{o}}
+ \DeclareUnicodeCharacter{0152}{\OE}
+ \DeclareUnicodeCharacter{0153}{\oe}
+ \DeclareUnicodeCharacter{0154}{\'R}
+ \DeclareUnicodeCharacter{0155}{\'r}
+ \DeclareUnicodeCharacter{0158}{\v{R}}
+ \DeclareUnicodeCharacter{0159}{\v{r}}
+ \DeclareUnicodeCharacter{015A}{\'S}
+ \DeclareUnicodeCharacter{015B}{\'s}
+ \DeclareUnicodeCharacter{015C}{\^S}
+ \DeclareUnicodeCharacter{015D}{\^s}
+ \DeclareUnicodeCharacter{015E}{\cedilla{S}}
+ \DeclareUnicodeCharacter{015F}{\cedilla{s}}
+
+ \DeclareUnicodeCharacter{0160}{\v{S}}
+ \DeclareUnicodeCharacter{0161}{\v{s}}
+ \DeclareUnicodeCharacter{0162}{\cedilla{t}}
+ \DeclareUnicodeCharacter{0163}{\cedilla{T}}
+ \DeclareUnicodeCharacter{0164}{\v{T}}
+
+ \DeclareUnicodeCharacter{0168}{\~U}
+ \DeclareUnicodeCharacter{0169}{\~u}
+ \DeclareUnicodeCharacter{016A}{\=U}
+ \DeclareUnicodeCharacter{016B}{\=u}
+ \DeclareUnicodeCharacter{016C}{\u{U}}
+ \DeclareUnicodeCharacter{016D}{\u{u}}
+ \DeclareUnicodeCharacter{016E}{\ringaccent{U}}
+ \DeclareUnicodeCharacter{016F}{\ringaccent{u}}
+
+ \DeclareUnicodeCharacter{0170}{\H{U}}
+ \DeclareUnicodeCharacter{0171}{\H{u}}
+ \DeclareUnicodeCharacter{0174}{\^W}
+ \DeclareUnicodeCharacter{0175}{\^w}
+ \DeclareUnicodeCharacter{0176}{\^Y}
+ \DeclareUnicodeCharacter{0177}{\^y}
+ \DeclareUnicodeCharacter{0178}{\"Y}
+ \DeclareUnicodeCharacter{0179}{\'Z}
+ \DeclareUnicodeCharacter{017A}{\'z}
+ \DeclareUnicodeCharacter{017B}{\dotaccent{Z}}
+ \DeclareUnicodeCharacter{017C}{\dotaccent{z}}
+ \DeclareUnicodeCharacter{017D}{\v{Z}}
+ \DeclareUnicodeCharacter{017E}{\v{z}}
+
+ \DeclareUnicodeCharacter{01C4}{D\v{Z}}
+ \DeclareUnicodeCharacter{01C5}{D\v{z}}
+ \DeclareUnicodeCharacter{01C6}{d\v{z}}
+ \DeclareUnicodeCharacter{01C7}{LJ}
+ \DeclareUnicodeCharacter{01C8}{Lj}
+ \DeclareUnicodeCharacter{01C9}{lj}
+ \DeclareUnicodeCharacter{01CA}{NJ}
+ \DeclareUnicodeCharacter{01CB}{Nj}
+ \DeclareUnicodeCharacter{01CC}{nj}
+ \DeclareUnicodeCharacter{01CD}{\v{A}}
+ \DeclareUnicodeCharacter{01CE}{\v{a}}
+ \DeclareUnicodeCharacter{01CF}{\v{I}}
+
+ \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}}
+ \DeclareUnicodeCharacter{01D1}{\v{O}}
+ \DeclareUnicodeCharacter{01D2}{\v{o}}
+ \DeclareUnicodeCharacter{01D3}{\v{U}}
+ \DeclareUnicodeCharacter{01D4}{\v{u}}
+
+ \DeclareUnicodeCharacter{01E2}{\={\AE}}
+ \DeclareUnicodeCharacter{01E3}{\={\ae}}
+ \DeclareUnicodeCharacter{01E6}{\v{G}}
+ \DeclareUnicodeCharacter{01E7}{\v{g}}
+ \DeclareUnicodeCharacter{01E8}{\v{K}}
+ \DeclareUnicodeCharacter{01E9}{\v{k}}
+
+ \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}}
+ \DeclareUnicodeCharacter{01F1}{DZ}
+ \DeclareUnicodeCharacter{01F2}{Dz}
+ \DeclareUnicodeCharacter{01F3}{dz}
+ \DeclareUnicodeCharacter{01F4}{\'G}
+ \DeclareUnicodeCharacter{01F5}{\'g}
+ \DeclareUnicodeCharacter{01F8}{\`N}
+ \DeclareUnicodeCharacter{01F9}{\`n}
+ \DeclareUnicodeCharacter{01FC}{\'{\AE}}
+ \DeclareUnicodeCharacter{01FD}{\'{\ae}}
+ \DeclareUnicodeCharacter{01FE}{\'{\O}}
+ \DeclareUnicodeCharacter{01FF}{\'{\o}}
+
+ \DeclareUnicodeCharacter{021E}{\v{H}}
+ \DeclareUnicodeCharacter{021F}{\v{h}}
+
+ \DeclareUnicodeCharacter{0226}{\dotaccent{A}}
+ \DeclareUnicodeCharacter{0227}{\dotaccent{a}}
+ \DeclareUnicodeCharacter{0228}{\cedilla{E}}
+ \DeclareUnicodeCharacter{0229}{\cedilla{e}}
+ \DeclareUnicodeCharacter{022E}{\dotaccent{O}}
+ \DeclareUnicodeCharacter{022F}{\dotaccent{o}}
+
+ \DeclareUnicodeCharacter{0232}{\=Y}
+ \DeclareUnicodeCharacter{0233}{\=y}
+ \DeclareUnicodeCharacter{0237}{\dotless{j}}
+
+ \DeclareUnicodeCharacter{02DB}{\ogonek{ }}
+
+ \DeclareUnicodeCharacter{1E02}{\dotaccent{B}}
+ \DeclareUnicodeCharacter{1E03}{\dotaccent{b}}
+ \DeclareUnicodeCharacter{1E04}{\udotaccent{B}}
+ \DeclareUnicodeCharacter{1E05}{\udotaccent{b}}
+ \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}}
+ \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}}
+ \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}}
+ \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}}
+ \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}}
+ \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}}
+ \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}}
+ \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}}
+
+ \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}}
+ \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}}
+
+ \DeclareUnicodeCharacter{1E20}{\=G}
+ \DeclareUnicodeCharacter{1E21}{\=g}
+ \DeclareUnicodeCharacter{1E22}{\dotaccent{H}}
+ \DeclareUnicodeCharacter{1E23}{\dotaccent{h}}
+ \DeclareUnicodeCharacter{1E24}{\udotaccent{H}}
+ \DeclareUnicodeCharacter{1E25}{\udotaccent{h}}
+ \DeclareUnicodeCharacter{1E26}{\"H}
+ \DeclareUnicodeCharacter{1E27}{\"h}
+
+ \DeclareUnicodeCharacter{1E30}{\'K}
+ \DeclareUnicodeCharacter{1E31}{\'k}
+ \DeclareUnicodeCharacter{1E32}{\udotaccent{K}}
+ \DeclareUnicodeCharacter{1E33}{\udotaccent{k}}
+ \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}}
+ \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}}
+ \DeclareUnicodeCharacter{1E36}{\udotaccent{L}}
+ \DeclareUnicodeCharacter{1E37}{\udotaccent{l}}
+ \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}}
+ \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}}
+ \DeclareUnicodeCharacter{1E3E}{\'M}
+ \DeclareUnicodeCharacter{1E3F}{\'m}
+
+ \DeclareUnicodeCharacter{1E40}{\dotaccent{M}}
+ \DeclareUnicodeCharacter{1E41}{\dotaccent{m}}
+ \DeclareUnicodeCharacter{1E42}{\udotaccent{M}}
+ \DeclareUnicodeCharacter{1E43}{\udotaccent{m}}
+ \DeclareUnicodeCharacter{1E44}{\dotaccent{N}}
+ \DeclareUnicodeCharacter{1E45}{\dotaccent{n}}
+ \DeclareUnicodeCharacter{1E46}{\udotaccent{N}}
+ \DeclareUnicodeCharacter{1E47}{\udotaccent{n}}
+ \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}}
+ \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}}
+
+ \DeclareUnicodeCharacter{1E54}{\'P}
+ \DeclareUnicodeCharacter{1E55}{\'p}
+ \DeclareUnicodeCharacter{1E56}{\dotaccent{P}}
+ \DeclareUnicodeCharacter{1E57}{\dotaccent{p}}
+ \DeclareUnicodeCharacter{1E58}{\dotaccent{R}}
+ \DeclareUnicodeCharacter{1E59}{\dotaccent{r}}
+ \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}}
+ \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}}
+ \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}}
+ \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}}
+
+ \DeclareUnicodeCharacter{1E60}{\dotaccent{S}}
+ \DeclareUnicodeCharacter{1E61}{\dotaccent{s}}
+ \DeclareUnicodeCharacter{1E62}{\udotaccent{S}}
+ \DeclareUnicodeCharacter{1E63}{\udotaccent{s}}
+ \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}}
+ \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}}
+ \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}}
+ \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}}
+ \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}}
+ \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}}
+
+ \DeclareUnicodeCharacter{1E7C}{\~V}
+ \DeclareUnicodeCharacter{1E7D}{\~v}
+ \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}}
+ \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}}
+
+ \DeclareUnicodeCharacter{1E80}{\`W}
+ \DeclareUnicodeCharacter{1E81}{\`w}
+ \DeclareUnicodeCharacter{1E82}{\'W}
+ \DeclareUnicodeCharacter{1E83}{\'w}
+ \DeclareUnicodeCharacter{1E84}{\"W}
+ \DeclareUnicodeCharacter{1E85}{\"w}
+ \DeclareUnicodeCharacter{1E86}{\dotaccent{W}}
+ \DeclareUnicodeCharacter{1E87}{\dotaccent{w}}
+ \DeclareUnicodeCharacter{1E88}{\udotaccent{W}}
+ \DeclareUnicodeCharacter{1E89}{\udotaccent{w}}
+ \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}}
+ \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}}
+ \DeclareUnicodeCharacter{1E8C}{\"X}
+ \DeclareUnicodeCharacter{1E8D}{\"x}
+ \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}}
+ \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}}
+
+ \DeclareUnicodeCharacter{1E90}{\^Z}
+ \DeclareUnicodeCharacter{1E91}{\^z}
+ \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}}
+ \DeclareUnicodeCharacter{1E93}{\udotaccent{z}}
+ \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}}
+ \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}}
+ \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}}
+ \DeclareUnicodeCharacter{1E97}{\"t}
+ \DeclareUnicodeCharacter{1E98}{\ringaccent{w}}
+ \DeclareUnicodeCharacter{1E99}{\ringaccent{y}}
+
+ \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}}
+ \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}}
+
+ \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}}
+ \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}}
+ \DeclareUnicodeCharacter{1EBC}{\~E}
+ \DeclareUnicodeCharacter{1EBD}{\~e}
+
+ \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}}
+ \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}}
+ \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}}
+ \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}}
+
+ \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}}
+ \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}}
+
+ \DeclareUnicodeCharacter{1EF2}{\`Y}
+ \DeclareUnicodeCharacter{1EF3}{\`y}
+ \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}}
+
+ \DeclareUnicodeCharacter{1EF8}{\~Y}
+ \DeclareUnicodeCharacter{1EF9}{\~y}
+
+ \DeclareUnicodeCharacter{2013}{--}
+ \DeclareUnicodeCharacter{2014}{---}
+ \DeclareUnicodeCharacter{2018}{\quoteleft}
+ \DeclareUnicodeCharacter{2019}{\quoteright}
+ \DeclareUnicodeCharacter{201A}{\quotesinglbase}
+ \DeclareUnicodeCharacter{201C}{\quotedblleft}
+ \DeclareUnicodeCharacter{201D}{\quotedblright}
+ \DeclareUnicodeCharacter{201E}{\quotedblbase}
+ \DeclareUnicodeCharacter{2022}{\bullet}
+ \DeclareUnicodeCharacter{2026}{\dots}
+ \DeclareUnicodeCharacter{2039}{\guilsinglleft}
+ \DeclareUnicodeCharacter{203A}{\guilsinglright}
+ \DeclareUnicodeCharacter{20AC}{\euro}
+
+ \DeclareUnicodeCharacter{2192}{\expansion}
+ \DeclareUnicodeCharacter{21D2}{\result}
+
+ \DeclareUnicodeCharacter{2212}{\minus}
+ \DeclareUnicodeCharacter{2217}{\point}
+ \DeclareUnicodeCharacter{2261}{\equiv}
+}% end of \utfeightchardefs
+
+
+% US-ASCII character definitions.
+\def\asciichardefs{% nothing need be done
+ \relax
+}
+
+% Make non-ASCII characters printable again for compatibility with
+% existing Texinfo documents that may use them, even without declaring a
+% document encoding.
+%
+\setnonasciicharscatcode \other
+
+
+\message{formatting,}
+
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness = 10000
+
+% Don't be so finicky about underfull hboxes, either.
+\hbadness = 2000
+
+% Following George Bush, get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+ \ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+ \else
+ \emergencystretch = .15\hsize
+ \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading. The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+ \voffset = #3\relax
+ \topskip = #6\relax
+ \splittopskip = \topskip
+ %
+ \vsize = #1\relax
+ \advance\vsize by \topskip
+ \outervsize = \vsize
+ \advance\outervsize by 2\topandbottommargin
+ \pageheight = \vsize
+ %
+ \hsize = #2\relax
+ \outerhsize = \hsize
+ \advance\outerhsize by 0.5in
+ \pagewidth = \hsize
+ %
+ \normaloffset = #4\relax
+ \bindingoffset = #5\relax
+ %
+ \ifpdf
+ \pdfpageheight #7\relax
+ \pdfpagewidth #8\relax
+ % if we don't reset these, they will remain at "1 true in" of
+ % whatever layout pdftex was dumped with.
+ \pdfhorigin = 1 true in
+ \pdfvorigin = 1 true in
+ \fi
+ %
+ \setleading{\textleading}
+ %
+ \parindent = \defaultparindent
+ \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % If page is nothing but text, make it come out even.
+ \internalpagesizes{607.2pt}{6in}% that's 46 lines
+ {\voffset}{.25in}%
+ {\bindingoffset}{36pt}%
+ {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+ \parskip = 2pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.5in}{5in}%
+ {-.2in}{0in}%
+ {\bindingoffset}{16pt}%
+ {9.25in}{7in}%
+ %
+ \lispnarrowing = 0.3in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+ \parskip = 1.5pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.4in}{4.8in}%
+ {-.2in}{-.4in}%
+ {0pt}{14pt}%
+ {9in}{6in}%
+ %
+ \lispnarrowing = 0.25in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % Double-side printing via postscript on Laserjet 4050
+ % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+ % To change the settings for a different printer or situation, adjust
+ % \normaloffset until the front-side and back-side texts align. Then
+ % do the same for \bindingoffset. You can set these for testing in
+ % your texinfo source file like this:
+ % @tex
+ % \global\normaloffset = -6mm
+ % \global\bindingoffset = 10mm
+ % @end tex
+ \internalpagesizes{673.2pt}{160mm}% that's 51 lines
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{44pt}%
+ {297mm}{210mm}%
+ %
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+ \parskip = 2pt plus 1pt minus 0.1pt
+ \textleading = 12.5pt
+ %
+ \internalpagesizes{160mm}{120mm}%
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{8pt}%
+ {210mm}{148mm}%
+ %
+ \lispnarrowing = 0.2in
+ \tolerance = 800
+ \hfuzz = 1.2pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 2mm
+ \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{237mm}{150mm}%
+ {\voffset}{4.6mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ %
+ % Must explicitly reset to 0 because we call \afourpaper.
+ \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{241mm}{165mm}%
+ {\voffset}{-2.95mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+ \globaldefs = 1
+ %
+ \parskip = 3pt plus 2pt minus 1pt
+ \setleading{\textleading}%
+ %
+ \dimen0 = #1\relax
+ \advance\dimen0 by \voffset
+ %
+ \dimen2 = \hsize
+ \advance\dimen2 by \normaloffset
+ %
+ \internalpagesizes{#1}{\hsize}%
+ {\voffset}{\normaloffset}%
+ {\bindingoffset}{44pt}%
+ {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
+
+% DEL is a comment character, in case @c does not suffice.
+\catcode`\^^? = 14
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\catcode`\$=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+\def\normaldollar{$}%$ font-lock fix
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font. Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts. But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt\char34}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt\char126}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+\let\realunder=_
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+
+\catcode`\|=\active
+\def|{{\tt\char124}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+\catcode`\$=\active
+\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+ \normalturnoffactive
+ \otherbackslash
+}
+
+\catcode`\@=0
+
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work
+
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
+
+% In texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active
+@def@normalbackslash{{@tt@backslashcurfont}}
+% On startup, @fixbackslash assigns:
+% @let \ = @normalbackslash
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+%
+@def@normalturnoffactive{%
+ @let\=@normalbackslash
+ @let"=@normaldoublequote
+ @let~=@normaltilde
+ @let^=@normalcaret
+ @let_=@normalunderscore
+ @let|=@normalverticalbar
+ @let<=@normalless
+ @let>=@normalgreater
+ @let+=@normalplus
+ @let$=@normaldollar %$ font-lock fix
+ @markupsetuplqdefault
+ @markupsetuprqdefault
+ @unsepspaces
+}
+
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\' in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+% Also turn back on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
+%
+@gdef@fixbackslash{%
+ @ifx\@eatinput @let\ = @normalbackslash @fi
+ @catcode`+=@active
+ @catcode`@_=@active
+}
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
+
+% These look ok in all fonts, so just make them not special.
+@catcode`@& = @other
+@catcode`@# = @other
+@catcode`@% = @other
+
+@c Finally, make ` and ' active, so that txicodequoteundirected and
+@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we
+@c don't make ` and ' active, @code will not get them as active chars.
+@c Do this last of all since we use ` in the previous @catcode assignments.
+@catcode`@'=@active
+@catcode`@`=@active
+@markupsetuplqdefault
+@markupsetuprqdefault
+
+@c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c page-delimiter: "^\\\\message"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@ignore
+ arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore
diff --git a/doc/troubleshooting.texi b/doc/troubleshooting.texi
new file mode 100755
index 0000000..18fee7a
--- /dev/null
+++ b/doc/troubleshooting.texi
@@ -0,0 +1,131 @@
+@node Troubleshooting, Statement Index, Running Knot DNS, Top
+@chapter Troubleshooting
+
+@menu
+* Submitting a bugreport::
+* Generating backtrace::
+* Debug messages::
+@end menu
+
+First of all, check the logs (@pxref{log}).
+By default, Knot DNS logs all error messages to syslog. Enabling at least
+the @code{warning} message severity may help you identify some problems.
+
+@node Submitting a bugreport
+@section Submitting a bugreport
+
+If you are unable to solve the problem by yourselves, you can submit a
+bugreport to the Knot DNS team. For security issues (e.g. crash) do not
+use the public mailinglist. Instead, write to
+@url{mailto:knot-dns@@labs.nic.cz, knot-dns@@labs.nic.cz}. All other bugs
+and questions may be directed to the Knot DNS users mailinglist
+(@url{mailto:knot-dns-users@@lists.nic.cz, knot-dns-users@@lists.nic.cz}).
+
+The bugreport should contain at least:
+@itemize
+@item Knot DNS version and type of installation (source, package, etc.),
+@item type and version of your operating system,
+@item basic hardware information,
+@item description of the bug,
+@item log output of all messages (category @code{any}) with severity Info
+and higher (severities @code{info, notice, warning, error}, or @code{any} if debug messages are not turned on (see below)),
+@item steps to reproduce the bug (if known),
+@item backtrace (if the bug caused a crash; see next section).
+@end itemize
+
+If it is possible, the actual configuration file and/or zone file(s) will
+be very useful as well.
+
+@node Generating backtrace
+@section Generating backtrace
+
+There are several ways to achieve that, the most common way is to leave core
+dumps and then extract a backtrace from it.
+This doesn't affect any server operation, you just need to make sure
+the OS is configured to generate them.
+
+@example
+$ ulimit -c unlimited # enable unlimited core dump size
+...
+$ gdb $(which knotd) core.<KNOT_PID> # start gdb on a core dump
+(gdb) thread apply all bt # extract backtrace from all threads
+(gdb) q
+@end example
+
+If the error is repeatable, you can run the binary in a @code{gdb} debugger
+or attach the debugger to the running process. The backtrace from a running
+process is generally useful when debugging problems like stuck process and similar.
+@example
+$ knotc -c knot.conf start
+$ sudo gdb --pid <KNOT_PID>
+(gdb) continue
+...
+(gdb) thread apply all bt
+(gdb) q
+@end example
+
+
+
+@node Debug messages
+@section Debug messages
+
+@menu
+* Enabling debug messages in server::
+@end menu
+
+In some cases the aforementioned information may not be enough to find
+and fix the bug. In these cases it may be useful to turn on debug messages.
+
+Two steps are required in order to log debug messages. First you need to
+allow the debug messages in the server. Then the logging must be configured
+to log debug messages (@pxref{log}). It is recommended to log these
+messages to a file. Firstly, the debug output may be rather large and
+secondly, it is easier to use the data for debugging.
+
+@node Enabling debug messages in server
+@subsection Enabling debug messages in server
+
+@menu
+* Debug messages Example::
+@end menu
+
+Allowing debug messages in the server is possible only when configuring the
+sources. Two @command{configure} options are required to do this:
+
+@itemize
+@item
+The @code{--enable-debug} option specifies the server modules for which you
+want to enable debug messages. One or more of the following modules may be
+listed, separated by commas:
+
+@itemize
+@item @code{server} - Messages related to networking, threads and low-level
+ journal handling.
+@item @code{zones} - All operations with zones - loading, updating, saving,
+ timers, high-level journal management.
+@item @code{xfr} - AXFR, IXFR and NOTIFY handling.
+@item @code{packet} - Packet parsing and response creation.
+@item @code{dname} - Parsing, comparing and other operations on domain names.
+@item @code{rr} - Details of processed resource records.
+@item @code{ns} - Query processing, high-level handling of all requests
+ (transfers, NOTIFY, normal queries).
+@item @code{hash} - Details of hash table (the main data structure) operation.
+@item @code{compiler} - Zone file compilation.
+@end itemize
+
+@item
+The @code{--enable-debuglevel} option is used to specify the verbosity of the
+debug output. Be careful with this, as the @code{details} verbosity may produce
+really large logs (in order of GBs). There are three levels of verbosity:
+@code{brief}, @code{verbose} and @code{details}.
+
+@end itemize
+
+@node Debug messages Example
+@subsubsection Example
+
+@example
+$ ./configure --enable-debug=server,zones --enable-debuglevel=verbose
+@end example
+
+
diff --git a/knot.sample.conf.in b/knot.sample.conf.in
index db5c587..db5c587 100644..100755
--- a/knot.sample.conf.in
+++ b/knot.sample.conf.in
diff --git a/m4/ax_check_compiler_flags.m4 b/m4/ax_check_compiler_flags.m4
index 05e5c3b..05e5c3b 100644..100755
--- a/m4/ax_check_compiler_flags.m4
+++ b/m4/ax_check_compiler_flags.m4
diff --git a/m4/ax_ext.m4 b/m4/ax_ext.m4
index 898f2bf..898f2bf 100644..100755
--- a/m4/ax_ext.m4
+++ b/m4/ax_ext.m4
diff --git a/m4/ax_gcc_x86_cpuid.m4 b/m4/ax_gcc_x86_cpuid.m4
index e9231b8..e9231b8 100644..100755
--- a/m4/ax_gcc_x86_cpuid.m4
+++ b/m4/ax_gcc_x86_cpuid.m4
diff --git a/m4/libtool.m4 b/m4/libtool.m4
index 8ff3c76..8ff3c76 100644..100755
--- a/m4/libtool.m4
+++ b/m4/libtool.m4
diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4
index 17cfd51..17cfd51 100644..100755
--- a/m4/ltoptions.m4
+++ b/m4/ltoptions.m4
diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4
index 9000a05..9000a05 100644..100755
--- a/m4/ltsugar.m4
+++ b/m4/ltsugar.m4
diff --git a/m4/ltversion.m4 b/m4/ltversion.m4
index 9c7b5d4..9c7b5d4 100644..100755
--- a/m4/ltversion.m4
+++ b/m4/ltversion.m4
diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4
index c573da9..c573da9 100644..100755
--- a/m4/lt~obsolete.m4
+++ b/m4/lt~obsolete.m4
diff --git a/samples/Makefile.am b/samples/Makefile.am
index f9aa616..f9aa616 100644..100755
--- a/samples/Makefile.am
+++ b/samples/Makefile.am
diff --git a/samples/Makefile.in b/samples/Makefile.in
index 6abb962..6abb962 100644..100755
--- a/samples/Makefile.in
+++ b/samples/Makefile.in
diff --git a/samples/bogus25.com.zone b/samples/bogus25.com.zone
index 5260270..5260270 100644..100755
--- a/samples/bogus25.com.zone
+++ b/samples/bogus25.com.zone
diff --git a/samples/example.com.zone b/samples/example.com.zone
index c6a0aef..c6a0aef 100644..100755
--- a/samples/example.com.zone
+++ b/samples/example.com.zone
diff --git a/samples/example.com.zone.signed b/samples/example.com.zone.signed
index d57b39b..d57b39b 100644..100755
--- a/samples/example.com.zone.signed
+++ b/samples/example.com.zone.signed
diff --git a/samples/example.com.zone.signed.nsec3 b/samples/example.com.zone.signed.nsec3
index 88a28fc..88a28fc 100644..100755
--- a/samples/example.com.zone.signed.nsec3
+++ b/samples/example.com.zone.signed.nsec3
diff --git a/samples/knot.full.conf b/samples/knot.full.conf
index d9d9de7..9aac27b 100644..100755
--- a/samples/knot.full.conf
+++ b/samples/knot.full.conf
@@ -18,7 +18,7 @@ system {
identity "I have no mouth and must scream";
# Version of the server (see RFC 4892). Not used yet.
- version "0.1";
+ version "1.1.0";
# Server identifier
# Use string format "text"
@@ -39,7 +39,7 @@ system {
workers 1;
# User for running server
- # May also specify user.group (f.e. knot.users)
+ # May also specify user.group (e.g. knot.users)
user root;
}
@@ -119,6 +119,11 @@ zones {
# Shared options for all listed zones
#
+ # Build differences from zone file changes
+ # Possible values: on|off
+ # Default value: off
+ ixfr-from-differences off;
+
# Enable semantic checks for all zones (if 'on')
# Possible values: on|off
# Default value: off
@@ -161,6 +166,11 @@ zones {
# it is considered relative to the current directory from which the server
# was started.
file "samples/example.com.zone";
+
+ # Build differences from zone file changes
+ # Possible values: on|off
+ # Default value: off
+ ixfr-from-differences off;
# Disable ANY type queries for authoritative answers (if 'on')
# Possible values: on|off
@@ -186,7 +196,7 @@ zones {
# Possible values: <1..INT_MAX> (seconds)
# Default value: inherited from zones.zonefile-sync
# It is also possible to suffix with unit size [s/m/h/d]
- # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
+ # f.e. 1s = 1 second, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
zonefile-sync 1h;
# XFR master server
diff --git a/samples/knot.min.conf b/samples/knot.min.conf
index f9bc29c..6b7c1af 100644..100755
--- a/samples/knot.min.conf
+++ b/samples/knot.min.conf
@@ -4,7 +4,7 @@
# This is a sample of a minimal configuration file for Knot DNS.
#
# For exhaustive list of all options see samples/knot.full.conf
-# in the source directory.
+# in the source directory or refer to user manual.
#
system {
diff --git a/samples/knot.sample.conf.in b/samples/knot.sample.conf.in
index 7084595..09da2a8 100644..100755
--- a/samples/knot.sample.conf.in
+++ b/samples/knot.sample.conf.in
@@ -14,5 +14,5 @@ zones {
}
log {
- syslog { any warning, error; }
+ syslog { any info, notice, warning, error; }
}
diff --git a/scripts/urcu-tls-compat.patch b/scripts/urcu-tls-compat.patch
index cd5ff25..cd5ff25 100644..100755
--- a/scripts/urcu-tls-compat.patch
+++ b/scripts/urcu-tls-compat.patch
diff --git a/src/Makefile.am b/src/Makefile.am
index 60c51c6..eece959 100644..100755
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,8 +1,8 @@
ACLOCAL_AMFLAGS = -I ../m4
libexec_PROGRAMS = knot-zcompile unittests unittests-zcompile unittests-libknot-realdata unittests-libknot unittests-xfr
sbin_PROGRAMS = knotc knotd
-MANPAGES = knotc.8 knotd.8
-man8_MANS = knotc.8 knotd.8
+MANPAGES = knotc.8 knotd.8 knot.conf.5
+man8_MANS = knotc.8 knotd.8 knot.conf.5
EXTRA_DIST = $(man8_MANS)
# $(YACC) will generate header file
@@ -58,8 +58,6 @@ knot_zcompile_SOURCES = \
unittests_SOURCES = \
tests/common/acl_tests.c \
tests/common/acl_tests.h \
- tests/common/da_tests.c \
- tests/common/da_tests.h \
tests/common/events_tests.c \
tests/common/events_tests.h \
tests/common/skiplist_tests.c \
@@ -159,8 +157,6 @@ libknot_la_SOURCES = \
libknot/util/debug.c \
libknot/util/debug.h \
libknot/util/utils.h \
- libknot/util/conv.h \
- libknot/util/conv.c \
libknot/util/descriptor.c \
libknot/util/tolower.h \
libknot/util/tolower.c \
@@ -184,6 +180,8 @@ libknot_la_SOURCES = \
libknot/zone/node.c \
libknot/zone/dname-table.h \
libknot/zone/dname-table.c \
+ libknot/zone/zone-diff.h \
+ libknot/zone/zone-diff.c \
libknot/hash/hash-functions.c \
libknot/hash/cuckoo-hash-table.c \
libknot/hash/universal-system.c \
@@ -225,17 +223,19 @@ libknots_la_SOURCES = \
common/mempattern.c \
common/lists.c \
common/base32.c \
+ common/base64.c \
+ common/base64.h \
common/lists.h \
+ common/heap.h \
+ common/heap.c \
common/base32.h \
common/print.c \
common/print.h \
- common/dynamic-array.c \
common/skip-list.c \
common/base32hex.c \
common/skip-list.h \
common/general-tree.h \
common/general-tree.c \
- common/dynamic-array.h \
common/tree.h \
common/base32hex.h \
common/evqueue.h \
diff --git a/src/Makefile.in b/src/Makefile.in
index c00450d..d20810f 100644..100755
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -60,13 +60,13 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libknot_la_LIBADD =
-am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo conv.lo \
+am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo \
descriptor.lo tolower.lo query.lo response.lo packet.lo \
zone.lo zone-contents.lo zone-tree.lo zonedb.lo node.lo \
- dname-table.lo hash-functions.lo cuckoo-hash-table.lo \
- universal-system.lo name-server.lo changesets.lo xfr-in.lo \
- ddns.lo edns.lo rrset.lo dname.lo rdata.lo nsec3.lo tsig.lo \
- tsig-op.lo
+ dname-table.lo zone-diff.lo hash-functions.lo \
+ cuckoo-hash-table.lo universal-system.lo name-server.lo \
+ changesets.lo xfr-in.lo ddns.lo edns.lo rrset.lo dname.lo \
+ rdata.lo nsec3.lo tsig.lo tsig-op.lo
libknot_la_OBJECTS = $(am_libknot_la_OBJECTS)
libknotd_la_DEPENDENCIES = libknot.la libknots.la @LIBOBJS@
am_libknotd_la_OBJECTS = gatherer.lo stat.lo error.lo \
@@ -78,7 +78,7 @@ am_libknotd_la_OBJECTS = gatherer.lo stat.lo error.lo \
libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS)
libknots_la_DEPENDENCIES = @LIBOBJS@
am_libknots_la_OBJECTS = slab.lo tap.lo mempattern.lo lists.lo \
- base32.lo print.lo dynamic-array.lo skip-list.lo base32hex.lo \
+ base32.lo base64.lo heap.lo print.lo skip-list.lo base32hex.lo \
general-tree.lo evqueue.lo evsched.lo acl.lo sockaddr.lo \
ref.lo errors.lo dSFMT.lo prng.lo fdset.lo fdset_poll.lo \
fdset_kqueue.lo fdset_epoll.lo log.lo
@@ -99,12 +99,11 @@ knotc_DEPENDENCIES = libknotd.la libknot.la libknots.la @LIBOBJS@
am_knotd_OBJECTS = main.$(OBJEXT)
knotd_OBJECTS = $(am_knotd_OBJECTS)
knotd_DEPENDENCIES = libknotd.la libknot.la libknots.la @LIBOBJS@
-am_unittests_OBJECTS = acl_tests.$(OBJEXT) da_tests.$(OBJEXT) \
- events_tests.$(OBJEXT) skiplist_tests.$(OBJEXT) \
- slab_tests.$(OBJEXT) fdset_tests.$(OBJEXT) \
- conf_tests.$(OBJEXT) dthreads_tests.$(OBJEXT) \
- journal_tests.$(OBJEXT) server_tests.$(OBJEXT) \
- unittests_main.$(OBJEXT)
+am_unittests_OBJECTS = acl_tests.$(OBJEXT) events_tests.$(OBJEXT) \
+ skiplist_tests.$(OBJEXT) slab_tests.$(OBJEXT) \
+ fdset_tests.$(OBJEXT) conf_tests.$(OBJEXT) \
+ dthreads_tests.$(OBJEXT) journal_tests.$(OBJEXT) \
+ server_tests.$(OBJEXT) unittests_main.$(OBJEXT)
nodist_unittests_OBJECTS =
unittests_OBJECTS = $(am_unittests_OBJECTS) \
$(nodist_unittests_OBJECTS)
@@ -326,8 +325,8 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I ../m4
-MANPAGES = knotc.8 knotd.8
-man8_MANS = knotc.8 knotd.8
+MANPAGES = knotc.8 knotd.8 knot.conf.5
+man8_MANS = knotc.8 knotd.8 knot.conf.5
EXTRA_DIST = $(man8_MANS)
# $(YACC) will generate header file
@@ -382,8 +381,6 @@ knot_zcompile_SOURCES = \
unittests_SOURCES = \
tests/common/acl_tests.c \
tests/common/acl_tests.h \
- tests/common/da_tests.c \
- tests/common/da_tests.h \
tests/common/events_tests.c \
tests/common/events_tests.h \
tests/common/skiplist_tests.c \
@@ -482,8 +479,6 @@ libknot_la_SOURCES = \
libknot/util/debug.c \
libknot/util/debug.h \
libknot/util/utils.h \
- libknot/util/conv.h \
- libknot/util/conv.c \
libknot/util/descriptor.c \
libknot/util/tolower.h \
libknot/util/tolower.c \
@@ -507,6 +502,8 @@ libknot_la_SOURCES = \
libknot/zone/node.c \
libknot/zone/dname-table.h \
libknot/zone/dname-table.c \
+ libknot/zone/zone-diff.h \
+ libknot/zone/zone-diff.c \
libknot/hash/hash-functions.c \
libknot/hash/cuckoo-hash-table.c \
libknot/hash/universal-system.c \
@@ -548,17 +545,19 @@ libknots_la_SOURCES = \
common/mempattern.c \
common/lists.c \
common/base32.c \
+ common/base64.c \
+ common/base64.h \
common/lists.h \
+ common/heap.h \
+ common/heap.c \
common/base32.h \
common/print.c \
common/print.h \
- common/dynamic-array.c \
common/skip-list.c \
common/base32hex.c \
common/skip-list.h \
common/general-tree.h \
common/general-tree.c \
- common/dynamic-array.h \
common/tree.h \
common/base32hex.h \
common/evqueue.h \
@@ -844,14 +843,13 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base32hex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/changesets.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf_tests.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo-hash-table.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dSFMT.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/da_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddns.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/descriptor.Plo@am__quote@
@@ -862,7 +860,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dname_tests_realdata.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dthreads_tests.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynamic-array.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edns_tests_realdata.Po@am__quote@
@@ -879,6 +876,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gatherer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general-tree.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash-functions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/journal_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knotc_main.Po@am__quote@
@@ -949,6 +947,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile_main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zlexer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-contents.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-diff.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump-text.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-dump.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zone-load.Plo@am__quote@
@@ -1005,13 +1004,6 @@ debug.lo: libknot/util/debug.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o debug.lo `test -f 'libknot/util/debug.c' || echo '$(srcdir)/'`libknot/util/debug.c
-conv.lo: libknot/util/conv.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conv.lo -MD -MP -MF $(DEPDIR)/conv.Tpo -c -o conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conv.Tpo $(DEPDIR)/conv.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/conv.c' object='conv.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c
-
descriptor.lo: libknot/util/descriptor.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT descriptor.lo -MD -MP -MF $(DEPDIR)/descriptor.Tpo -c -o descriptor.lo `test -f 'libknot/util/descriptor.c' || echo '$(srcdir)/'`libknot/util/descriptor.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/descriptor.Tpo $(DEPDIR)/descriptor.Plo
@@ -1089,6 +1081,13 @@ dname-table.lo: libknot/zone/dname-table.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dname-table.lo `test -f 'libknot/zone/dname-table.c' || echo '$(srcdir)/'`libknot/zone/dname-table.c
+zone-diff.lo: libknot/zone/zone-diff.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-diff.lo -MD -MP -MF $(DEPDIR)/zone-diff.Tpo -c -o zone-diff.lo `test -f 'libknot/zone/zone-diff.c' || echo '$(srcdir)/'`libknot/zone/zone-diff.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-diff.Tpo $(DEPDIR)/zone-diff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/zone/zone-diff.c' object='zone-diff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-diff.lo `test -f 'libknot/zone/zone-diff.c' || echo '$(srcdir)/'`libknot/zone/zone-diff.c
+
hash-functions.lo: libknot/hash/hash-functions.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hash-functions.lo -MD -MP -MF $(DEPDIR)/hash-functions.Tpo -c -o hash-functions.lo `test -f 'libknot/hash/hash-functions.c' || echo '$(srcdir)/'`libknot/hash/hash-functions.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hash-functions.Tpo $(DEPDIR)/hash-functions.Plo
@@ -1355,6 +1354,20 @@ base32.lo: common/base32.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o base32.lo `test -f 'common/base32.c' || echo '$(srcdir)/'`common/base32.c
+base64.lo: common/base64.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT base64.lo -MD -MP -MF $(DEPDIR)/base64.Tpo -c -o base64.lo `test -f 'common/base64.c' || echo '$(srcdir)/'`common/base64.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/base64.Tpo $(DEPDIR)/base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/base64.c' object='base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o base64.lo `test -f 'common/base64.c' || echo '$(srcdir)/'`common/base64.c
+
+heap.lo: common/heap.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT heap.lo -MD -MP -MF $(DEPDIR)/heap.Tpo -c -o heap.lo `test -f 'common/heap.c' || echo '$(srcdir)/'`common/heap.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/heap.Tpo $(DEPDIR)/heap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/heap.c' object='heap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o heap.lo `test -f 'common/heap.c' || echo '$(srcdir)/'`common/heap.c
+
print.lo: common/print.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT print.lo -MD -MP -MF $(DEPDIR)/print.Tpo -c -o print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/print.Tpo $(DEPDIR)/print.Plo
@@ -1362,13 +1375,6 @@ print.lo: common/print.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o print.lo `test -f 'common/print.c' || echo '$(srcdir)/'`common/print.c
-dynamic-array.lo: common/dynamic-array.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dynamic-array.lo -MD -MP -MF $(DEPDIR)/dynamic-array.Tpo -c -o dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dynamic-array.Tpo $(DEPDIR)/dynamic-array.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common/dynamic-array.c' object='dynamic-array.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dynamic-array.lo `test -f 'common/dynamic-array.c' || echo '$(srcdir)/'`common/dynamic-array.c
-
skip-list.lo: common/skip-list.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT skip-list.lo -MD -MP -MF $(DEPDIR)/skip-list.Tpo -c -o skip-list.lo `test -f 'common/skip-list.c' || echo '$(srcdir)/'`common/skip-list.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/skip-list.Tpo $(DEPDIR)/skip-list.Plo
@@ -1593,20 +1599,6 @@ acl_tests.obj: tests/common/acl_tests.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o acl_tests.obj `if test -f 'tests/common/acl_tests.c'; then $(CYGPATH_W) 'tests/common/acl_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/acl_tests.c'; fi`
-da_tests.o: tests/common/da_tests.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.o -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_tests.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o da_tests.o `test -f 'tests/common/da_tests.c' || echo '$(srcdir)/'`tests/common/da_tests.c
-
-da_tests.obj: tests/common/da_tests.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT da_tests.obj -MD -MP -MF $(DEPDIR)/da_tests.Tpo -c -o da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/da_tests.Tpo $(DEPDIR)/da_tests.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/common/da_tests.c' object='da_tests.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o da_tests.obj `if test -f 'tests/common/da_tests.c'; then $(CYGPATH_W) 'tests/common/da_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/common/da_tests.c'; fi`
-
events_tests.o: tests/common/events_tests.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT events_tests.o -MD -MP -MF $(DEPDIR)/events_tests.Tpo -c -o events_tests.o `test -f 'tests/common/events_tests.c' || echo '$(srcdir)/'`tests/common/events_tests.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/events_tests.Tpo $(DEPDIR)/events_tests.Po
diff --git a/src/common/LICENSE.txt b/src/common/LICENSE.txt
index 15e3bef..15e3bef 100644..100755
--- a/src/common/LICENSE.txt
+++ b/src/common/LICENSE.txt
diff --git a/src/common/acl.c b/src/common/acl.c
index c8e0488..c8e0488 100644..100755
--- a/src/common/acl.c
+++ b/src/common/acl.c
diff --git a/src/common/acl.h b/src/common/acl.h
index 7ce8f26..7ce8f26 100644..100755
--- a/src/common/acl.h
+++ b/src/common/acl.h
diff --git a/src/common/base32.c b/src/common/base32.c
index 43b86c1..43b86c1 100644..100755
--- a/src/common/base32.c
+++ b/src/common/base32.c
diff --git a/src/common/base32.h b/src/common/base32.h
index 45df9fa..45df9fa 100644..100755
--- a/src/common/base32.h
+++ b/src/common/base32.h
diff --git a/src/common/base32hex.c b/src/common/base32hex.c
index cd2d2ce..cd2d2ce 100644..100755
--- a/src/common/base32hex.c
+++ b/src/common/base32hex.c
diff --git a/src/common/base32hex.h b/src/common/base32hex.h
index 9ac4fa8..9ac4fa8 100644..100755
--- a/src/common/base32hex.h
+++ b/src/common/base32hex.h
diff --git a/src/common/base64.c b/src/common/base64.c
new file mode 100755
index 0000000..f1f601c
--- /dev/null
+++ b/src/common/base64.c
@@ -0,0 +1,574 @@
+/* base64.c -- Encode binary data using printable characters.
+ Copyright (C) 1999-2001, 2004-2006, 2009-2012 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Simon Josefsson. Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking. Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ * FAIL: input was not valid base64
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ * FAIL: input too long
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+#include <config.h>
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+#include <string.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+ return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+ If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+ possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+ terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen)
+{
+ static const char b64str[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ while (inlen && outlen)
+ {
+ *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ = b64str[((to_uchar (in[0]) << 4)
+ + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+ & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ =
+ (inlen
+ ? b64str[((to_uchar (in[1]) << 2)
+ + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+ & 0x3f]
+ : '=');
+ if (!--outlen)
+ break;
+ *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+ if (!--outlen)
+ break;
+ if (inlen)
+ inlen--;
+ if (inlen)
+ in += 3;
+ }
+
+ if (outlen)
+ *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+ from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+ the length of the encoded data, excluding the terminating zero. On
+ return, the OUT variable will hold a pointer to newly allocated
+ memory that must be deallocated by the caller. If output string
+ length would overflow, 0 is returned and OUT is set to NULL. If
+ memory allocation failed, OUT is set to NULL, and the return value
+ indicates length of the requested memory block, i.e.,
+ BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+ size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+ /* Check for overflow in outlen computation.
+ *
+ * If there is no overflow, outlen >= inlen.
+ *
+ * If the operation (inlen + 2) overflows then it yields at most +1, so
+ * outlen is 0.
+ *
+ * If the multiplication overflows, we lose at least half of the
+ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+ * (inlen > 4).
+ */
+ if (inlen > outlen)
+ {
+ *out = NULL;
+ return 0;
+ }
+
+ *out = malloc (outlen);
+ if (!*out)
+ return outlen;
+
+ base64_encode (in, inlen, *out, outlen);
+
+ return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+ (think EBCDIC). However, it does assume that the characters in the
+ Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX
+ 1003.1-2001 require that char and unsigned char are 8-bit
+ quantities, though, taking care of that problem. But this may be a
+ potential problem on non-POSIX C99 platforms.
+
+ IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+ as the formal parameter rather than "x". */
+#define B64(_) \
+ ((_) == 'A' ? 0 \
+ : (_) == 'B' ? 1 \
+ : (_) == 'C' ? 2 \
+ : (_) == 'D' ? 3 \
+ : (_) == 'E' ? 4 \
+ : (_) == 'F' ? 5 \
+ : (_) == 'G' ? 6 \
+ : (_) == 'H' ? 7 \
+ : (_) == 'I' ? 8 \
+ : (_) == 'J' ? 9 \
+ : (_) == 'K' ? 10 \
+ : (_) == 'L' ? 11 \
+ : (_) == 'M' ? 12 \
+ : (_) == 'N' ? 13 \
+ : (_) == 'O' ? 14 \
+ : (_) == 'P' ? 15 \
+ : (_) == 'Q' ? 16 \
+ : (_) == 'R' ? 17 \
+ : (_) == 'S' ? 18 \
+ : (_) == 'T' ? 19 \
+ : (_) == 'U' ? 20 \
+ : (_) == 'V' ? 21 \
+ : (_) == 'W' ? 22 \
+ : (_) == 'X' ? 23 \
+ : (_) == 'Y' ? 24 \
+ : (_) == 'Z' ? 25 \
+ : (_) == 'a' ? 26 \
+ : (_) == 'b' ? 27 \
+ : (_) == 'c' ? 28 \
+ : (_) == 'd' ? 29 \
+ : (_) == 'e' ? 30 \
+ : (_) == 'f' ? 31 \
+ : (_) == 'g' ? 32 \
+ : (_) == 'h' ? 33 \
+ : (_) == 'i' ? 34 \
+ : (_) == 'j' ? 35 \
+ : (_) == 'k' ? 36 \
+ : (_) == 'l' ? 37 \
+ : (_) == 'm' ? 38 \
+ : (_) == 'n' ? 39 \
+ : (_) == 'o' ? 40 \
+ : (_) == 'p' ? 41 \
+ : (_) == 'q' ? 42 \
+ : (_) == 'r' ? 43 \
+ : (_) == 's' ? 44 \
+ : (_) == 't' ? 45 \
+ : (_) == 'u' ? 46 \
+ : (_) == 'v' ? 47 \
+ : (_) == 'w' ? 48 \
+ : (_) == 'x' ? 49 \
+ : (_) == 'y' ? 50 \
+ : (_) == 'z' ? 51 \
+ : (_) == '0' ? 52 \
+ : (_) == '1' ? 53 \
+ : (_) == '2' ? 54 \
+ : (_) == '3' ? 55 \
+ : (_) == '4' ? 56 \
+ : (_) == '5' ? 57 \
+ : (_) == '6' ? 58 \
+ : (_) == '7' ? 59 \
+ : (_) == '8' ? 60 \
+ : (_) == '9' ? 61 \
+ : (_) == '+' ? 62 \
+ : (_) == '/' ? 63 \
+ : -1)
+
+static const signed char b64[0x100] = {
+ B64 (0), B64 (1), B64 (2), B64 (3),
+ B64 (4), B64 (5), B64 (6), B64 (7),
+ B64 (8), B64 (9), B64 (10), B64 (11),
+ B64 (12), B64 (13), B64 (14), B64 (15),
+ B64 (16), B64 (17), B64 (18), B64 (19),
+ B64 (20), B64 (21), B64 (22), B64 (23),
+ B64 (24), B64 (25), B64 (26), B64 (27),
+ B64 (28), B64 (29), B64 (30), B64 (31),
+ B64 (32), B64 (33), B64 (34), B64 (35),
+ B64 (36), B64 (37), B64 (38), B64 (39),
+ B64 (40), B64 (41), B64 (42), B64 (43),
+ B64 (44), B64 (45), B64 (46), B64 (47),
+ B64 (48), B64 (49), B64 (50), B64 (51),
+ B64 (52), B64 (53), B64 (54), B64 (55),
+ B64 (56), B64 (57), B64 (58), B64 (59),
+ B64 (60), B64 (61), B64 (62), B64 (63),
+ B64 (64), B64 (65), B64 (66), B64 (67),
+ B64 (68), B64 (69), B64 (70), B64 (71),
+ B64 (72), B64 (73), B64 (74), B64 (75),
+ B64 (76), B64 (77), B64 (78), B64 (79),
+ B64 (80), B64 (81), B64 (82), B64 (83),
+ B64 (84), B64 (85), B64 (86), B64 (87),
+ B64 (88), B64 (89), B64 (90), B64 (91),
+ B64 (92), B64 (93), B64 (94), B64 (95),
+ B64 (96), B64 (97), B64 (98), B64 (99),
+ B64 (100), B64 (101), B64 (102), B64 (103),
+ B64 (104), B64 (105), B64 (106), B64 (107),
+ B64 (108), B64 (109), B64 (110), B64 (111),
+ B64 (112), B64 (113), B64 (114), B64 (115),
+ B64 (116), B64 (117), B64 (118), B64 (119),
+ B64 (120), B64 (121), B64 (122), B64 (123),
+ B64 (124), B64 (125), B64 (126), B64 (127),
+ B64 (128), B64 (129), B64 (130), B64 (131),
+ B64 (132), B64 (133), B64 (134), B64 (135),
+ B64 (136), B64 (137), B64 (138), B64 (139),
+ B64 (140), B64 (141), B64 (142), B64 (143),
+ B64 (144), B64 (145), B64 (146), B64 (147),
+ B64 (148), B64 (149), B64 (150), B64 (151),
+ B64 (152), B64 (153), B64 (154), B64 (155),
+ B64 (156), B64 (157), B64 (158), B64 (159),
+ B64 (160), B64 (161), B64 (162), B64 (163),
+ B64 (164), B64 (165), B64 (166), B64 (167),
+ B64 (168), B64 (169), B64 (170), B64 (171),
+ B64 (172), B64 (173), B64 (174), B64 (175),
+ B64 (176), B64 (177), B64 (178), B64 (179),
+ B64 (180), B64 (181), B64 (182), B64 (183),
+ B64 (184), B64 (185), B64 (186), B64 (187),
+ B64 (188), B64 (189), B64 (190), B64 (191),
+ B64 (192), B64 (193), B64 (194), B64 (195),
+ B64 (196), B64 (197), B64 (198), B64 (199),
+ B64 (200), B64 (201), B64 (202), B64 (203),
+ B64 (204), B64 (205), B64 (206), B64 (207),
+ B64 (208), B64 (209), B64 (210), B64 (211),
+ B64 (212), B64 (213), B64 (214), B64 (215),
+ B64 (216), B64 (217), B64 (218), B64 (219),
+ B64 (220), B64 (221), B64 (222), B64 (223),
+ B64 (224), B64 (225), B64 (226), B64 (227),
+ B64 (228), B64 (229), B64 (230), B64 (231),
+ B64 (232), B64 (233), B64 (234), B64 (235),
+ B64 (236), B64 (237), B64 (238), B64 (239),
+ B64 (240), B64 (241), B64 (242), B64 (243),
+ B64 (244), B64 (245), B64 (246), B64 (247),
+ B64 (248), B64 (249), B64 (250), B64 (251),
+ B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+ false otherwise. Note that '=' is padding and not considered to be
+ part of the alphabet. */
+bool
+isbase64 (char ch)
+{
+ return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Initialize decode-context buffer, CTX. */
+void
+base64_decode_ctx_init (struct base64_decode_context *ctx)
+{
+ ctx->i = 0;
+}
+
+/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
+ none of those four is a newline, then return *IN. Otherwise, copy up to
+ 4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
+ index CTX->i and setting CTX->i to reflect the number of bytes copied,
+ and return CTX->buf. In either case, advance *IN to point to the byte
+ after the last one processed, and set *N_NON_NEWLINE to the number of
+ verified non-newline bytes accessible through the returned pointer. */
+static inline char *
+get_4 (struct base64_decode_context *ctx,
+ char const *restrict *in, char const *restrict in_end,
+ size_t *n_non_newline)
+{
+ if (ctx->i == 4)
+ ctx->i = 0;
+
+ if (ctx->i == 0)
+ {
+ char const *t = *in;
+ if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
+ {
+ /* This is the common case: no newline. */
+ *in += 4;
+ *n_non_newline = 4;
+ return (char *) t;
+ }
+ }
+
+ {
+ /* Copy non-newline bytes into BUF. */
+ char const *p = *in;
+ while (p < in_end)
+ {
+ char c = *p++;
+ if (c != '\n')
+ {
+ ctx->buf[ctx->i++] = c;
+ if (ctx->i == 4)
+ break;
+ }
+ }
+
+ *in = p;
+ *n_non_newline = ctx->i;
+ return ctx->buf;
+ }
+}
+
+#define return_false \
+ do \
+ { \
+ *outp = out; \
+ return false; \
+ } \
+ while (false)
+
+/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
+ into the output buffer, *OUT, of size *OUTLEN bytes. Return true if
+ decoding is successful, false otherwise. If *OUTLEN is too small,
+ as many bytes as possible are written to *OUT. On return, advance
+ *OUT to point to the byte after the last one written, and decrement
+ *OUTLEN to reflect the number of bytes remaining in *OUT. */
+static inline bool
+decode_4 (char const *restrict in, size_t inlen,
+ char *restrict *outp, size_t *outleft)
+{
+ char *out = *outp;
+ if (inlen < 2)
+ return false;
+
+ if (!isbase64 (in[0]) || !isbase64 (in[1]))
+ return false;
+
+ if (*outleft)
+ {
+ *out++ = ((b64[to_uchar (in[0])] << 2)
+ | (b64[to_uchar (in[1])] >> 4));
+ --*outleft;
+ }
+
+ if (inlen == 2)
+ return_false;
+
+ if (in[2] == '=')
+ {
+ if (inlen != 4)
+ return_false;
+
+ if (in[3] != '=')
+ return_false;
+ }
+ else
+ {
+ if (!isbase64 (in[2]))
+ return_false;
+
+ if (*outleft)
+ {
+ *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+ | (b64[to_uchar (in[2])] >> 2));
+ --*outleft;
+ }
+
+ if (inlen == 3)
+ return_false;
+
+ if (in[3] == '=')
+ {
+ if (inlen != 4)
+ return_false;
+ }
+ else
+ {
+ if (!isbase64 (in[3]))
+ return_false;
+
+ if (*outleft)
+ {
+ *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+ | b64[to_uchar (in[3])]);
+ --*outleft;
+ }
+ }
+ }
+
+ *outp = out;
+ return true;
+}
+
+/* Decode base64-encoded input array IN of length INLEN to output array
+ OUT that can hold *OUTLEN bytes. The input data may be interspersed
+ with newlines. Return true if decoding was successful, i.e. if the
+ input was valid base64 data, false otherwise. If *OUTLEN is too
+ small, as many bytes as possible will be written to OUT. On return,
+ *OUTLEN holds the length of decoded bytes in OUT. Note that as soon
+ as any non-alphabet, non-newline character is encountered, decoding
+ is stopped and false is returned. If INLEN is zero, then process
+ only whatever data is stored in CTX.
+
+ Initially, CTX must have been initialized via base64_decode_ctx_init.
+ Subsequent calls to this function must reuse whatever state is recorded
+ in that buffer. It is necessary for when a quadruple of base64 input
+ bytes spans two input buffers.
+
+ If CTX is NULL then newlines are treated as garbage and the input
+ buffer is processed as a unit. */
+
+bool
+base64_decode_ctx (struct base64_decode_context *ctx,
+ const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen)
+{
+ size_t outleft = *outlen;
+ bool ignore_newlines = ctx != NULL;
+ bool flush_ctx = false;
+ unsigned int ctx_i = 0;
+
+ if (ignore_newlines)
+ {
+ ctx_i = ctx->i;
+ flush_ctx = inlen == 0;
+ }
+
+
+ while (true)
+ {
+ size_t outleft_save = outleft;
+ if (ctx_i == 0 && !flush_ctx)
+ {
+ while (true)
+ {
+ /* Save a copy of outleft, in case we need to re-parse this
+ block of four bytes. */
+ outleft_save = outleft;
+ if (!decode_4 (in, inlen, &out, &outleft))
+ break;
+
+ in += 4;
+ inlen -= 4;
+ }
+ }
+
+ if (inlen == 0 && !flush_ctx)
+ break;
+
+ /* Handle the common case of 72-byte wrapped lines.
+ This also handles any other multiple-of-4-byte wrapping. */
+ if (inlen && *in == '\n' && ignore_newlines)
+ {
+ ++in;
+ --inlen;
+ continue;
+ }
+
+ /* Restore OUT and OUTLEFT. */
+ out -= outleft_save - outleft;
+ outleft = outleft_save;
+
+ {
+ char const *in_end = in + inlen;
+ char const *non_nl;
+
+ if (ignore_newlines)
+ non_nl = get_4 (ctx, &in, in_end, &inlen);
+ else
+ non_nl = in; /* Might have nl in this case. */
+
+ /* If the input is empty or consists solely of newlines (0 non-newlines),
+ then we're done. Likewise if there are fewer than 4 bytes when not
+ flushing context and not treating newlines as garbage. */
+ if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines))
+ {
+ inlen = 0;
+ break;
+ }
+ if (!decode_4 (non_nl, inlen, &out, &outleft))
+ break;
+
+ inlen = in_end - in;
+ }
+ }
+
+ *outlen -= outleft;
+
+ return inlen == 0;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+ data stored in IN of size INLEN to the *OUT buffer. On return, the
+ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
+ if the caller is not interested in the decoded length. *OUT may be
+ NULL to indicate an out of memory error, in which case *OUTLEN
+ contains the size of the memory block needed. The function returns
+ true on successful decoding and memory allocation errors. (Use the
+ *OUT and *OUTLEN parameters to differentiate between successful
+ decoding and memory error.) The function returns false if the
+ input was invalid, in which case *OUT is NULL and *OUTLEN is
+ undefined. */
+bool
+base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+ const char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ /* This may allocate a few bytes too many, depending on input,
+ but it's not worth the extra CPU time to compute the exact size.
+ The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
+ input ends with "=" and minus another 1 if the input ends with "==".
+ Dividing before multiplying avoids the possibility of overflow. */
+ size_t needlen = 3 * (inlen / 4) + 3;
+
+ *out = malloc (needlen);
+ if (!*out)
+ return true;
+
+ if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
+ {
+ free (*out);
+ *out = NULL;
+ return false;
+ }
+
+ if (outlen)
+ *outlen = needlen;
+
+ return true;
+}
diff --git a/src/common/base64.h b/src/common/base64.h
new file mode 100755
index 0000000..aa0b696
--- /dev/null
+++ b/src/common/base64.h
@@ -0,0 +1,60 @@
+/* base64.h -- Encode binary data using printable characters.
+ Copyright (C) 2004-2006, 2009-2012 Free Software Foundation, Inc.
+ Written by Simon Josefsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+ integer >= n/k, i.e., the ceiling of n/k. */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+struct base64_decode_context
+{
+ unsigned int i;
+ char buf[4];
+};
+
+extern bool isbase64 (char ch);
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
+
+extern bool base64_decode_ctx (struct base64_decode_context *ctx,
+ const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+ const char *in, size_t inlen,
+ char **out, size_t *outlen);
+
+#define base64_decode(in, inlen, out, outlen) \
+ base64_decode_ctx (NULL, in, inlen, out, outlen)
+
+#define base64_decode_alloc(in, inlen, out, outlen) \
+ base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
+
+#endif /* BASE64_H */
diff --git a/src/common/crc.h b/src/common/crc.h
index cf2e123..7d748be 100644..100755
--- a/src/common/crc.h
+++ b/src/common/crc.h
@@ -77,3 +77,5 @@ static inline crc_t crc_finalize(crc_t crc)
#endif /* __CRC_H__ */
+
+/*! @} */
diff --git a/src/common/dSFMT-params.h b/src/common/dSFMT-params.h
index c779d8a..c779d8a 100644..100755
--- a/src/common/dSFMT-params.h
+++ b/src/common/dSFMT-params.h
diff --git a/src/common/dSFMT-params521.h b/src/common/dSFMT-params521.h
index f771dc1..f771dc1 100644..100755
--- a/src/common/dSFMT-params521.h
+++ b/src/common/dSFMT-params521.h
diff --git a/src/common/dSFMT.c b/src/common/dSFMT.c
index 090bb80..090bb80 100644..100755
--- a/src/common/dSFMT.c
+++ b/src/common/dSFMT.c
diff --git a/src/common/dSFMT.h b/src/common/dSFMT.h
index f1aa9bd..f1aa9bd 100644..100755
--- a/src/common/dSFMT.h
+++ b/src/common/dSFMT.h
diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c
deleted file mode 100644
index 24f2aaa..0000000
--- a/src/common/dynamic-array.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include <pthread.h>
-#include <assert.h>
-#include <stdio.h>
-
-#include <urcu.h>
-
-//#include "common.h"
-#include "common/dynamic-array.h"
-
-#ifndef ERR_ALLOC_FAILED
-#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d\n", \
- __FILE__, __LINE__)
-#endif
-
-//#define DA_DEBUG
-
-#ifndef dbg_da
-#ifdef DA_DEBUG
-#define dbg_da(msg...) fprintf(stderr, msg)
-#else
-#define dbg_da(msg...)
-#endif
-#endif
-
-/*----------------------------------------------------------------------------*/
-/* Private functions */
-/*----------------------------------------------------------------------------*/
-
-enum da_resize_type {
- DA_LARGER, DA_SMALLER
-};
-
-typedef enum da_resize_type da_resize_type_t;
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \retval 1 On success.
- * \retval -1 On failure.
- */
-static int da_resize(da_array_t *array, da_resize_type_t type)
-{
- dbg_da("da_resize(): array pointer: %p, items pointer: %p\n", array,
- array->items);
-
- unsigned new_size = ((type == DA_LARGER)
- ? (array->allocated *= 2)
- : (array->allocated /= 2));
-
- void *new_items = malloc(new_size * array->item_size);
- if (new_items == NULL) {
- ERR_ALLOC_FAILED;
- return -1;
- }
-
- dbg_da("Place for new items: %p\n", new_items);
-
- // copy the contents from the old array to the new
- memcpy(new_items, array->items, array->count * array->item_size);
-
- // do RCU update
- void *old_items = rcu_xchg_pointer(&array->items, new_items);
- array->allocated = new_size;
-
- dbg_da("Old items pointer: %p\n", old_items);
-
- // wait for readers to finish
- //synchronize_rcu();
- // deallocate the old array
- dbg_da("RCU synchronized, deallocating old items array at address %p."
- "\n", old_items);
- free(old_items);
-
- return 1;
-}
-
-/*----------------------------------------------------------------------------*/
-/* Public functions */
-/*----------------------------------------------------------------------------*/
-
-da_array_t *da_create(unsigned count, size_t item_size)
-{
- da_array_t *a = (da_array_t *)malloc(sizeof(da_array_t));
- if (a == NULL) {
- ERR_ALLOC_FAILED;
- return NULL;
- }
- da_initialize(a, count, item_size);
- return a;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int da_initialize(da_array_t *array, unsigned count, size_t item_size)
-{
- assert(array != NULL);
- pthread_mutex_init(&array->mtx, NULL);
- pthread_mutex_lock(&array->mtx);
-
- array->items = malloc(count * item_size);
- if (array->items == NULL) {
- array->allocated = 0;
- array->count = 0;
- ERR_ALLOC_FAILED;
- pthread_mutex_unlock(&array->mtx);
- return -1;
- }
-
- array->allocated = count;
- array->count = 0;
- array->item_size = item_size;
- memset(array->items, 0, count * item_size);
-
- pthread_mutex_unlock(&array->mtx);
- return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int da_reserve(da_array_t *array, unsigned count)
-{
- pthread_mutex_lock(&array->mtx);
- unsigned res = 0;
-
- assert(array->allocated >= array->count);
- if ((array->allocated - array->count) >= count) {
- dbg_da("Enough place in the array, no resize needed.\n");
- res = 0;
- } else {
- dbg_da("Resizing array.\n");
- res = da_resize(array, DA_LARGER);
- }
- pthread_mutex_unlock(&array->mtx);
-
- return res;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int da_occupy(da_array_t *array, unsigned count)
-{
- pthread_mutex_lock(&array->mtx);
- unsigned res = 0;
- assert(array->allocated >= array->count);
-
- if ((array->allocated - array->count) < count) {
- dbg_da("Not enough place to occupy.\n");
- res = -1;
- } else {
- array->count += count;
- }
-
- pthread_mutex_unlock(&array->mtx);
- return res;
-}
-
-/*----------------------------------------------------------------------------*/
-
-unsigned da_try_reserve(const da_array_t *array, unsigned count)
-{
- assert(array->allocated >= array->count);
- if ((array->allocated - array->count) >= count) {
- return 0;
- }
-
- return 1;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void da_release(da_array_t *array, unsigned count)
-{
- pthread_mutex_lock(&array->mtx);
-
- assert(array->allocated >= array->count);
- assert(array->count >= count);
- dbg_da("Decreasing count of items in array.\n");
- array->count -= count;
-
- pthread_mutex_unlock(&array->mtx);
-}
-
-/*----------------------------------------------------------------------------*/
-
-void da_destroy(da_array_t *array)
-{
- pthread_mutex_lock(&array->mtx);
- void *old_items = rcu_dereference(array->items);
- rcu_set_pointer(&array->items, NULL);
- pthread_mutex_unlock(&array->mtx);
-
- //synchronize_rcu();
- free(old_items);
- pthread_mutex_destroy(&array->mtx);
-}
-
-/*----------------------------------------------------------------------------*/
-
-void *da_get_items(const da_array_t *array)
-{
- return array->items;
-}
-
-/*----------------------------------------------------------------------------*/
-
-unsigned da_get_count(const da_array_t *array)
-{
- return array->count;
-}
diff --git a/src/common/dynamic-array.h b/src/common/dynamic-array.h
deleted file mode 100644
index 77a5d13..0000000
--- a/src/common/dynamic-array.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-/*!
- * \file dynamic-array.h
- *
- * \author Lubos Slovak <lubos.slovak@nic.cz>
- *
- * \brief Safe dynamic array implementation.
- *
- * \todo Somehow check if the array is initialized and do not use otherwise.
- * Maybe some magic, or so.
- * \todo This structure is too slow because of the mutex.
- *
- * \addtogroup common_lib
- * @{
- */
-
-#ifndef _KNOTD_COMMON_DYNAMIC_ARRAY_H_
-#define _KNOTD_COMMON_DYNAMIC_ARRAY_H_
-
-#include <string.h>
-#include <pthread.h>
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Dynamic array structure.
- *
- * Before using the dynamic array, it must be initialized using da_initialize().
- * When getting individual items always use da_get_items() to obtain pointer to
- * the actual array.
- *
- * Items in the array cannot be dereferenced (it uses void * for storing the
- * the items). It is needed to type-cast the item array (obtained by calling
- * da_get_items()) to a proper type before dereferencing.
- *
- * When adding items, first reserve enough space for them by callling
- * da_reserve() and subsequently tell the array about the inserted items by
- * calling da_occupy(). When removing, the array must be told about the fact
- * by calling da_release().
- *
- * For getting the actual number of items in array use da_get_count().
- *
- * When the array is no more needed, the da_destroy() function must be called
- * before deallocating the structure.
- */
-struct da_array {
- /*! \brief The actual array. The items can't be dereferenced directly.*/
- void *items;
-
- /*!
- * \brief Size of the stored items in bytes (used in counting of space
- * needed.
- */
- size_t item_size;
-
- /*!
- * \brief Size of allocated space in number of items that can be stored.
- */
- unsigned allocated;
-
- /*! \brief Number of items actually stored in the array. */
- unsigned count;
-
- /*! \brief Mutex. */
- pthread_mutex_t mtx;
-};
-
-typedef struct da_array da_array_t;
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Creates and initializes the dynamic array.
- *
- * Initialization comprises of allocating place for \a count items of size
- * \a item_size and setting the items to zeros.
- *
- * \retval 0 if successful.
- * \retval -1 if not successful.
- */
-da_array_t *da_create(unsigned count, size_t item_size);
-
-/*!
- * \brief Initializes the dynamic array.
- *
- * Initialization comprises of allocating place for \a count items of size
- * \a item_size and setting the items to zeros.
- *
- * \retval 0 if successful.
- * \retval -1 if not successful.
- */
-int da_initialize(da_array_t *array, unsigned count, size_t item_size);
-
-/*!
- * \brief Reserves space for \a count more items.
- *
- * \retval 0 if successful and resizing was not necessary.
- * \retval 1 if successful and the array was enlarged.
- * \retval -1 if not successful - resizing was needed but could not be done.
- */
-int da_reserve(da_array_t *array, unsigned count);
-
-/*!
- * \brief Increases the number of items in array by \a count.
- *
- * \retval 0 If successful.
- * \retval -1 If not successful (not enough allocated space, i.e. must run
- * da_reserve()).
- */
-int da_occupy(da_array_t *array, unsigned count);
-
-/*!
- * \brief Tries to reserve space for \a count more items.
- *
- * \retval 0 if successful and resizing is not necessary.
- * \retval 1 if successful but the array will need to be resized.
- */
-unsigned da_try_reserve(const da_array_t *array, unsigned count);
-
-/*!
- * \brief Releases space taken by \a count items.
- */
-void da_release(da_array_t *array, unsigned count);
-
-/*!
- * \brief Poperly deallocates the array.
- */
-void da_destroy(da_array_t *array);
-
-/*!
- * \brief Returns the array of items as a void *.
- */
-void *da_get_items(const da_array_t *array);
-
-/*!
- * \brief Returns count of items in the array.
- */
-unsigned da_get_count(const da_array_t *array);
-
-/*----------------------------------------------------------------------------*/
-
-#endif /* _KNOTD_COMMON_DYNAMIC_ARRAY_H_ */
-
-/*! @} */
diff --git a/src/common/errors.c b/src/common/errors.c
index 3b770e9..3b770e9 100644..100755
--- a/src/common/errors.c
+++ b/src/common/errors.c
diff --git a/src/common/errors.h b/src/common/errors.h
index a2773ac..a2773ac 100644..100755
--- a/src/common/errors.h
+++ b/src/common/errors.h
diff --git a/src/common/evqueue.c b/src/common/evqueue.c
index 7614111..240ced6 100644..100755
--- a/src/common/evqueue.c
+++ b/src/common/evqueue.c
@@ -126,8 +126,11 @@ void evqueue_free(evqueue_t **q)
*q = 0;
/* Deinitialize. */
- close(eq->fds[EVQUEUE_READFD]);
- close(eq->fds[EVQUEUE_WRITEFD]);
+ for (int i = 0; i < 2; ++i) {
+ if (eq->fds[i] > -1) {
+ close(eq->fds[i]);
+ }
+ }
free(eq);
}
diff --git a/src/common/evqueue.h b/src/common/evqueue.h
index ffb3860..ffb3860 100644..100755
--- a/src/common/evqueue.h
+++ b/src/common/evqueue.h
diff --git a/src/common/evsched.c b/src/common/evsched.c
index cc2cf7c..8b6f721 100644..100755
--- a/src/common/evsched.c
+++ b/src/common/evsched.c
@@ -26,6 +26,15 @@
#define OPENBSD_SLAB_BROKEN
#endif
+/* Heap only cares about x<y. */
+static int compare_event_heap_nodes(event_t **e1, event_t **e2)
+{
+ if (timercmp(&(*e1)->tv, &(*e2)->tv, <)) return -1;
+ if (timercmp(&(*e1)->tv, &(*e2)->tv, >)) return 1;
+ return 0;
+}
+
+
/*!
* \brief Set event timer to T (now) + dt miliseconds.
*/
@@ -69,7 +78,7 @@ evsched_t *evsched_new()
#ifndef OPENBSD_SLAB_BROKEN
slab_cache_init(&s->cache.alloc, sizeof(event_t));
#endif
- init_list(&s->calendar);
+ heap_init(&s->heap, sizeof(event_t *), compare_event_heap_nodes, 0, NULL);
return s;
}
@@ -86,10 +95,16 @@ void evsched_delete(evsched_t **s)
pthread_mutex_destroy(&(*s)->rl);
pthread_mutex_destroy(&(*s)->mx);
pthread_cond_destroy(&(*s)->notify);
- node *n = 0, *nxt = 0;
- WALK_LIST_DELSAFE(n, nxt, (*s)->calendar) {
- evsched_event_free((*s), (event_t*)n);
+
+ while (! EMPTY_HEAP(&(*s)->heap)) /* FIXME: Would be faster to simply walk through the array */
+ {
+ event_t *e = *((event_t**)(HHEAD(&(*s)->heap)));
+ heap_delmin(&(*s)->heap);
+ evsched_event_free((*s), e);
}
+
+ free((*s)->heap.data);
+ (*s)->heap.data = NULL;;
#ifndef OPENBSD_SLAB_BROKEN
/* Free allocator. */
@@ -154,25 +169,26 @@ event_t* evsched_next(evsched_t *s)
while(1) {
/* Check event queue. */
- if (!EMPTY_LIST(s->calendar)) {
+ if (!EMPTY_HEAP(&s->heap)) {
/* Get current time. */
struct timeval dt;
gettimeofday(&dt, 0);
/* Get next event. */
- event_t *next_ev = HEAD(s->calendar);
+ event_t *next_ev = *((event_t**)HHEAD(&s->heap));
/* Immediately return. */
if (timercmp(&dt, &next_ev->tv, >=)) {
s->current = next_ev;
- rem_node(&next_ev->n);
+ heap_delmin(&s->heap);
pthread_mutex_unlock(&s->mx);
pthread_mutex_lock(&s->rl);
return next_ev;
}
/* Wait for next event or interrupt. Unlock calendar. */
+ /* FIXME: Blocks this the possibility to add any event earlier? */
struct timespec ts;
ts.tv_sec = next_ev->tv.tv_sec;
ts.tv_nsec = next_ev->tv.tv_usec * 1000L;
@@ -214,31 +230,12 @@ int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt)
/* Update event timer. */
evsched_settimer(ev, dt);
-
+ ev->parent = s;
+
/* Lock calendar. */
pthread_mutex_lock(&s->mx);
-
- /* Schedule event. */
- node *n = 0, *prev = 0;
- if (!EMPTY_LIST(s->calendar)) {
- WALK_LIST(n, s->calendar) {
- event_t* cur = (event_t *)n;
- if (timercmp(&cur->tv, &ev->tv, <)) {
- prev = n;
- } else {
- break;
- }
- }
- }
-
- /* Append to list. */
- ev->parent = s;
- if (prev) {
- insert_node(&ev->n, prev);
- } else {
- add_head(&s->calendar, &ev->n);
- }
-
+
+ heap_insert(&s->heap, &ev);
/* Unlock calendar. */
pthread_cond_signal(&s->notify);
@@ -293,6 +290,8 @@ event_t* evsched_schedule_term(evsched_t *s, uint32_t dt)
int evsched_cancel(evsched_t *s, event_t *ev)
{
+ int found;
+
if (!s || !ev) {
return -1;
}
@@ -303,19 +302,8 @@ int evsched_cancel(evsched_t *s, event_t *ev)
/* Lock calendar. */
pthread_mutex_lock(&s->mx);
- /* Find in list. */
- event_t *n = 0;
- int found = 0;
- WALK_LIST(n, s->calendar) {
- if (n == ev) {
- found = 1;
- break;
- }
- }
-
- /* Remove from list. */
- if (found) {
- rem_node(&ev->n);
+ if ((found = heap_find(&s->heap, &ev))) {
+ heap_delete(&s->heap, found);
}
/* Unlock calendar. */
diff --git a/src/common/evsched.h b/src/common/evsched.h
index 8ca9c62..47bf672 100644..100755
--- a/src/common/evsched.h
+++ b/src/common/evsched.h
@@ -68,7 +68,7 @@
#include <pthread.h>
#include "common/slab/slab.h"
-#include "common/lists.h"
+#include "common/heap.h"
#include "common/evqueue.h"
/*!
@@ -92,7 +92,7 @@ typedef struct {
event_t *current; /*!< Current running event. */
pthread_mutex_t mx; /*!< Event queue locking. */
pthread_cond_t notify; /*!< Event queue notification. */
- list calendar; /*!< Event calendar. */
+ struct heap heap;
struct {
slab_cache_t alloc; /*!< Events SLAB cache. */
pthread_mutex_t lock; /*!< Events cache spin lock. */
diff --git a/src/common/fdset.c b/src/common/fdset.c
index c915e01..c915e01 100644..100755
--- a/src/common/fdset.c
+++ b/src/common/fdset.c
diff --git a/src/common/fdset.h b/src/common/fdset.h
index 4fbd9bc..4038083 100644..100755
--- a/src/common/fdset.h
+++ b/src/common/fdset.h
@@ -74,7 +74,7 @@ typedef struct fdset_it_t {
/*!
* \brief File descriptor set implementation backend.
- * \notice Functions documentation following.
+ * \note Functions documentation following.
* \internal
*/
struct fdset_backend_t
diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c
index d4481b5..d4481b5 100644..100755
--- a/src/common/fdset_epoll.c
+++ b/src/common/fdset_epoll.c
diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h
index 58f25f8..58f25f8 100644..100755
--- a/src/common/fdset_epoll.h
+++ b/src/common/fdset_epoll.h
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
index 108c572..108c572 100644..100755
--- a/src/common/fdset_kqueue.c
+++ b/src/common/fdset_kqueue.c
diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h
index 4b650a7..4b650a7 100644..100755
--- a/src/common/fdset_kqueue.h
+++ b/src/common/fdset_kqueue.h
diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c
index 19804f5..9b1c135 100644..100755
--- a/src/common/fdset_poll.c
+++ b/src/common/fdset_poll.c
@@ -71,9 +71,10 @@ int fdset_poll_add(fdset_t *fdset, int fd, int events)
}
/* Append. */
- int nid = fdset->nfds++;;
+ int nid = fdset->nfds++;
fdset->fds[nid].fd = fd;
fdset->fds[nid].events = POLLIN;
+ fdset->fds[nid].revents = 0;
return 0;
}
diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h
index 68e9e69..68e9e69 100644..100755
--- a/src/common/fdset_poll.h
+++ b/src/common/fdset_poll.h
diff --git a/src/common/general-tree.c b/src/common/general-tree.c
index e1048e7..e1048e7 100644..100755
--- a/src/common/general-tree.c
+++ b/src/common/general-tree.c
diff --git a/src/common/general-tree.h b/src/common/general-tree.h
index 552638a..552638a 100644..100755
--- a/src/common/general-tree.h
+++ b/src/common/general-tree.h
diff --git a/src/common/heap.c b/src/common/heap.c
new file mode 100755
index 0000000..6fefb11
--- /dev/null
+++ b/src/common/heap.c
@@ -0,0 +1,149 @@
+/*
+ * Universal Heap Macros
+ *
+ * (c) 2012 Ondrej Filip <feela@network.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+/***
+ * [[intro]]
+ * Introduction
+ * ------------
+ *
+ * Binary heap is a simple data structure, which for example supports efficient insertions, deletions
+ * and access to the minimal inserted item. We define several macros for such operations.
+ * Note that because of simplicity of heaps, we have decided to define direct macros instead
+ * of a <<generic:,macro generator>> as for several other data structures in the Libucw.
+ *
+ * A heap is represented by a number of elements and by an array of values. Beware that we
+ * index this array from one, not from zero as do the standard C arrays.
+ *
+ * Most macros use these parameters:
+ *
+ * - @type - the type of elements
+ * - @num - a variable (signed or unsigned integer) with the number of elements
+ * - @heap - a C array of type @type; the heap is stored in `heap[1] .. heap[num]`; `heap[0]` is unused
+ * - @less - a callback to compare two element values; `less(x, y)` shall return a non-zero value iff @x is lower than @y
+ * - @swap - a callback to swap two array elements; `swap(heap, i, j, t)` must swap `heap[i]` with `heap[j]` with possible help of temporary variable @t (type @type).
+ *
+ * A valid heap must follow these rules:
+ *
+ * - `num >= 0`
+ * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]`
+ *
+ * The first element `heap[1]` is always lower or equal to all other elements.
+ *
+ * [[macros]]
+ * Macros
+ * ------
+ ***/
+
+#include "heap.h"
+#include <string.h>
+#include <stdlib.h>
+
+void _def_swap(struct heap *h, void *e1, void *e2)
+{
+ if (e1 == e2) return;
+ void *tmp = HTEMPELEMENT(h);
+ memcpy(tmp, e1, h->elm_size);
+ memcpy(e1, e2, h->elm_size);
+ memcpy(e2, tmp, h->elm_size);
+}
+
+
+int heap_init(struct heap *h, int elm_size, int (*cmp)(void *, void *), int init_size, void (*swap)(struct heap *, void *, void *))
+{
+ int isize = init_size ? init_size : INITIAL_HEAP_SIZE;
+
+ h->num = 0;
+ h->max_size = isize;
+ h->cmp = cmp;
+ h->swap = swap ? swap : _def_swap;
+ h->data = malloc((isize + 1) * elm_size);
+ h->elm_size = elm_size;
+
+ return h->data ? 1 : 0;
+};
+
+static inline void _heap_bubble_down(struct heap *h, int e)
+{
+ int e1;
+ for (;;)
+ {
+ e1 = 2*e;
+ if(e1 > h->num) break;
+ if((h->cmp(HELEMENT(h, e),HELEMENT(h,e1)) < 0) && (e1 == h->num || (h->cmp(HELEMENT(h, e),HELEMENT(h,e1+1)) < 0))) break;
+ if((e1 != h->num) && (h->cmp(HELEMENT(h, e1+1), HELEMENT(h,e1)) < 0)) e1++;
+ h->swap(h,HELEMENT(h,e),HELEMENT(h,e1));
+ e = e1;
+ }
+}
+
+static inline void _heap_bubble_up(struct heap *h, int e)
+{
+ int e1;
+ while (e > 1)
+ {
+ e1 = e/2;
+ if(h->cmp(HELEMENT(h, e1),HELEMENT(h,e)) < 0) break;
+ h->swap(h,HELEMENT(h,e),HELEMENT(h,e1));
+ e = e1;
+ }
+
+}
+
+void heap_delmin(struct heap *h)
+{
+ if(h->num == 0) return;
+ if(h->num > 1)
+ {
+ h->swap(h,HHEAD(h),HELEMENT(h,h->num));
+ }
+ --h->num;
+ _heap_bubble_down(h, 1);
+}
+
+int heap_insert(struct heap *h, void *e)
+{
+ if(h->num == h->max_size)
+ {
+ h->max_size = h->max_size * HEAP_INCREASE_STEP;
+ h->data = realloc(h->data, (h->max_size + 1) * h->elm_size);
+ }
+
+ h->num++;
+ memcpy(HELEMENT(h,h->num),e,h->elm_size);
+ _heap_bubble_up(h,h->num);
+
+ return h->data ? 1 :0 ;
+}
+
+int heap_find(struct heap *h, void *elm) /* FIXME - very slow */
+{
+ int i = h->num;
+
+ while(i > 0)
+ {
+ if(h->cmp(HELEMENT(h, i),elm) == 0) break;
+ --i;
+ }
+ return i;
+}
+
+void heap_delete(struct heap *h, int e)
+{
+ h->swap(h, HELEMENT(h, e), HELEMENT(h, h->num));
+ h->num--;
+ if(h->cmp(HELEMENT(h, e), HELEMENT(h, h->num + 1)) < 0) _heap_bubble_up(h, e);
+ else _heap_bubble_down(h, e);
+
+ if ((h->num > INITIAL_HEAP_SIZE) && (h->num < h->max_size / HEAP_DECREASE_THRESHOLD))
+ {
+ h->max_size = h->max_size / HEAP_INCREASE_STEP;
+ h->data = realloc(h->data, (h->max_size + 1) * h->elm_size);
+ }
+}
+
diff --git a/src/common/heap.h b/src/common/heap.h
new file mode 100755
index 0000000..d6f8a0b
--- /dev/null
+++ b/src/common/heap.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file heap.h
+ *
+ * \author Ondrej Filip <ondrej.filip@nic.cz>
+ *
+ * \brief Universal heap support
+ *
+ *
+ * \addtogroup common_lib
+ * @{
+ */
+
+#ifndef _HEAP_H_
+#define _HEAP_H_
+
+struct heap {
+ int num; /* Number of elements */
+ int elm_size; /* Size of a single element */
+ int max_size; /* Size of allocated memory */
+ int (*cmp)(void *, void *);
+ void (*swap)(struct heap *, void *, void *);
+ void *data;
+}; /* Array follows */
+
+#define INITIAL_HEAP_SIZE 512 /* initial heap size */
+#define HEAP_INCREASE_STEP 2 /* multiplier for each inflation, keep conservative */
+#define HEAP_DECREASE_THRESHOLD 2 /* threshold for deflation, keep conservative */
+#define HTEMPELEMENT(h) ((h)->data) /* Pointer to tmp element (for swap) */
+#define HELEMENT(h,num) ((char*)(h)->data + (num) * (h)->elm_size)
+#define HHEAD(h) HELEMENT((h),1)
+#define EMPTY_HEAP(h) ((h)->num == 0) /* h->num == 0 */
+
+int heap_init(struct heap *, int, int (*cmp)(), int, void (*swap)());
+void heap_delmin(struct heap *);
+int heap_insert(struct heap *, void *);
+int heap_find(struct heap *, void *);
+void heap_delete(struct heap *, int);
+
+
+#endif /* _HEAP_H_ */
+
+/*! @} */
diff --git a/src/common/latency.c b/src/common/latency.c
index a563f58..a563f58 100644..100755
--- a/src/common/latency.c
+++ b/src/common/latency.c
diff --git a/src/common/latency.h b/src/common/latency.h
index d965c56..d965c56 100644..100755
--- a/src/common/latency.h
+++ b/src/common/latency.h
diff --git a/src/common/libtap/README b/src/common/libtap/README
index d57b81d..d57b81d 100644..100755
--- a/src/common/libtap/README
+++ b/src/common/libtap/README
diff --git a/src/common/libtap/tap.c b/src/common/libtap/tap.c
index d6bb995..d6bb995 100644..100755
--- a/src/common/libtap/tap.c
+++ b/src/common/libtap/tap.c
diff --git a/src/common/libtap/tap.h b/src/common/libtap/tap.h
index 89484f4..89484f4 100644..100755
--- a/src/common/libtap/tap.h
+++ b/src/common/libtap/tap.h
diff --git a/src/common/libtap/tap_unit.h b/src/common/libtap/tap_unit.h
index c248fde..c248fde 100644..100755
--- a/src/common/libtap/tap_unit.h
+++ b/src/common/libtap/tap_unit.h
diff --git a/src/common/lists.c b/src/common/lists.c
index 9a93733..9a93733 100644..100755
--- a/src/common/lists.c
+++ b/src/common/lists.c
diff --git a/src/common/lists.h b/src/common/lists.h
index 972ea49..897b1a9 100644..100755
--- a/src/common/lists.h
+++ b/src/common/lists.h
@@ -71,6 +71,7 @@ typedef struct list { /* In fact two overlayed nodes */
WALK_LIST_DELSAFE(n,nxt,list) { \
free(n); \
} \
+ init_list(&list); \
} while(0)
void add_tail(list *, node *);
diff --git a/src/common/log.c b/src/common/log.c
index c70f739..c70f739 100644..100755
--- a/src/common/log.c
+++ b/src/common/log.c
diff --git a/src/common/log.h b/src/common/log.h
index 305020c..305020c 100644..100755
--- a/src/common/log.h
+++ b/src/common/log.h
diff --git a/src/common/mempattern.c b/src/common/mempattern.c
index 5982e18..5982e18 100644..100755
--- a/src/common/mempattern.c
+++ b/src/common/mempattern.c
diff --git a/src/common/mempattern.h b/src/common/mempattern.h
index ae1fa78..ae1fa78 100644..100755
--- a/src/common/mempattern.h
+++ b/src/common/mempattern.h
diff --git a/src/common/modified_tree.h b/src/common/modified_tree.h
index 4c3e325..9ceddd1 100644..100755
--- a/src/common/modified_tree.h
+++ b/src/common/modified_tree.h
@@ -123,7 +123,7 @@
else if (cmp > 0) \
self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \
else if (merge) { \
- merge(&(elm->data), &(self->data)); \
+ merge(&(self->data), &(elm->data)); \
*merged = 1; } \
else \
self->field.avl_right= MOD_TREE_INSERT_##node##_##field(self->field.avl_right, elm, compare, merge, merged); \
diff --git a/src/common/print.c b/src/common/print.c
index 9764568..9764568 100644..100755
--- a/src/common/print.c
+++ b/src/common/print.c
diff --git a/src/common/print.h b/src/common/print.h
index 482f55e..482f55e 100644..100755
--- a/src/common/print.h
+++ b/src/common/print.h
diff --git a/src/common/prng.c b/src/common/prng.c
index 250a506..250a506 100644..100755
--- a/src/common/prng.c
+++ b/src/common/prng.c
diff --git a/src/common/prng.h b/src/common/prng.h
index 9f17a78..a64eabb 100644..100755
--- a/src/common/prng.h
+++ b/src/common/prng.h
@@ -39,3 +39,5 @@
double tls_rand();
#endif //_KNOTD_ACL_H_
+
+/*! @} */
diff --git a/src/common/ref.c b/src/common/ref.c
index 3b9c033..3b9c033 100644..100755
--- a/src/common/ref.c
+++ b/src/common/ref.c
diff --git a/src/common/ref.h b/src/common/ref.h
index 13a7037..13a7037 100644..100755
--- a/src/common/ref.h
+++ b/src/common/ref.h
diff --git a/src/common/skip-list.c b/src/common/skip-list.c
index cde08d7..cde08d7 100644..100755
--- a/src/common/skip-list.c
+++ b/src/common/skip-list.c
diff --git a/src/common/skip-list.h b/src/common/skip-list.h
index 784f366..784f366 100644..100755
--- a/src/common/skip-list.h
+++ b/src/common/skip-list.h
diff --git a/src/common/slab/alloc-common.h b/src/common/slab/alloc-common.h
index 32878ab..32878ab 100644..100755
--- a/src/common/slab/alloc-common.h
+++ b/src/common/slab/alloc-common.h
diff --git a/src/common/slab/slab.c b/src/common/slab/slab.c
index df30998..9a3e8de 100644..100755
--- a/src/common/slab/slab.c
+++ b/src/common/slab/slab.c
@@ -231,7 +231,7 @@ void __attribute__ ((destructor)) slab_deinit()
* Cache helper functions.
*/
-/* \notice Not used right now.
+/* \note Not used right now.
static void slab_dump(slab_t* slab) {
printf("%s: buffers (bufsize=%zuB, %u/%u free): \n",
diff --git a/src/common/slab/slab.h b/src/common/slab/slab.h
index 4ea7e31..4ea7e31 100644..100755
--- a/src/common/slab/slab.h
+++ b/src/common/slab/slab.h
diff --git a/src/common/sockaddr.c b/src/common/sockaddr.c
index b4e75ee..b4e75ee 100644..100755
--- a/src/common/sockaddr.c
+++ b/src/common/sockaddr.c
diff --git a/src/common/sockaddr.h b/src/common/sockaddr.h
index 52e621c..52e621c 100644..100755
--- a/src/common/sockaddr.h
+++ b/src/common/sockaddr.h
diff --git a/src/common/tree.h b/src/common/tree.h
index efea65b..efea65b 100644..100755
--- a/src/common/tree.h
+++ b/src/common/tree.h
diff --git a/src/config.h.in b/src/config.h.in
index bd88e96..b711d4f 100644..100755
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -27,6 +27,15 @@
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
+/* Define if FreeBSD-like cpuset_t exists. */
+#undef HAVE_CPUSET_BSD
+
+/* Define if Linux-like cpu_set_t exists. */
+#undef HAVE_CPUSET_LINUX
+
+/* Define if cpuset_t and cpuset(3) exists. */
+#undef HAVE_CPUSET_NETBSD
+
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
@@ -105,6 +114,9 @@
/* Define to 1 if you have the `pselect' function. */
#undef HAVE_PSELECT
+/* Define to 1 if you have the <pthread_np.h> header file. */
+#undef HAVE_PTHREAD_NP_H
+
/* Define to 1 if you have the `pthread_setaffinity_np' function. */
#undef HAVE_PTHREAD_SETAFFINITY_NP
@@ -237,6 +249,9 @@
/* RR debug. */
#undef KNOT_RR_DEBUG
+/* Hash table stash debug. */
+#undef KNOT_STASH_DEBUG
+
/* XFR debug. */
#undef KNOT_XFR_DEBUG
diff --git a/src/knot.conf.5 b/src/knot.conf.5
new file mode 100755
index 0000000..d086860
--- /dev/null
+++ b/src/knot.conf.5
@@ -0,0 +1,296 @@
+.TH "knot.conf" "5" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1"
+.SH "NAME"
+.LP
+.B knot.conf
+\- Configuration file manual for Knot DNS server.
+.SH "SYNOPSIS"
+.LP
+.B knot.conf
+.SH "DESCRIPTION"
+.B knot.conf
+serves as an example of the configuration for knotc(8) and knotd(8).
+.SH "EXAMPLE"
+.LP
+# This is a comment.
+
+#
+# There are 4 main sections of this config file:
+# system, zones, interfaces and log
+#
+
+# Section 'system' contains general options for the server
+system {
+
+ # Identity of the server (see RFC 4892). Not used yet.
+ identity "I have no mouth and must scream";
+
+ # Version of the server (see RFC 4892). Not used yet.
+ version "0.1";
+
+ # Server identifier
+ # Use string format "text"
+ # Or hexstring 0x01ab00
+ nsid "myserver0";
+
+ # Working directory of the server
+ # Used to store compiled zones and PID file
+ storage "/tmp/knot-sample";
+
+ # Custom pidfile path
+ # default: pidfile is created in 'storage'.
+ pidfile "/tmp/knot.pid";
+
+ # Number of workers per interface
+ # This option is used to force number of threads used per interface
+ # Default: unset (auto-estimates optimal value from the number of online CPUs)
+ workers 1;
+
+ # User for running server
+ # May also specify user.group (f.e. knot.users)
+ user root;
+}
+
+# Section 'keys' contains list of TSIG keys
+keys {
+
+ # TSIG key
+ #
+ # format: name key-type "<key>";
+ # where key-type may be one of the following:
+ # hmac-md5
+ # hmac-sha1
+ # hmac-sha224
+ # hmac-sha256
+ # hmac-sha384
+ # hmac-sha512
+ # and <key> is the private key
+ key0.server0 hmac-md5 "Wg==";
+
+ # TSIG key for zone
+ key0.example.com hmac-md5 "==gW";
+}
+
+# Section 'interfaces' contains definitions of listening interfaces.
+interfaces {
+
+ # Interface entry
+ #
+ # Format 1: <name> { address <address>; [port <port>;] }
+ ipv4 { # <name> is an arbitrary symbolic name
+ address 127.0.0.1; # <address> may be ither IPv4 or IPv6 address
+ port 53531; # port is required for XFR/IN and NOTIFY/OUT
+ }
+
+ # Format 2: <name> { address <address>@<port>; }
+ # shortipv4 {
+ # address 127.0.0.1@53532;
+ #}
+
+ # Format 1 (IPv6 interface)
+ # ipv6 {
+ # address ::1@53533;
+ # }
+
+ # Format 2 (IPv6 interface)
+ # ipv6b {
+ # address [::1]@53534;
+ # }
+
+}
+
+# Section 'remotes' contains symbolic names for remote servers.
+# Syntax for 'remotes' is the same as for 'interfaces'.
+remotes {
+
+ # Remote entry
+ #
+ # Format 1: <name> { address <address>; [port <port>;] }
+ server0 { # <name> is an arbitrary symbolic name
+ address 127.0.0.1; # <address> may be ither IPv4 or IPv6 address
+ port 53531; # port is optional (default: 53)
+ key key0.server0; # (optional) specification of TSIG key associated for this remote
+ via ipv4; # (optional) source interface for queries
+ via 82.35.64.59; # (optional) source interface for queries, direct IPv4
+ via [::cafe]; # (optional) source interface for queries, direct IPv6
+ }
+
+ # Format 2: <name> { address <address>@<port>; }
+ server1 {
+ address 127.0.0.1@53001;
+ }
+}
+
+# Section 'zones' contains information about zones to be served.
+zones {
+
+ # Shared options for all listed zones
+ #
+
+ # Build differences from zone file changes
+ # Possible values: on|off
+ # Default value: off
+ ixfr-from-differences off;
+
+ # Enable semantic checks for all zones (if 'on')
+ # Possible values: on|off
+ # Default value: off
+ semantic-checks off;
+
+ # Disable ANY type queries for authoritative answers (if 'on')
+ # Possible values: on|off
+ # Default value: off
+ disable-any off;
+
+ # NOTIFY response timeout
+ # Possible values: <1,...> (seconds)
+ # Default value: 60
+ notify-timeout 60;
+
+ # Number of retries for NOTIFY
+ # Possible values: <1,...>
+ # Default value: 5
+ notify-retries 5;
+
+ # Timeout for syncing changes from zone database to zonefile
+ # Possible values: <1..INT_MAX> (seconds)
+ # Default value: 1h (1 hour)
+ # It is also possible to suffix with unit size [s/m/h/d]
+ # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
+ zonefile-sync 1h;
+
+ # File size limit for IXFR journal
+ # Possible values: <1..INT_MAX>
+ # Default value: N/A (infinite)
+ # It is also possible to suffix with unit size [k/M/G]
+ # f.e. 1k, 100M, 2G
+ ixfr-fslimit 1G;
+
+ # Zone entry
+ #
+ # Format: <zone-name> { file "<path-to-zone-file>"; }
+ example.com { # <zone-name> is the DNS name of the zone (zone root)
+ # <path-to-zone-file> may be either absolute or relative, in which case
+ # it is considered relative to the current directory from which the server
+ # was started.
+ file "samples/example.com.zone";
+
+ # Build differences from zone file changes
+ # Possible values: on|off
+ # Default value: off
+ ixfr-from-differences off;
+
+ # Disable ANY type queries for authoritative answers (if 'on')
+ # Possible values: on|off
+ # Default value: off
+ disable-any off;
+
+ # Enable zone semantic checks
+ # Possible values: on|off
+ # Default value: off
+ semantic-checks on;
+
+ # NOTIFY response timeout (specific for current zone)
+ # Possible values: <1,...> (seconds)
+ # Default value: 60
+ notify-timeout 60;
+
+ # Number of retries for NOTIFY (specific for current zone)
+ # Possible values: <1,...>
+ # Default value: 5
+ notify-retries 5;
+
+ # Timeout for syncing changes from zone database to zonefile
+ # Possible values: <1..INT_MAX> (seconds)
+ # Default value: inherited from zones.zonefile-sync
+ # It is also possible to suffix with unit size [s/m/h/d]
+ # f.e. 1s = 1 day, 1m = 1 minute, 1h = 1 hour, 1d = 1 day
+ zonefile-sync 1h;
+
+ # XFR master server
+ xfr-in server0;
+
+ # ACL list of XFR slaves
+ xfr-out server0, server1;
+
+ # ACL list of servers allowed to send NOTIFY queries
+ notify-in server0;
+
+ # List of servers to send NOTIFY to
+ notify-out server0, server1;
+ }
+}
+
+# Section 'log' configures logging of server messages.
+#
+# Logging recognizes 3 symbolic names of log devices:
+# stdout - Standard output
+# stderr - Standard error output
+# syslog - Syslog
+#
+# In addition, arbitrary number of log files may be specified (see below).
+#
+# Log messages are characterized by severity and category.
+# Supported severities:
+# debug - Debug messages. Must be turned on at compile time.
+# info - Informational messages.
+# notice - Notices and hints.
+# warning - Warnings. An action from the operator may be required.
+# error - Recoverable error. Some action should be taken.
+# fatal - Non-recoverable errors resulting in server shutdown.
+# (Not supported yet.)
+# all - All severities.
+#
+# Categories designate the source of the log message and roughly correspond
+# to server modules
+# Supported categories:
+# server - Messages related to general operation of the server.
+# zone - Messages related to zones, zone parsing and loading.
+# answering - Messages regarding query processing and response creation.
+# any - All categories
+#
+# More severities (separated by commas) may be listed for each category.
+# All applicable severities must be listed.
+# (I.e. specifying 'error' severity does mean: 'log error messages',
+# and NOT 'log all messages of severity error and above'.)
+#
+# Default settings (in case there are no entries in 'log' section or the section
+# is missing at all):
+#
+# stderr { any error; }
+# syslog { any error; }
+log {
+
+ # Log entry
+ #
+ # Format 1:
+ # <log> {
+ # <category1> <severity1> [, <severity2> ...];
+ # <category2> <severity1> [, <severity2> ...];
+ # ...
+ # }
+ syslog { # <log> is a symbolic name of a log device (see above)
+ # log errors of any category
+ any error; # for <category> and <severity> see above
+ # log also warnings and notices from category 'zone'
+ zone warning, notice;
+ # log info from server
+ server info;
+ }
+
+ # Log fatal, warnings and errors to stderr
+ stderr {
+ any error, warning;
+ }
+
+ # Format 2:
+ # file <path> {
+ # <category1> <severity1> [, <severity2> ...];
+ # <category2> <severity1> [, <severity2> ...];
+ # }
+ file "/tmp/knot-sample/knotd.debug" { # <path> is absolute or relative path to log file
+ server debug;
+ }
+}
+.SH "SEE ALSO"
+.LP
+knotd(8), knotc(8)
diff --git a/src/knot.service b/src/knot.service
index 36864d0..36864d0 100644..100755
--- a/src/knot.service
+++ b/src/knot.service
diff --git a/src/knot.spec b/src/knot.spec
index 5856aab..5856aab 100644..100755
--- a/src/knot.spec
+++ b/src/knot.spec
diff --git a/src/knot.sysconfig b/src/knot.sysconfig
index 99daeba..99daeba 100644..100755
--- a/src/knot.sysconfig
+++ b/src/knot.sysconfig
diff --git a/src/knot/common.h b/src/knot/common.h
index c321b94..ddf24b1 100644..100755
--- a/src/knot/common.h
+++ b/src/knot/common.h
@@ -85,14 +85,6 @@ typedef unsigned int uint; /*!< \brief Unsigned. */
/*! \brief Eliminate compiler warning with unused parameters. */
#define UNUSED(param) (void)(param)
-/*! \brief Type-safe minimum macro. */
-#define MIN(a, b) \
- ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; })
-
-/*! \brief Type-safe maximum macro. */
-#define MAX(a, b) \
- ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
-
/* Optimisation macros. */
#ifndef likely
/*! \brief Optimize for x to be true value. */
diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index 16b0343..58d1f4b 100644..100755
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -90,6 +90,7 @@ notify-out { lval.t = yytext; return NOTIFY_OUT; }
workers { lval.t = yytext; return WORKERS; }
user { lval.t = yytext; return USER; }
pidfile { lval.t = yytext; return PIDFILE; }
+ixfr-from-differences { lval.t = yytext; return BUILD_DIFFS; }
interfaces { lval.t = yytext; return INTERFACES; }
address { lval.t = yytext; return ADDRESS; }
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index b415662..69a5415 100644..100755
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -172,6 +172,7 @@ static void conf_zone_start(void *scanner, char *name) {
this_zone->ixfr_fslimit = -1; // Default policy applies
this_zone->dbsync_timeout = -1; // Default policy applies
this_zone->disable_any = -1; // Default policy applies
+ this_zone->build_diffs = -1; // Default policy applies
// Append mising dot to ensure FQDN
size_t nlen = strlen(name);
@@ -266,6 +267,7 @@ static int conf_mask(void* scanner, int nval, int prefixlen) {
%token <tok> XFR_OUT
%token <tok> NOTIFY_IN
%token <tok> NOTIFY_OUT
+%token <tok> BUILD_DIFFS
%token <tok> INTERFACES ADDRESS PORT
%token <tok> IPA
@@ -630,12 +632,31 @@ zone_acl:
;
zone_start:
- | TEXT { conf_zone_start(scanner, $1.t); }
| USER { conf_zone_start(scanner, strdup($1.t)); }
| REMOTES { conf_zone_start(scanner, strdup($1.t)); }
| LOG_SRC { conf_zone_start(scanner, strdup($1.t)); }
| LOG { conf_zone_start(scanner, strdup($1.t)); }
| LOG_LEVEL { conf_zone_start(scanner, strdup($1.t)); }
+ | NUM '/' TEXT {
+ if ($1.i < 0 || $1.i > 255) {
+ char buf[256] = "";
+ snprintf(buf, sizeof(buf), "rfc2317 origin prefix '%ld' out of bounds", $1.i);
+ cf_error(scanner, buf);
+ }
+ size_t len = 3 + 1 + strlen($3.t) + 1; /* <0,255> '/' rest */
+ char *name = malloc(len * sizeof(char));
+ if (name == NULL) {
+ cf_error(scanner, "out of memory");
+ } else {
+ name[0] = '\0';
+ if (snprintf(name, len, "%ld/%s", $1.i, $3.t) < 0) {
+ cf_error(scanner,"failed to convert rfc2317 origin to string");
+ }
+ }
+ free($3.t);
+ conf_zone_start(scanner, name);
+ }
+ | TEXT { conf_zone_start(scanner, $1.t); }
;
zone:
@@ -643,6 +664,7 @@ zone:
| zone zone_acl '}'
| zone zone_acl_list
| zone FILENAME TEXT ';' { this_zone->file = $3.t; }
+ | zone BUILD_DIFFS BOOL ';' { this_zone->build_diffs = $3.i; }
| zone SEMANTIC_CHECKS BOOL ';' { this_zone->enable_checks = $3.i; }
| zone DISABLE_ANY BOOL ';' { this_zone->disable_any = $3.i; }
| zone DBSYNC_TIMEOUT NUM ';' { this_zone->dbsync_timeout = $3.i; }
@@ -669,6 +691,7 @@ zones:
ZONES '{'
| zones zone '}'
| zones DISABLE_ANY BOOL ';' { new_config->disable_any = $3.i; }
+ | zones BUILD_DIFFS BOOL ';' { new_config->build_diffs = $3.i; }
| zones SEMANTIC_CHECKS BOOL ';' { new_config->zone_checks = $3.i; }
| zones IXFR_FSLIMIT SIZE ';' { new_config->ixfr_fslimit = $3.l; }
| zones IXFR_FSLIMIT NUM ';' { new_config->ixfr_fslimit = $3.i; }
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 4bbf622..e0bd0ea 100644..100755
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -73,74 +73,6 @@ void cf_error(void *scanner, const char *msg)
_parser_res = KNOTD_EPARSEFAIL;
}
-/*
- * Config helper functions.
- */
-
-/*! \brief Free TSIG key. */
-static void key_free(conf_key_t *k)
-{
- /* Secure erase. */
- if (k->k.secret) {
- memset(k->k.secret, 0, strlen(k->k.secret));
- }
- free(k->k.secret);
- knot_dname_free(&k->k.name);
- free(k);
-}
-
-/*! \brief Free config interfaces. */
-static void iface_free(conf_iface_t *iface)
-{
- if (!iface) {
- return;
- }
-
- free(iface->name);
- free(iface->address);
- free(iface);
-}
-
-/*! \brief Free config logs. */
-static void log_free(conf_log_t *log)
-{
- if (!log) {
- return;
- }
-
- if (log->file) {
- free(log->file);
- }
-
- /* Free loglevel mapping. */
- node *n = 0, *nxt = 0;
- WALK_LIST_DELSAFE(n, nxt, log->map) {
- free((conf_log_map_t*)n);
- }
-
- free(log);
-}
-
-/*! \brief Free config zones. */
-static void zone_free(conf_zone_t *zone)
-{
- if (!zone) {
- return;
- }
-
- /* Free ACL lists. */
- WALK_LIST_FREE(zone->acl.xfr_in);
- WALK_LIST_FREE(zone->acl.xfr_out);
- WALK_LIST_FREE(zone->acl.notify_in);
- WALK_LIST_FREE(zone->acl.notify_out);
-
- free(zone->name);
- free(zone->file);
- free(zone->db);
- free(zone->ixfr_db);
- free(zone);
-}
-
/*!
* \brief Call config hooks that need updating.
*
@@ -214,6 +146,11 @@ static int conf_process(conf_t *conf)
if (zone->dbsync_timeout < 0) {
zone->dbsync_timeout = conf->dbsync_timeout;
}
+
+ // Default policy for ixfr-from-differences
+ if (zone->build_diffs < 0) {
+ zone->build_diffs = conf->build_diffs;
+ }
// Default policy for semantic checks
if (zone->enable_checks < 0) {
@@ -286,7 +223,12 @@ static int conf_process(conf_t *conf)
*dpos = '\0';
}
+ /* Copy origin and remove bad characters. */
memcpy(dpos, zone->name, zname_len + 1);
+ for (int i = 0; i < zname_len; ++i) {
+ if (dpos[i] == '/') dpos[i] = '_';
+ }
+
memcpy(dpos + zname_len, "db", 3);
zone->db = dest;
@@ -299,16 +241,26 @@ static int conf_process(conf_t *conf)
ret = KNOTD_ENOMEM; /* Error report. */
continue;
}
- strncpy(dest, conf->storage, stor_len + 1);
+ dpos = dest;
+ memcpy(dpos, conf->storage, stor_len + 1);
+ dpos += stor_len;
if (conf->storage[stor_len - 1] != '/') {
- strncat(dest, "/", 1);
+ *(dpos++) = '/';
+ *dpos = '\0';
}
const char *dbext = "diff.db";
- strncat(dest, zone->name, zname_len);
- strncat(dest, dbext, strlen(dbext));
+ memcpy(dpos, zone->name, zname_len + 1);
+ for (int i = 0; i < zname_len; ++i) {
+ if (dpos[i] == '/') dpos[i] = '_';
+ }
+ memcpy(dpos + zname_len, dbext, strlen(dbext) + 1);
zone->ixfr_db = dest;
}
+
+ /* Update UID and GID. */
+ if (conf->uid < 0) conf->uid = getuid();
+ if (conf->gid < 0) conf->gid = getgid();
return ret;
}
@@ -475,6 +427,7 @@ conf_t *conf_new(const char* path)
c->ixfr_fslimit = -1;
c->uid = -1;
c->gid = -1;
+ c->build_diffs = 0; /* Disable by default. */
return c;
}
@@ -553,33 +506,33 @@ void conf_truncate(conf_t *conf, int unload_hooks)
// Free keys
WALK_LIST_DELSAFE(n, nxt, conf->keys) {
- key_free((conf_key_t *)n);
+ conf_free_key((conf_key_t *)n);
}
// Free interfaces
WALK_LIST_DELSAFE(n, nxt, conf->ifaces) {
- iface_free((conf_iface_t*)n);
+ conf_free_iface((conf_iface_t*)n);
}
conf->ifaces_count = 0;
init_list(&conf->ifaces);
// Free logs
WALK_LIST_DELSAFE(n, nxt, conf->logs) {
- log_free((conf_log_t*)n);
+ conf_free_log((conf_log_t*)n);
}
conf->logs_count = 0;
init_list(&conf->logs);
// Free remotes
WALK_LIST_DELSAFE(n, nxt, conf->remotes) {
- iface_free((conf_iface_t*)n);
+ conf_free_iface((conf_iface_t*)n);
}
conf->remotes_count = 0;
init_list(&conf->remotes);
// Free zones
WALK_LIST_DELSAFE(n, nxt, conf->zones) {
- zone_free((conf_zone_t*)n);
+ conf_free_zone((conf_zone_t*)n);
}
conf->zones_count = 0;
init_list(&conf->zones);
@@ -784,3 +737,63 @@ char* strcpath(char *path)
return path;
}
+void conf_free_zone(conf_zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ /* Free ACL lists. */
+ WALK_LIST_FREE(zone->acl.xfr_in);
+ WALK_LIST_FREE(zone->acl.xfr_out);
+ WALK_LIST_FREE(zone->acl.notify_in);
+ WALK_LIST_FREE(zone->acl.notify_out);
+
+ free(zone->name);
+ free(zone->file);
+ free(zone->db);
+ free(zone->ixfr_db);
+ free(zone);
+}
+
+void conf_free_key(conf_key_t *k)
+{
+ /* Secure erase. */
+ if (k->k.secret) {
+ memset(k->k.secret, 0, strlen(k->k.secret));
+ }
+ free(k->k.secret);
+ knot_dname_free(&k->k.name);
+ free(k);
+}
+
+void conf_free_iface(conf_iface_t *iface)
+{
+ if (!iface) {
+ return;
+ }
+
+ free(iface->name);
+ free(iface->address);
+ free(iface);
+}
+
+void conf_free_log(conf_log_t *log)
+{
+ if (!log) {
+ return;
+ }
+
+ if (log->file) {
+ free(log->file);
+ }
+
+ /* Free loglevel mapping. */
+ node *n = 0, *nxt = 0;
+ WALK_LIST_DELSAFE(n, nxt, log->map) {
+ free((conf_log_map_t*)n);
+ }
+
+ free(log);
+}
+
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
index 7e1b61f..ae41454 100644..100755
--- a/src/knot/conf/conf.h
+++ b/src/knot/conf/conf.h
@@ -96,6 +96,7 @@ typedef struct conf_zone_t {
int disable_any; /*!< Disable ANY type queries for AA.*/
int notify_retries; /*!< NOTIFY query retries. */
int notify_timeout; /*!< Timeout for NOTIFY response (s). */
+ int build_diffs; /*!< Calculate differences from changes. */
struct {
list xfr_in; /*!< Remotes accepted for for xfr-in.*/
list xfr_out; /*!< Remotes accepted for xfr-out.*/
@@ -197,6 +198,7 @@ typedef struct conf_t {
int notify_timeout; /*!< Timeout for NOTIFY response in seconds. */
int dbsync_timeout; /*!< Default interval between syncing to zonefile.*/
size_t ixfr_fslimit; /*!< File size limit for IXFR journal. */
+ int build_diffs; /*!< Calculate differences from changes. */
/*
* Implementation specifics
@@ -323,22 +325,6 @@ static inline conf_t* conf() {
return s_config; // Inline for performance reasons.
}
-/*!
- * \brief Lock configuration for reading.
- *
- * \return Configuration context.
- */
-static inline void conf_read_lock() {
- rcu_read_lock();
-}
-
-/*!
- * \brief Unlock configuration for reading.
- */
-static inline void conf_read_unlock() {
- rcu_read_unlock();
-}
-
/*
* Utilities.
*/
@@ -363,6 +349,18 @@ char* strcdup(const char *s1, const char *s2);
*/
char* strcpath(char *path);
+/*! \brief Free zone config. */
+void conf_free_zone(conf_zone_t *zone);
+
+/*! \brief Free TSIG key config. */
+void conf_free_key(conf_key_t *k);
+
+/*! \brief Free interface config. */
+void conf_free_iface(conf_iface_t *iface);
+
+/*! \brief Free log config. */
+void conf_free_log(conf_log_t *log);
+
#endif /* _KNOTD_CONF_H_ */
/*! @} */
diff --git a/src/knot/conf/logconf.c b/src/knot/conf/logconf.c
index 4d7334f..4d7334f 100644..100755
--- a/src/knot/conf/logconf.c
+++ b/src/knot/conf/logconf.c
diff --git a/src/knot/conf/logconf.h b/src/knot/conf/logconf.h
index 7b9e054..7b9e054 100644..100755
--- a/src/knot/conf/logconf.h
+++ b/src/knot/conf/logconf.h
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 97412dd..07a9e38 100644..100755
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -43,7 +43,8 @@ enum knotc_flag_t {
F_VERBOSE = 1 << 1,
F_WAIT = 1 << 2,
F_INTERACTIVE = 1 << 3,
- F_AUTO = 1 << 4
+ F_AUTO = 1 << 4,
+ F_UNPRIVILEGED= 1 << 5
};
static inline unsigned has_flag(unsigned flags, enum knotc_flag_t f) {
@@ -55,9 +56,9 @@ void help(int argc, char **argv)
{
printf("Usage: %sc [parameters] start|stop|restart|reload|running|"
"compile [additional]\n", PACKAGE_NAME);
- printf("Parameters:\n"
+ printf("\nParameters:\n"
" -c [file], --config=[file] Select configuration file.\n"
- " -j [num], --jobs=[num] Number of parallel tasks to run (only for 'compile').\n"
+ " -j [num], --jobs=[num] Number of parallel tasks to run when compiling.\n"
" -f, --force Force operation - override some checks.\n"
" -v, --verbose Verbose mode - additional runtime information.\n"
" -V, --version Print %s server version.\n"
@@ -66,7 +67,7 @@ void help(int argc, char **argv)
" -a, --auto Enable automatic recompilation (start or reload).\n"
" -h, --help Print help and usage.\n",
PACKAGE_NAME);
- printf("Actions:\n"
+ printf("\nActions:\n"
" start Start %s server zone (no-op if running).\n"
" stop Stop %s server (no-op if not running).\n"
" restart Stops and then starts %s server.\n"
@@ -75,8 +76,8 @@ void help(int argc, char **argv)
" running Check if server is running.\n"
" checkconf Check server configuration.\n"
"\n"
- " checkzone Check zones (accepts specific zones, f.e. "
- "'knotc checkzone example1.com example2.com').\n"
+ " checkzone Check zones (accepts specific zones, \n"
+ " e.g. 'knotc checkzone example1.com example2.com').\n"
" compile Compile zones (accepts specific zones, see above).\n",
PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME, PACKAGE_NAME);
}
@@ -142,10 +143,15 @@ pid_t wait_cmd(pid_t proc, int *rc)
return proc;
}
-pid_t start_cmd(const char *argv[], int argc)
+pid_t start_cmd(const char *argv[], int argc, int flags)
{
pid_t chproc = fork();
if (chproc == 0) {
+
+ /* Alter privileges. */
+ if (flags & F_UNPRIVILEGED) {
+ proc_update_privileges(conf()->uid, conf()->gid);
+ }
/* Duplicate, it doesn't run from stack address anyway. */
char **args = malloc((argc + 1) * sizeof(char*));
@@ -180,7 +186,7 @@ pid_t start_cmd(const char *argv[], int argc)
int exec_cmd(const char *argv[], int argc)
{
int ret = 0;
- pid_t proc = start_cmd(argv, argc);
+ pid_t proc = start_cmd(argv, argc, 0);
wait_cmd(proc, &ret);
return ret;
}
@@ -292,15 +298,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
int rc = 0;
if (strcmp(action, "start") == 0) {
// Check pidfile for w+
- FILE* chkf = fopen(pidfile, "w+");
- if (chkf == NULL) {
- log_server_error("PID file '%s' is not writeable, "
- "refusing to start\n", pidfile);
- return 1;
- } else {
- fclose(chkf);
- chkf = NULL;
- }
+ log_server_info("Starting server...\n");
// Check PID
valid_cmd = 1;
@@ -332,7 +330,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
}
// Lock configuration
- conf_read_lock();
+ rcu_read_lock();
// Prepare command
const char *cfg = conf()->filename;
@@ -346,7 +344,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
};
// Unlock configuration
- conf_read_unlock();
+ rcu_read_unlock();
// Execute command
if (has_flag(flags, F_INTERACTIVE)) {
@@ -381,6 +379,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
if (strcmp(action, "stop") == 0) {
// Check PID
+ log_server_info("Stopping server...\n");
valid_cmd = 1;
rc = 0;
if (pid <= 0 || !pid_running(pid)) {
@@ -440,7 +439,6 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
}
}
- log_server_info("Restarting server.\n");
rc = execute("start", argv, argc, -1, flags, jobs, pidfile);
}
if (strcmp(action, "reload") == 0) {
@@ -527,7 +525,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
valid_cmd = 1;
// Lock configuration
- conf_read_lock();
+ rcu_read_lock();
// Generate databases for all zones
node *n = 0;
@@ -604,7 +602,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
}
fflush(stdout);
fflush(stderr);
- pid_t zcpid = start_cmd(args, ac);
+ pid_t zcpid = start_cmd(args, ac, F_UNPRIVILEGED);
zctask_add(tasks, jobs, zcpid, zone);
++running;
}
@@ -617,7 +615,7 @@ int execute(const char *action, char **argv, int argc, pid_t pid,
free(tasks);
// Unlock configuration
- conf_read_unlock();
+ rcu_read_unlock();
}
if (!valid_cmd) {
log_server_error("Invalid command: '%s'\n", action);
@@ -707,7 +705,7 @@ int main(int argc, char **argv)
log_server_error("Couldn't open configuration file "
"'%s'.\n", config_fn);
} else {
- log_server_error("Failed to parse configuration '%s'.\n",
+ log_server_error("Failed to load configuration '%s'.\n",
config_fn);
}
free(default_fn);
@@ -722,7 +720,7 @@ int main(int argc, char **argv)
log_levels_add(LOGT_STDOUT, LOG_ANY,
LOG_MASK(LOG_INFO)|LOG_MASK(LOG_DEBUG));
}
-
+
// Fetch PID
char* pidfile = pid_filename();
if (!pidfile) {
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
index bb61804..d3fa2fc 100644..100755
--- a/src/knot/ctl/process.c
+++ b/src/knot/ctl/process.c
@@ -21,6 +21,9 @@
#include <errno.h>
#include <string.h>
#include <signal.h>
+#include <grp.h>
+#include <unistd.h>
+#include <assert.h>
#include "knot/common.h"
#include "knot/ctl/process.h"
@@ -29,7 +32,7 @@
char* pid_filename()
{
- conf_read_lock();
+ rcu_read_lock();
/* Read configuration. */
char* ret = 0;
@@ -37,7 +40,7 @@ char* pid_filename()
ret = strdup(conf()->pidfile);
}
- conf_read_unlock();
+ rcu_read_unlock();
return ret;
}
@@ -113,6 +116,7 @@ int pid_write(const char* fn)
int pid_remove(const char* fn)
{
if (unlink(fn) < 0) {
+ perror("unlink");
return KNOTD_EINVAL;
}
@@ -124,3 +128,45 @@ int pid_running(pid_t pid)
return kill(pid, 0) == 0;
}
+void proc_update_privileges(int uid, int gid)
+{
+#ifdef HAVE_SETGROUPS
+ /* Drop supplementary groups. */
+ if (uid != getuid() || gid != getgid()) {
+ if (setgroups(0, NULL) < 0) {
+ log_server_warning("Failed to drop supplementary groups"
+ " for uid '%d' (%s).\n",
+ getuid(), strerror(errno));
+ }
+ }
+#endif
+
+ /* Watch uid/gid. */
+ if (gid != getgid()) {
+ log_server_info("Changing group id to '%d'.\n", gid);
+ if (setregid(gid, gid) < 0) {
+ log_server_error("Failed to change gid to '%d'.\n",
+ gid);
+ }
+ }
+ if (uid != getuid()) {
+ log_server_info("Changing user id to '%d'.\n", uid);
+ if (setreuid(uid, uid) < 0) {
+ log_server_error("Failed to change uid to '%d'.\n",
+ uid);
+ }
+ }
+
+ /* Check storage writeability. */
+ char *lfile = strcdup(conf()->storage, "/knot.lock");
+ assert(lfile != NULL);
+ FILE* fp = fopen(lfile, "w");
+ if (fp == NULL) {
+ log_server_warning("Storage directory '%s' is not writeable.\n",
+ conf()->storage);
+ } else {
+ fclose(fp);
+ unlink(lfile);
+ }
+ free(lfile);
+}
diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h
index d8f2f4c..a387add 100644..100755
--- a/src/knot/ctl/process.h
+++ b/src/knot/ctl/process.h
@@ -83,6 +83,15 @@ int pid_remove(const char* fn);
*/
int pid_running(pid_t pid);
+/*!
+ * \brief Update process privileges to new UID/GID.
+ *
+ * \param uid New user ID.
+ * \param gid New group ID.
+ *
+ */
+void proc_update_privileges(int uid, int gid);
+
#endif // _KNOTD_PROCESS_H_
/*! @} */
diff --git a/src/knot/main.c b/src/knot/main.c
index 99ee1cf..4486a1c 100644..100755
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <getopt.h>
#include <limits.h>
+
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
#endif /* HAVE_CAP_NG_H */
@@ -72,7 +73,7 @@ void help(int argc, char **argv)
{
printf("Usage: %sd [parameters]\n",
PACKAGE_NAME);
- printf("Parameters:\n"
+ printf("\nParameters:\n"
" -c, --config [file] Select configuration file.\n"
" -d, --daemonize Run server as a daemon.\n"
" -v, --verbose Verbose mode - additional runtime information.\n"
@@ -138,6 +139,7 @@ int main(int argc, char **argv)
emptyset.sa_flags = 0;
sigaction(SIGALRM, &emptyset, NULL); // Interrupt
sigaction(SIGPIPE, &emptyset, NULL); // Mask
+ rcu_register_thread();
// Setup event queue
evqueue_set(evqueue_new());
@@ -158,11 +160,10 @@ int main(int argc, char **argv)
server_t *server = server_create();
// Initialize configuration
- conf_read_lock();
+ rcu_read_lock();
conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
conf_add_hook(conf(), CONF_ALL, server_conf_hook, server);
- conf_add_hook(conf(), CONF_ALL, zones_ns_conf_hook, server->nameserver);
- conf_read_unlock();
+ rcu_read_unlock();
// Find implicit configuration file
if (!config_fn) {
@@ -229,7 +230,7 @@ int main(int argc, char **argv)
log_server_error("Couldn't open configuration file "
"'%s'.\n", config_fn);
} else {
- log_server_error("Failed to parse configuration '%s'.\n",
+ log_server_error("Failed to load configuration '%s'.\n",
config_fn);
}
server_wait(server);
@@ -242,21 +243,28 @@ int main(int argc, char **argv)
}
log_server_info("\n");
- // Create server instance
- char* pidfile = pid_filename();
+ /* Alter privileges. */
+ proc_update_privileges(conf()->uid, conf()->gid);
+
+ /* Load zones and add hook. */
+ zones_ns_conf_hook(conf(), server->nameserver);
+ conf_add_hook(conf(), CONF_ALL, zones_ns_conf_hook, server->nameserver);
// Run server
int res = 0;
+ int has_pid = 0;
+ char* pidfile = pid_filename();
log_server_info("Starting server...\n");
if ((server_start(server)) == KNOTD_EOK) {
// Save PID
- int has_pid = 1;
+ has_pid = 1;
int rc = pid_write(pidfile);
if (rc < 0) {
has_pid = 0;
log_server_warning("Failed to create "
- "PID file '%s'.\n", pidfile);
+ "PID file '%s' (%s).\n",
+ pidfile, strerror(errno));
}
// Change directory if daemonized
@@ -370,7 +378,7 @@ int main(int argc, char **argv)
server_destroy(&server);
// Remove PID file
- if (pid_remove(pidfile) < 0) {
+ if (has_pid && pid_remove(pidfile) < 0) {
log_server_warning("Failed to remove PID file.\n");
}
@@ -381,6 +389,8 @@ int main(int argc, char **argv)
// Destroy event loop
evqueue_t *q = evqueue();
evqueue_free(&q);
+
+ rcu_unregister_thread();
// Free default config filename if exists
free(config_fn);
diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h
index 1a8698e..1a8698e 100644..100755
--- a/src/knot/other/debug.h
+++ b/src/knot/other/debug.h
diff --git a/src/knot/other/error.c b/src/knot/other/error.c
index 70c84a3..7f005e7 100644..100755
--- a/src/knot/other/error.c
+++ b/src/knot/other/error.c
@@ -41,6 +41,8 @@ const error_table_t knotd_error_msgs[] = {
{KNOTD_ENOIPV6, "IPv6 support disabled."},
{KNOTD_EMALF, "Malformed data."},
{KNOTD_ESPACE, "Not enough space provided."},
- {KNOTD_EEXPIRED, "Resource is expired."},
+ {KNOTD_EEXPIRED, "Resource is expired."},
+ {KNOTD_ENODIFF, "Cannot create zone diff."},
+ {KNOTD_EUPTODATE, "Zone is up-to-date."},
{KNOTD_ERROR, 0}
};
diff --git a/src/knot/other/error.h b/src/knot/other/error.h
index 65c51cf..4b9efee 100644..100755
--- a/src/knot/other/error.h
+++ b/src/knot/other/error.h
@@ -63,8 +63,10 @@ enum knot_error_t {
KNOTD_EMALF, /*!< \brief Malformed data. */
KNOTD_ESPACE, /*!< \brief Not enough space provided. */
KNOTD_EEXPIRED, /*!< \brief Resource is expired. */
+ KNOTD_ENODIFF, /*!< \brief Cannot create zone diff. */
+ KNOTD_EUPTODATE, /*!< \brief Zone is up-to-date. */
- KNOTD_ERROR_COUNT = 21
+ KNOTD_ERROR_COUNT = 23
};
/*! \brief Table linking error messages to error codes. */
diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c
index e2b45f2..0c38cfc 100644..100755
--- a/src/knot/server/dthreads.c
+++ b/src/knot/server/dthreads.c
@@ -21,15 +21,25 @@
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
+#include <urcu.h>
+
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
#endif /* HAVE_CAP_NG_H */
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif /* HAVE_PTHREAD_NP_H */
#include "knot/common.h"
#include "knot/server/dthreads.h"
#include "common/log.h"
#include "knot/other/error.h"
+/* BSD cpu set compatibility. */
+#if defined(HAVE_CPUSET_BSD)
+typedef cpuset_t cpu_set_t;
+#endif
+
/*! \brief Lock thread state for R/W. */
static inline void lock_thread_rw(dthread_t *thread)
{
@@ -126,6 +136,7 @@ static void *thread_ep(void *data)
sigaddset(&ignset, SIGPIPE);
sigaddset(&ignset, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &ignset, 0); /*! \todo Review under BSD (issue #1441). */
+ rcu_register_thread();
dbg_dt("dthreads: [%p] entered ep\n", thread);
@@ -207,6 +218,7 @@ static void *thread_ep(void *data)
lock_thread_rw(thread);
thread->state |= ThreadJoinable;
unlock_thread_rw(thread);
+ rcu_unregister_thread();
// Return
return 0;
@@ -854,22 +866,44 @@ int dt_stop(dt_unit_t *unit)
// return KNOTD_EOK;
//}
-int dt_setaffinity(dthread_t *thread, void *mask, size_t len)
+
+
+int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count)
{
- if (thread == NULL || mask == NULL) {
+ if (thread == NULL) {
return KNOTD_EINVAL;
}
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
- if (len != sizeof(cpu_set_t)) {
- return KNOTD_EINVAL;
+ int ret = -1;
+
+/* Linux, FreeBSD interface. */
+#if defined(HAVE_CPUSET_LINUX) || defined(HAVE_CPUSET_BSD)
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ for (unsigned i = 0; i < cpu_count; ++i) {
+ CPU_SET(cpu_id[i], &set);
}
- pthread_t tid = pthread_self();
- int ret = pthread_setaffinity_np(tid, len, (cpu_set_t*)mask);
+ ret = pthread_setaffinity_np(thread->_thr, sizeof(cpu_set_t), &set);
+/* NetBSD interface. */
+#elif defined(HAVE_CPUSET_NETBSD)
+ cpuset_t *set = cpuset_create();
+ if (set == NULL) {
+ return KNOTD_ENOMEM;
+ }
+ cpuset_zero(set);
+ for (unsigned i = 0; i < cpu_count; ++i) {
+ cpuset_set(cpu_id[i], &set);
+ }
+ ret = pthread_setaffinity_np(thread->_thr, cpuset_size(set), set);
+ cpuset_destroy(set);
+#endif /* interface */
+
if (ret < 0) {
return KNOTD_ERROR;
}
-#else
+
+#else /* HAVE_PTHREAD_SETAFFINITY_NP */
return KNOTD_ENOTSUP;
#endif
@@ -988,8 +1022,17 @@ int dt_compact(dt_unit_t *unit)
int dt_online_cpus()
{
int ret = -1;
+/* Linux, Solaris, OS X 10.4+ */
#ifdef _SC_NPROCESSORS_ONLN
ret = (int) sysconf(_SC_NPROCESSORS_ONLN);
+#else
+/* FreeBSD, NetBSD, OpenBSD, OS X < 10.4 */
+#if HAVE_SYSCTLBYNAME
+ size_t rlen = sizeof(int);
+ if (sysctlbyname("hw.ncpu", &ret, &rlen, NULL, 0) < 0) {
+ ret = -1;
+ }
+#endif
#endif
return ret;
}
diff --git a/src/knot/server/dthreads.h b/src/knot/server/dthreads.h
index 8ba457b..758bc28 100644..100755
--- a/src/knot/server/dthreads.h
+++ b/src/knot/server/dthreads.h
@@ -254,12 +254,13 @@ int dt_stop(dt_unit_t *unit);
* \brief Set thread affinity to masked CPU's.
*
* \param thread Target thread instance.
- * \param mask CPU mask (should be pointer to cpu_set_t).
+ * \param cpu_id Array of CPU IDs to set affinity to.
+ * \param cpu_count Number of CPUs in the array, set to 0 for no CPU.
*
* \retval KNOTD_EOK on success.
* \retval KNOTD_EINVAL on invalid parameters.
*/
-int dt_setaffinity(dthread_t *thread, void *mask, size_t len);
+int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count);
/*!
* \brief Set thread to execute another runnable.
diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c
index f901837..fa84021 100644..100755
--- a/src/knot/server/journal.c
+++ b/src/knot/server/journal.c
@@ -117,8 +117,8 @@ static int journal_recover(journal_t *j)
}
/* Write back. */
- lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
- if (!sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) {
+ int seek_ret = lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) {
dbg_journal("journal: failed to write back queue state\n");
return KNOTD_ERROR;
}
@@ -146,6 +146,7 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len)
/* Calculate remaining bytes to reach file size limit. */
size_t fs_remaining = j->fslimit - j->fsize;
+ int seek_ret = 0;
/* Increase free segment if on the end of file. */
journal_node_t *n = j->nodes + j->qtail;
@@ -193,8 +194,8 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len)
/* Write back evicted node. */
head->flags = JOURNAL_FREE;
- lseek(j->fd, JOURNAL_HSIZE + (j->qhead + 1) * node_len, SEEK_SET);
- if (!sfwrite(head, node_len, j->fd)) {
+ seek_ret = lseek(j->fd, JOURNAL_HSIZE + (j->qhead + 1) * node_len, SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(head, node_len, j->fd)) {
return KNOTD_ERROR;
}
@@ -204,8 +205,8 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len)
/* Write back query state. */
j->qhead = (j->qhead + 1) % j->max_nodes;
uint16_t qstate[2] = {j->qhead, j->qtail};
- lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
- if (!sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) {
+ seek_ret = lseek(j->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), j->fd)) {
return KNOTD_ERROR;
}
@@ -260,8 +261,8 @@ int journal_write_out(journal_t *journal, journal_node_t *n)
journal->free.pos + journal->free.len);
/* Write back free segment state. */
- lseek(journal->fd, JOURNAL_HSIZE, SEEK_SET);
- if (!sfwrite(&journal->free, node_len, journal->fd)) {
+ int seek_ret = lseek(journal->fd, JOURNAL_HSIZE, SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(&journal->free, node_len, journal->fd)) {
/* Node is marked valid and failed to shrink free space,
* node will be overwritten on the next write. Return error.
*/
@@ -278,8 +279,8 @@ int journal_write_out(journal_t *journal, journal_node_t *n)
* qtail - highest valid node identifier (most recently used)
*/
uint16_t qstate[2] = {journal->qhead, journal->qtail};
- lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
- if (!sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) {
+ seek_ret = lseek(journal->fd, JOURNAL_HSIZE - 2 * sizeof(uint16_t), SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(qstate, 2 * sizeof(uint16_t), journal->fd)) {
dbg_journal("journal: failed to write back queue state\n");
return KNOTD_ERROR;
}
@@ -297,11 +298,15 @@ int journal_update_crc(int fd)
char buf[4096];
ssize_t rb = 0;
crc_t crc = crc_init();
- lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET);
+ if (lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) {
+ return KNOTD_ERROR;
+ }
while((rb = read(fd, buf, sizeof(buf))) > 0) {
crc = crc_update(crc, (const unsigned char *)buf, rb);
}
- lseek(fd, MAGIC_LENGTH, SEEK_SET);
+ if (lseek(fd, MAGIC_LENGTH, SEEK_SET) < 0) {
+ return KNOTD_ERROR;
+ }
if (!sfwrite(&crc, sizeof(crc_t), fd)) {
dbg_journal("journal: couldn't write CRC to fd=%d\n", fd);
return KNOTD_ERROR;
@@ -508,7 +513,12 @@ journal_t* journal_open(const char *fn, size_t fslimit, int mode, uint16_t bflag
/* Compare */
if (crc == crc_calc) {
- lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET); /* Rewind. */
+ /* Rewind. */
+ if (lseek(fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) {
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ return NULL;
+ }
} else {
log_server_warning("Journal file '%s' CRC error, "
"it will be flushed.\n", fn);
@@ -717,10 +727,10 @@ int journal_read(journal_t *journal, uint64_t id, journal_cmp_t cf, char *dst)
(unsigned long long)id, n->pos, n->pos + n->len, n->flags);
/* Seek journal node. */
- lseek(journal->fd, n->pos, SEEK_SET);
+ int seek_ret = lseek(journal->fd, n->pos, SEEK_SET);
/* Read journal node content. */
- if (!sfread(dst, n->len, journal->fd)) {
+ if (seek_ret < 0 || !sfread(dst, n->len, journal->fd)) {
return KNOTD_ERROR;
}
@@ -741,8 +751,8 @@ int journal_write(journal_t *journal, uint64_t id, const char *src, size_t size)
}
/* Write data to permanent storage. */
- lseek(journal->fd, n->pos, SEEK_SET);
- if (!sfwrite(src, size, journal->fd)) {
+ int seek_ret = lseek(journal->fd, n->pos, SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(src, size, journal->fd)) {
return KNOTD_ERROR;
}
@@ -765,7 +775,9 @@ int journal_map(journal_t *journal, uint64_t id, char **dst, size_t size)
/* Reserve data in permanent storage. */
/*! \todo This is only needed when inflating journal file. */
- lseek(journal->fd, n->pos, SEEK_SET);
+ if (lseek(journal->fd, n->pos, SEEK_SET) < 0) {
+ return KNOTD_ERROR;
+ }
char nbuf[4096] = {0};
size_t wb = sizeof(nbuf);
while (size > 0) {
@@ -868,8 +880,8 @@ int journal_update(journal_t *journal, journal_node_t *n)
i, (unsigned long long)n->id, n->flags);
/* Write back. */
- lseek(journal->fd, jn_fpos, SEEK_SET);
- if (!sfwrite(n, node_len, journal->fd)) {
+ int seek_ret = lseek(journal->fd, jn_fpos, SEEK_SET);
+ if (seek_ret < 0 || !sfwrite(n, node_len, journal->fd)) {
dbg_journal("journal: failed to writeback node=%llu to %ld\n",
(unsigned long long)n->id, jn_fpos);
return KNOTD_ERROR;
diff --git a/src/knot/server/journal.h b/src/knot/server/journal.h
index 322e25d..fd0e3a6 100644..100755
--- a/src/knot/server/journal.h
+++ b/src/knot/server/journal.h
@@ -359,3 +359,5 @@ void journal_release(journal_t *journal);
int journal_update_crc(int fd);
#endif /* _KNOTD_JOURNAL_H_ */
+
+/*! @} */
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index aa0a52b..d0292e8 100644..100755
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -242,39 +242,49 @@ int notify_process_request(knot_nameserver_t *ns,
if (notify->parsed < notify->size) {
if (knot_packet_parse_rest(notify) != KNOT_EOK) {
dbg_notify("notify: failed to parse NOTIFY query\n");
- knot_ns_error_response(ns, knot_packet_id(notify),
- &notify->header.flags1,
- KNOT_RCODE_FORMERR, buffer,
- size);
+ knot_ns_error_response_from_query(ns, notify,
+ KNOT_RCODE_FORMERR,
+ buffer, size);
return KNOTD_EOK;
}
}
+ // check if it makes sense - if the QTYPE is SOA
+ if (knot_packet_qtype(notify) != KNOT_RRTYPE_SOA) {
+ // send back FORMERR
+ knot_ns_error_response_from_query(ns, notify,
+ KNOT_RCODE_FORMERR, buffer,
+ size);
+ return KNOTD_EOK;
+ }
+
// create NOTIFY response
dbg_notify("notify: creating response\n");
ret = notify_create_response(notify, buffer, size);
if (ret != KNOTD_EOK) {
dbg_notify("notify: failed to create NOTIFY response\n");
- knot_ns_error_response(ns, knot_packet_id(notify),
- &notify->header.flags1,
- KNOT_RCODE_SERVFAIL, buffer, size);
+ knot_ns_error_response_from_query(ns, notify,
+ KNOT_RCODE_SERVFAIL, buffer,
+ size);
return KNOTD_EOK;
}
// find the zone
+ rcu_read_lock();
const knot_dname_t *qname = knot_packet_qname(notify);
const knot_zone_t *z = knot_zonedb_find_zone_for_name(
ns->zone_db, qname);
if (z == NULL) {
+ rcu_read_unlock();
dbg_notify("notify: failed to find zone by name\n");
- knot_ns_error_response(ns, knot_packet_id(notify),
- &notify->header.flags1,
- KNOT_RCODE_REFUSED, buffer, size);
+ knot_ns_error_response_from_query(ns, notify,
+ KNOT_RCODE_FORMERR, buffer,
+ size);
return KNOTD_EOK;
}
notify_check_and_schedule(ns, z, from);
-
+ rcu_read_unlock();
return KNOTD_EOK;
}
@@ -294,13 +304,16 @@ int notify_process_response(knot_nameserver_t *nameserver,
*size = 0;
/* Find matching zone. */
+ rcu_read_lock();
const knot_dname_t *zone_name = knot_packet_qname(notify);
knot_zone_t *zone = knot_zonedb_find_zone(nameserver->zone_db,
zone_name);
if (!zone) {
+ rcu_read_unlock();
return KNOTD_ENOENT;
}
if (!knot_zone_data(zone)) {
+ rcu_read_unlock();
return KNOTD_ENOENT;
}
@@ -318,6 +331,7 @@ int notify_process_response(knot_nameserver_t *nameserver,
/* Found waiting NOTIFY query? */
if (!match) {
+ rcu_read_unlock();
pthread_mutex_unlock(&zd->lock);
return KNOTD_ERROR;
}
@@ -327,6 +341,8 @@ int notify_process_response(knot_nameserver_t *nameserver,
/* Zone was removed/reloaded. */
pthread_mutex_unlock(&zd->lock);
+
+ rcu_read_unlock();
return KNOTD_EOK;
}
diff --git a/src/knot/server/notify.h b/src/knot/server/notify.h
index a9ba807..a9ba807 100644..100755
--- a/src/knot/server/notify.h
+++ b/src/knot/server/notify.h
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 5611a0c..06f89b5 100644..100755
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -22,8 +22,6 @@
#include <errno.h>
#include <openssl/evp.h>
#include <assert.h>
-#include <grp.h>
-
#include "common/prng.h"
#include "knot/common.h"
@@ -219,7 +217,7 @@ static int server_bind_sockets(server_t *server)
*/
/* Lock configuration. */
- conf_read_lock();
+ rcu_read_lock();
/* Prepare helper lists. */
int bound = 0;
@@ -290,7 +288,7 @@ static int server_bind_sockets(server_t *server)
}
/* Unlock configuration. */
- conf_read_unlock();
+ rcu_read_unlock();
/* Publish new list. */
list* oldlist = rcu_xchg_pointer(&server->ifaces, newlist);
@@ -331,7 +329,7 @@ static int server_bind_handlers(server_t *server)
}
/* Lock config. */
- conf_read_lock();
+ rcu_read_lock();
/* Estimate number of threads/manager. */
int thr_count = 0;
@@ -388,7 +386,7 @@ static int server_bind_handlers(server_t *server)
}
/* Unlock config. */
- conf_read_unlock();
+ rcu_read_unlock();
return KNOTD_EOK;
}
@@ -552,7 +550,7 @@ int server_start(server_t *server)
xfr_start(server->xfr_h);
/* Lock configuration. */
- conf_read_lock();
+ rcu_read_lock();
// Start dispatchers
int ret = KNOTD_EOK;
@@ -573,7 +571,7 @@ int server_start(server_t *server)
}
/* Unlock configuration. */
- conf_read_unlock();
+ rcu_read_unlock();
dbg_server("server: server started\n");
@@ -653,16 +651,13 @@ void server_stop(server_t *server)
{
dbg_server("server: stopping server\n");
- /* Wait for XFR master. */
- xfr_stop(server->xfr_h);
+ /* Send termination event. */
+ evsched_schedule_term(server->sched, 0);
/* Interrupt XFR handler execution. */
if (server->xfr_h->interrupt) {
server->xfr_h->interrupt(server->xfr_h);
}
-
- /* Send termination event. */
- evsched_schedule_term(server->sched, 0);
/* Lock RCU. */
rcu_read_lock();
@@ -743,51 +738,9 @@ int server_conf_hook(const struct conf_t *conf, void *data)
"configured interfaces.\n");
}
}
-
- /* Lock configuration. */
- conf_read_lock();
- int priv_failed = 0;
-
-#ifdef HAVE_SETGROUPS
- /* Drop supplementary groups. */
- if (conf->gid > -1 || conf->uid > -1) {
- ret = setgroups(0, NULL);
-
- /* Collect results. */
- if (ret < 0) {
- log_server_error("Failed to set supplementary groups "
- "for uid '%d' (%s).\n",
- getuid(), strerror(errno));
- priv_failed = 1;
- }
- }
-#endif
-
- /* Watch uid/gid. */
- if (conf->gid > -1 && conf->gid != getgid()) {
- log_server_info("Changing group id to '%d'.\n", conf->gid);
- if (setregid(conf->gid, conf->gid) < 0) {
- log_server_error("Failed to change gid to '%d'.\n",
- conf->gid);
- priv_failed = 1;
- }
- }
- if (conf->uid > -1 && conf->uid != getuid()) {
- log_server_info("Changing user id to '%d'.\n", conf->uid);
- if (setreuid(conf->uid, conf->uid) < 0) {
- log_server_error("Failed to change uid to '%d'.\n",
- conf->uid);
- priv_failed = 1;
- }
- }
-
- if (priv_failed) {
- ret = KNOTD_EACCES;
- }
/* Exit if the server is not running. */
if (ret != KNOTD_EOK || !(server->state & ServerRunning)) {
- conf_read_unlock();
return KNOTD_ENOTRUNNING;
}
@@ -807,9 +760,6 @@ int server_conf_hook(const struct conf_t *conf, void *data)
}
}
- /* Unlock config. */
- conf_read_unlock();
-
return ret;
}
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
index 79a4729..79a4729 100644..100755
--- a/src/knot/server/server.h
+++ b/src/knot/server/server.h
diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c
index 4deb862..1e79f89 100644..100755
--- a/src/knot/server/socket.c
+++ b/src/knot/server/socket.c
@@ -99,6 +99,8 @@ int socket_connect(int fd, const char *addr, unsigned short port)
int socket_bind(int socket, int family, const char *addr, unsigned short port)
{
/* Check address family. */
+ int flag = 1;
+ int ret = 0;
struct sockaddr* paddr = 0;
socklen_t addrlen = 0;
struct sockaddr_in saddr;
@@ -150,12 +152,23 @@ int socket_bind(int socket, int family, const char *addr, unsigned short port)
addr, buf);
}
-#endif
+
+ /* Make the socket IPv6 only to allow 'any' for IPv4 and IPv6 at the same time. */
+#ifdef IPV6_V6ONLY
+ if (family == AF_INET6) {
+ /* Do not support mapping IPv4 in IPv6 sockets. */
+ ret = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY,
+ &flag, sizeof(flag));
+ if (ret < 0) {
+ return KNOTD_EINVAL;
+ }
+ }
+#endif /* IPV6_V6ONLY */
+#endif /* DISABLE_IPV6 */
}
/* Reuse old address if taken. */
- int flag = 1;
- int ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
+ ret = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
&flag, sizeof(flag));
if (ret < 0) {
return KNOTD_EINVAL;
@@ -164,7 +177,7 @@ int socket_bind(int socket, int family, const char *addr, unsigned short port)
/* Bind to specified address. */
int res = bind(socket, paddr, addrlen);
if (res < 0) {
- log_server_error("Cannot bind to socket (%d).\n",
+ log_server_error("Cannot bind to socket (errno %d).\n",
errno);
return knot_map_errno(EADDRINUSE, EINVAL, EACCES, ENOMEM);
}
diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h
index 2185f03..2185f03 100644..100755
--- a/src/knot/server/socket.h
+++ b/src/knot/server/socket.h
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index 977de0b..60f4301 100644..100755
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -104,7 +104,10 @@ static void tcp_sweep(fdset_t *set, int fd, void* data)
int r_port = 0;
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
- getpeername(fd, (struct sockaddr*)&addr, &len);
+ if (getpeername(fd, (struct sockaddr*)&addr, &len) < 0) {
+ dbg_net("tcp: sweep getpeername() on invalid socket=%d\n", fd);
+ return;
+ }
/* Translate */
if (addr.ss_family == AF_INET) {
@@ -158,15 +161,13 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
log_server_error("Socket type %d is not supported, "
"IPv6 support is probably disabled.\n",
w->ioh->type);
- return KNOTD_ECONNREFUSED;
+ return KNOTD_EINVAL;
}
/* Receive data. */
int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr);
if (n <= 0) {
dbg_net("tcp: client on fd=%d disconnected\n", fd);
- fdset_remove(w->fdset, fd);
- close(fd);
if (n == KNOTD_EAGAIN) {
char r_addr[SOCKADDR_STRLEN];
sockaddr_tostr(&addr, r_addr, sizeof(r_addr));
@@ -184,7 +185,7 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
knot_packet_t *packet =
knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
if (packet == NULL) {
- int ret = knot_ns_error_response_from_query(ns, qbuf, n,
+ int ret = knot_ns_error_response_from_query_wire(ns, qbuf, n,
KNOT_RCODE_SERVFAIL,
qbuf, &resp_len);
@@ -198,8 +199,8 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
int parse_res = knot_ns_parse_packet(qbuf, n, packet, &qtype);
if (unlikely(parse_res != KNOT_EOK)) {
if (parse_res > 0) { /* Returned RCODE */
- int ret = knot_ns_error_response_from_query(ns, qbuf, n,
- parse_res, qbuf, &resp_len);
+ int ret = knot_ns_error_response_from_query(ns, packet,
+ parse_res, qbuf, &resp_len);
if (ret == KNOT_EOK) {
tcp_reply(fd, qbuf, resp_len);
@@ -235,10 +236,9 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
/* Prepare context. */
res = xfr_request_init(&xfr, xfrt, XFR_FLAG_TCP, packet);
if (res != KNOTD_EOK) {
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_SERVFAIL, qbuf,
- &resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_SERVFAIL,
+ qbuf, &resp_len);
res = KNOTD_EOK;
break;
}
@@ -252,9 +252,9 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
return xfr_answer(ns, &xfr);
case KNOT_QUERY_UPDATE:
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_NOTIMPL, qbuf, &resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_NOTIMPL,
+ qbuf, &resp_len);
res = KNOTD_EOK;
break;
@@ -268,17 +268,17 @@ static int tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen
case KNOT_RESPONSE_NORMAL: /*!< TCP handler doesn't send queries. */
case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */
case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_REFUSED, qbuf, &resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_REFUSED,
+ qbuf, &resp_len);
res = KNOTD_EOK;
break;
/* Unknown opcodes. */
default:
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_FORMERR, qbuf, &resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_FORMERR,
+ qbuf, &resp_len);
res = KNOTD_EOK;
break;
}
@@ -326,7 +326,11 @@ static int tcp_accept(int fd)
struct timeval tv;
tv.tv_sec = TCP_ACTIVITY_WD;
tv.tv_usec = 0;
- setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ log_server_warning("Couldn't set up TCP connection "
+ "watchdog timer for fd=%d.\n",
+ incoming);
+ }
#endif
}
@@ -389,7 +393,7 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen)
*/
#ifdef TCP_CORK
int cork = 1;
- setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+ int uncork = setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
#endif
/* Send message size. */
@@ -406,9 +410,11 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen)
}
#ifdef TCP_CORK
- /* Uncork. */
- cork = 0;
- setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+ /* Uncork only if corked successfuly. */
+ if (uncork == 0) {
+ cork = 0;
+ setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+ }
#endif
return sent;
}
@@ -444,7 +450,9 @@ int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr)
/* Get peer name. */
if (addr) {
socklen_t alen = addr->len;
- getpeername(fd, addr->ptr, &alen);
+ if (getpeername(fd, addr->ptr, &alen) < 0) {
+ return KNOTD_EMALF;
+ }
}
/* Receive payload. */
@@ -582,6 +590,12 @@ int tcp_loop_worker(dthread_t *thread)
"set to %ds\n",
it.fd, TCP_ACTIVITY_WD);
}
+ /*! \todo Refactor to allow erase on iterator.*/
+ if (ret == KNOTD_ECONNREFUSED) {
+ fdset_remove(w->fdset, it.fd);
+ close(it.fd);
+ break;
+ }
}
diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h
index ab6baab..ab6baab 100644..100755
--- a/src/knot/server/tcp-handler.h
+++ b/src/knot/server/tcp-handler.h
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index dcb5b1f..fc4e9b1 100644..100755
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -85,7 +85,7 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
if (packet == NULL) {
dbg_net("udp: failed to create packet on fd=%d\n", fd);
- int ret = knot_ns_error_response_from_query(ns, qbuf, qbuflen,
+ int ret = knot_ns_error_response_from_query_wire(ns, qbuf, qbuflen,
KNOT_RCODE_SERVFAIL,
qbuf, resp_len);
@@ -101,15 +101,25 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
if (unlikely(res != KNOTD_EOK)) {
dbg_net("udp: failed to parse packet on fd=%d\n", fd);
if (res > 0) { /* Returned RCODE */
- int ret = knot_ns_error_response_from_query(ns, qbuf,
- qbuflen,
- res, qbuf,
- resp_len);
+// int ret = knot_ns_error_response_from_query_wire(ns,
+// qbuf, qbuflen, res, qbuf, resp_len);
+ int ret = knot_ns_error_response_from_query(ns,
+ packet, res, qbuf, resp_len);
if (ret != KNOT_EOK) {
knot_packet_free(&packet);
return KNOTD_EMALF;
}
+ } else {
+ assert(res < 0);
+ int ret = knot_ns_error_response_from_query_wire(
+ ns, qbuf, qbuflen, KNOT_RCODE_SERVFAIL, qbuf,
+ resp_len);
+
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&packet);
+ return ret;
+ }
}
knot_packet_free(&packet);
@@ -122,14 +132,6 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
res = KNOTD_ERROR;
switch(qtype) {
- /* Response types. */
- case KNOT_RESPONSE_NORMAL:
- res = zones_process_response(ns, addr, packet, qbuf, resp_len);
- break;
- case KNOT_RESPONSE_NOTIFY:
- res = notify_process_response(ns, packet, addr, qbuf, resp_len);
- break;
-
/* Query types. */
case KNOT_QUERY_NORMAL:
res = zones_normal_query_answer(ns, packet, addr, qbuf,
@@ -140,9 +142,9 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
* Bind responds with FORMERR.
*/
/*! \note Draft exists for AXFR/UDP, but has not been standardized. */
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_FORMERR, qbuf, resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_FORMERR, qbuf,
+ resp_len);
res = KNOTD_EOK;
break;
case KNOT_QUERY_IXFR:
@@ -151,7 +153,7 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
* but I have found no tool or slave server to actually attempt
* IXFR/UDP.
*/
- knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA);
+// knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA);
res = zones_normal_query_answer(ns, packet, addr,
qbuf, resp_len,
NS_TRANSPORT_UDP);
@@ -163,27 +165,26 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
case KNOT_QUERY_UPDATE:
dbg_net("udp: UPDATE query on fd=%d not implemented\n", fd);
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_NOTIMPL, qbuf, resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_NOTIMPL, qbuf,
+ resp_len);
res = KNOTD_EOK;
break;
/* Unhandled opcodes. */
case KNOT_RESPONSE_AXFR: /*!< Processed in XFR handler. */
case KNOT_RESPONSE_IXFR: /*!< Processed in XFR handler. */
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_REFUSED, qbuf,
- resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_REFUSED, qbuf,
+ resp_len);
res = KNOTD_EOK;
break;
/* Unknown opcodes */
default:
- knot_ns_error_response(ns, knot_packet_id(packet),
- &packet->header.flags1,
- KNOT_RCODE_FORMERR, qbuf, resp_len);
+ knot_ns_error_response_from_query(ns, packet,
+ KNOT_RCODE_FORMERR, qbuf,
+ resp_len);
res = KNOTD_EOK;
break;
}
@@ -204,17 +205,14 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
/* Set CPU affinity to improve load distribution on multicore systems.
* Partial overlapping mask to be nice to scheduler.
*/
-#ifdef HAVE_PTHREAD_SETAFFINITY_NP
int cpcount = dt_online_cpus();
if (cpcount > 0) {
- unsigned tid = dt_get_id(thread);
- cpu_set_t cpus;
- CPU_ZERO(&cpus);
- CPU_SET(tid % cpcount, &cpus);
- CPU_SET((tid + 1) % cpcount, &cpus);
- dt_setaffinity(thread, &cpus, sizeof(cpu_set_t));
+ unsigned cpu[2];
+ cpu[0] = dt_get_id(thread);
+ cpu[1] = (cpu[0] + 1) % cpcount;
+ cpu[0] = cpu[0] % cpcount;
+ dt_setaffinity(thread, cpu, 2);
}
-#endif
knot_nameserver_t *ns = h->server->nameserver;
@@ -389,6 +387,7 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
free(addrs);
free(iov);
free(msgs);
+ close(sock);
return KNOTD_ENOMEM;
}
@@ -407,17 +406,14 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
/* Set CPU affinity to improve load distribution on multicore systems.
* Partial overlapping mask to be nice to scheduler.
*/
-#ifdef HAVE_PTHREAD_SETAFFINITY_NP
int cpcount = dt_online_cpus();
if (cpcount > 0) {
- unsigned tid = dt_get_id(thread);
- cpu_set_t cpus;
- CPU_ZERO(&cpus);
- CPU_SET(tid % cpcount, &cpus);
- CPU_SET((tid + 1) % cpcount, &cpus);
- dt_setaffinity(thread, &cpus, sizeof(cpu_set_t));
+ unsigned cpu[2];
+ cpu[0] = dt_get_id(thread);
+ cpu[1] = (cpu[0] + 1) % cpcount;
+ cpu[0] = cpu[0] % cpcount;
+ dt_setaffinity(thread, cpu, 2);
}
-#endif
/* Loop until all data is read. */
ssize_t n = 0;
@@ -433,7 +429,9 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
/* Error and interrupt handling. */
if (unlikely(n <= 0)) {
- if (errno != EINTR && errno != 0) {
+ if (errno != EINTR && errno != 0 && n < 0) {
+ log_server_error("I/O failure in UDP - errno %d "
+ "(Linux/recvmmsg)", errno);
dbg_net("udp: recvmmsg() failed: %d\n",
errno);
}
@@ -493,7 +491,10 @@ void __attribute__ ((constructor)) udp_master_init()
#ifdef MSG_WAITFORONE
/* Check for recvmmsg() support. */
if (dlsym(RTLD_DEFAULT, "recvmmsg") != 0) {
- _udp_master = udp_master_recvmmsg;
+ int r = recvmmsg(0, NULL, 0, 0, 0);
+ if (errno != ENOSYS) {
+ _udp_master = udp_master_recvmmsg;
+ }
}
/* Check for sendmmsg() support. */
diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h
index f5fcd04..f5fcd04 100644..100755
--- a/src/knot/server/udp-handler.h
+++ b/src/knot/server/udp-handler.h
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index a77f1f1..7863c9a 100644..100755
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -45,12 +45,19 @@
#define XFR_QUERY_WD 10 /*!< SOA/NOTIFY query timeout [s]. */
#define XFR_SWEEP_INTERVAL 2 /*! [seconds] between sweeps. */
#define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */
+#define XFR_MSG_DLTTR 9 /*! Index of letter differentiating IXFR/AXFR in log msg. */
/*! \brief Send interrupt to all workers. */
void xfr_interrupt(xfrhandler_t *h)
{
for(unsigned i = 0; i < h->unit->size; ++i) {
- evqueue_write(h->workers[i]->q, "", 1);
+ evqueue_t *q = h->workers[i]->q;
+ if (evqueue_write(q, "", 1) == 1) {
+ close(q->fds[EVQUEUE_WRITEFD]);
+ q->fds[EVQUEUE_WRITEFD] = -1;
+ } else {
+ dt_stop_id(h->unit->threads[i]);
+ }
}
}
@@ -63,6 +70,55 @@ static void xfr_request_deinit(knot_ns_xfr_t *r)
}
}
+/*!
+ * \brief Clean pending transfer data.
+ */
+static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data)
+{
+ int ret = KNOTD_EOK;
+ knot_changesets_t *chs = 0;
+
+ dbg_xfr_verb("Cleaning up after XFR-in.\n");
+
+ switch(data->type) {
+ case XFR_TYPE_AIN:
+ if (data->flags & XFR_FLAG_AXFR_FINISHED) {
+ knot_zone_contents_deep_free(
+ &data->new_contents, 1);
+ } else {
+ if (data->data) {
+ xfrin_constructed_zone_t *constr_zone =
+ (xfrin_constructed_zone_t *)data->data;
+ knot_zone_contents_deep_free(
+ &(constr_zone->contents), 0);
+ xfrin_free_orphan_rrsigs(&(constr_zone->rrsigs));
+ free(data->data);
+ data->data = 0;
+ }
+ }
+ break;
+ case XFR_TYPE_IIN:
+ if (data->data) {
+ chs = (knot_changesets_t *)data->data;
+ knot_free_changesets(&chs);
+ data->data = NULL;
+ }
+
+ // this function is called before new contents are created
+ assert(data->new_contents == NULL);
+
+ break;
+ }
+
+ /* Cleanup other data - so that the structure may be reused. */
+ data->packet_nr = 0;
+ data->tsig_data_size = 0;
+
+ dbg_xfr_detail("Done.\n");
+
+ return ret;
+}
+
/*! \brief Free allocated xfer descriptor (also deinitializes). */
static void xfr_free_task(knot_ns_xfr_t *task)
{
@@ -83,7 +139,8 @@ static void xfr_free_task(knot_ns_xfr_t *task)
}
/* Unlock if XFR/IN.*/
- if (task->type == XFR_TYPE_AIN || task->type == XFR_TYPE_IIN) {
+ int is_xfer = task->type == XFR_TYPE_AIN || task->type == XFR_TYPE_IIN;
+ if (is_xfer) {
knot_zone_t *zone = task->zone;
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
if (zd) {
@@ -92,9 +149,24 @@ static void xfr_free_task(knot_ns_xfr_t *task)
}
}
- /* Deinitialize */
- xfr_request_deinit(task);
+ /* Free TSIG buffers. */
+ if (task->digest) {
+ free(task->digest);
+ task->digest = NULL;
+ task->digest_size = 0;
+ }
+ if (task->tsig_data) {
+ free(task->tsig_data);
+ task->tsig_data = NULL;
+ task->tsig_data_size = 0;
+ }
+
if (!task->session_closed) {
+ /* Cleanup pending request. */
+ if (is_xfer) {
+ xfr_xfrin_cleanup(w, task);
+ }
+
/* Remove fd-related data. */
xfrhandler_t *h = w->master;
pthread_mutex_lock(&h->tasks_mx);
@@ -102,6 +174,12 @@ static void xfr_free_task(knot_ns_xfr_t *task)
pthread_mutex_unlock(&h->tasks_mx);
close(task->session);
}
+
+ /* No further access to zone. */
+ knot_zone_release(task->zone);
+
+ /* Deinitialize */
+ xfr_request_deinit(task);
free(task);
}
@@ -139,11 +217,15 @@ static knot_ns_xfr_t *xfr_handler_task(xfrworker_t *w, int fd)
static int xfr_udp_timeout(knot_ns_xfr_t *data)
{
/* Close socket. */
+ rcu_read_lock();
knot_zone_t *z = data->zone;
if (z && knot_zone_get_contents(z) && knot_zone_data(z)) {
- log_zone_info("%s Failed, timeout exceeded.\n",
- data->msgpref);
+ if (!(knot_zone_flags(z) & KNOT_ZONE_DISCARDED)) {
+ log_zone_info("%s Failed, timeout exceeded.\n",
+ data->msgpref);
+ }
}
+ rcu_read_unlock();
/* Invalidate pending query. */
xfr_free_task(data);
@@ -161,15 +243,93 @@ static int xfr_udp_timeout(knot_ns_xfr_t *data)
*/
static int xfr_process_udp_resp(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
{
+ /* Check if zone is valid. */
+ int ret = KNOTD_ECONNREFUSED;
+ rcu_read_lock();
+ if (knot_zone_flags(data->zone) & KNOT_ZONE_DISCARDED) {
+ rcu_read_unlock();
+ return ret;
+ }
+ rcu_read_unlock();
+
/* Receive msg. */
- ssize_t n = recvfrom(data->session, data->wire, data->wire_size, 0, data->addr.ptr, &data->addr.len);
+ ssize_t n = recvfrom(data->session, data->wire, data->wire_size,
+ 0, data->addr.ptr, &data->addr.len);
size_t resp_len = data->wire_size;
- if (n > 0) {
- udp_handle(fd, data->wire, n, &resp_len, &data->addr, w->ns);
+ if (n <= 0) {
+ return ret;
+ }
+
+ // parse packet
+ knot_packet_t *re = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ if (re == NULL) {
+ return KNOTD_ENOMEM;
+ }
+
+ knot_packet_type_t rt = KNOT_RESPONSE_NORMAL;
+ ret = knot_ns_parse_packet(data->wire, n, re, &rt);
+ if (ret != KNOTD_EOK) {
+ knot_packet_free(&re);
+ return KNOTD_EOK; /* Ignore */
+ }
+
+ /* Ignore other packets. */
+ if (rt != KNOT_RESPONSE_NORMAL && rt != KNOT_RESPONSE_NOTIFY) {
+ knot_packet_free(&re);
+ return KNOTD_EOK; /* Ignore */
+ }
+ ret = knot_packet_parse_rest(re);
+ if (ret != KNOT_EOK) {
+ knot_packet_free(&re);
+ return KNOTD_EOK; /* Ignore */
+ }
+
+ // check TSIG
+ const knot_rrset_t * tsig_rr = knot_packet_tsig(re);
+ if (data->tsig_key != NULL) {
+ /*! \todo Not sure about prev_time_signed, but this is the first
+ * reply and we should pass query sign time as the time
+ * may be different. Leaving to 0.
+ */
+ ret = knot_tsig_client_check(tsig_rr, data->wire, n,
+ data->digest, data->digest_size,
+ data->tsig_key, 0);
+ if (ret != KNOT_EOK) {
+ log_server_error("%s %s\n",
+ data->msgpref, knot_strerror(ret));
+ knot_packet_free(&re);
+ return KNOTD_ECONNREFUSED;
+ }
+
+ }
+
+ // process response
+ switch(rt) {
+ case KNOT_RESPONSE_NORMAL:
+ ret = zones_process_response(w->ns, &data->addr, re,
+ data->wire, &resp_len);
+ break;
+ case KNOT_RESPONSE_NOTIFY:
+ ret = notify_process_response(w->ns, re, &data->addr,
+ data->wire, &resp_len);
+ break;
+ default:
+ break;
}
+ knot_packet_free(&re);
+
+ /* Check up-to-date zone. */
+ if (ret == KNOTD_EUPTODATE) {
+ log_server_info("%s %s\n", data->msgpref, knotd_strerror(ret));
+ ret = KNOTD_ECONNREFUSED;
+ }
+
/* Invalidate pending query. */
- return KNOTD_ECONNREFUSED;
+ if (ret == KNOTD_EOK) {
+ ret = KNOTD_ECONNREFUSED;
+ }
+ return ret;
}
/*! \brief Sweep non-replied connection. */
@@ -250,46 +410,6 @@ static knot_ns_xfr_t *xfr_register_task(xfrworker_t *w, const knot_ns_xfr_t *req
}
/*!
- * \brief Clean pending transfer data.
- */
-static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data)
-{
- int ret = KNOTD_EOK;
- knot_changesets_t *chs = 0;
-
- switch(data->type) {
- case XFR_TYPE_AIN:
- if (data->flags & XFR_FLAG_AXFR_FINISHED) {
- knot_zone_contents_deep_free(
- &data->new_contents, 1);
- } else {
- if (data->data) {
- xfrin_constructed_zone_t *constr_zone =
- (xfrin_constructed_zone_t *)data->data;
- knot_zone_contents_deep_free(
- &(constr_zone->contents), 0);
- xfrin_free_orphan_rrsigs(&(constr_zone->rrsigs));
- free(data->data);
- data->data = 0;
- }
- }
- break;
- case XFR_TYPE_IIN:
- if (data->data) {
- chs = (knot_changesets_t *)data->data;
- knot_free_changesets(&chs);
- }
-
- // this function is called before new contents are created
- assert(data->new_contents == NULL);
-
- break;
- }
-
- return ret;
-}
-
-/*!
* \brief Finalize XFR/IN transfer.
*
* \param w XFR worker.
@@ -382,7 +502,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
/* Switch zone contents. */
switch_ret = xfrin_switch_zone(data->zone, data->new_contents,
- data->type);
+ data->type);
if (switch_ret != KNOT_EOK) {
log_zone_error("%s Failed to replace "
@@ -401,7 +521,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
break;
}
- xfrin_cleanup_successful_update( &chs->changes);
+ xfrin_cleanup_successful_update(&chs->changes);
/* Free changesets, but not the data. */
knot_free_changesets(&chs);
@@ -418,24 +538,6 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
}
/*!
- * \brief Prepare TSIG for XFR.
- */
-static int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key)
-{
- int ret = KNOT_EOK;
- xfr->tsig_key = key;
- xfr->tsig_size = tsig_wire_maxsize(key);
- xfr->digest_max_size = tsig_alg_digest_length(
- key->algorithm);
- xfr->digest = malloc(xfr->digest_max_size);
- memset(xfr->digest, 0 , xfr->digest_max_size);
- dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n",
- xfr->digest_max_size);
-
- return ret;
-}
-
-/*!
* \brief Check TSIG if exists.
*/
static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode, char **tag)
@@ -473,15 +575,11 @@ static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode, char **tag)
// return REFUSED
xfr->tsig_key = 0;
*rcode = KNOT_RCODE_REFUSED;
- return KNOT_EXFRREFUSED;
+ return KNOT_EXFRDENIED;
}
if (tsig_rr) {
tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr);
if (tsig_alg_digest_length(alg) == 0) {
- log_server_info("%s Unsupported digest algorithm "
- "requested, treating as "
- "bad key.\n",
- xfr->msgpref);
*rcode = KNOT_RCODE_NOTAUTH;
xfr->tsig_key = NULL;
xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
@@ -598,8 +696,14 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
/* Read DNS/TCP packet. */
int ret = 0;
int rcvd = tcp_recv(fd, buf, buflen, 0);
+
+ /* Raise read-lock and check if zone is still valid. */
+ rcu_read_lock();
+ int zone_discarded = (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED);
+
+ /* Handle incoming packet. */
data->wire_size = rcvd;
- if (rcvd <= 0) {
+ if (rcvd <= 0 || zone_discarded) {
data->wire_size = 0;
ret = KNOT_ECONN;
} else {
@@ -621,14 +725,15 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
/* AXFR-style IXFR. */
if (ret == KNOT_ENOIXFR) {
assert(data->type == XFR_TYPE_IIN);
- log_server_notice("%s Fallback to AXFR/IN.\n", data->msgpref);
+ log_server_notice("%s Fallback to AXFR.\n", data->msgpref);
data->type = XFR_TYPE_AIN;
- data->msgpref[0] = 'A';
+ data->msgpref[XFR_MSG_DLTTR] = 'A';
ret = knot_ns_process_axfrin(w->ns, data);
}
/* Check return code for errors. */
- dbg_xfr_verb("xfr: processed incoming XFR packet (res = %d)\n", ret);
+ dbg_xfr_verb("xfr: processed incoming XFR packet (%s)\n",
+ knot_strerror(ret));
/* Finished xfers. */
int xfer_finished = 0;
@@ -637,8 +742,8 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
}
/* IXFR refused, try again with AXFR. */
- if (zone && data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) {
- log_server_notice("%s Transfer failed, fallback to AXFR/IN.\n",
+ if (data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) {
+ log_server_notice("%s Transfer failed, fallback to AXFR.\n",
data->msgpref);
size_t bufsize = buflen;
data->wire_size = buflen; /* Reset maximum bufsize */
@@ -650,20 +755,26 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
data->wire, bufsize);
/* Switch to AIN type XFR and return now. */
if (ret == bufsize) {
+ rcu_read_unlock();
+ xfr_xfrin_cleanup(w, data);
data->type = XFR_TYPE_AIN;
- data->msgpref[0] = 'A';
+ data->msgpref[XFR_MSG_DLTTR] = 'A';
return KNOTD_EOK;
}
}
}
+
+ rcu_read_unlock();
/* Handle errors. */
- if (ret == KNOT_ENOXFR) {
- log_server_warning("%s Finished, %s\n",
- data->msgpref, knot_strerror(ret));
- } else if (ret < 0) {
- log_server_error("%s %s\n",
- data->msgpref, knot_strerror(ret));
+ if (!zone_discarded) {
+ if (ret == KNOT_ENOXFR) {
+ log_server_warning("%s Finished, %s\n",
+ data->msgpref, knot_strerror(ret));
+ } else if (ret < 0) {
+ log_server_error("%s %s\n",
+ data->msgpref, knot_strerror(ret));
+ }
}
/* Check finished zone. */
@@ -683,12 +794,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
knot_zone_t *zone = (knot_zone_t *)data->zone;
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
- /* Only for successful xfers. */
- if (ret > 0) {
+ /* Only for successful xfers on non-discarded zones. */
+ if (ret > 0 && !zone_discarded) {
ret = xfr_xfrin_finalize(w, data);
/* AXFR bootstrap timeout. */
- rcu_read_lock();
if (ret != KNOTD_EOK && !knot_zone_contents(zone)) {
/* Schedule request (60 - 90s random delay). */
int tmr_s = AXFR_BOOTSTRAP_RETRY;
@@ -702,25 +812,11 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf,
/* Update timers. */
server_t *server = (server_t *)knot_ns_get_data(w->ns);
zones_timers_update(zone, zd->conf, server->sched);
- rcu_read_unlock();
-
} else {
/* Cleanup */
xfr_xfrin_cleanup(w, data);
}
- /* Free TSIG buffers. */
- if (data->digest) {
- free(data->digest);
- data->digest = 0;
- data->digest_size = 0;
- }
- if (data->tsig_data) {
- free(data->tsig_data);
- data->tsig_data = 0;
- data->tsig_data_size = 0;
- }
-
/* Disconnect. */
result = KNOTD_ECONNREFUSED; /* Make it disconnect. */
}
@@ -753,6 +849,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
/* Enqueue to worker that has zone locked for XFR/IN. */
int ret = pthread_mutex_trylock(&zd->xfr_in.lock);
+ rcu_read_lock();
if (ret != 0) {
dbg_xfr_verb("xfr: XFR/IN switching to another thread, "
"zone '%s' is already in transfer\n",
@@ -769,8 +866,11 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
strerror_r(errno, ebuf, sizeof(ebuf));
dbg_xfr("xfr: couldn't write request to evqueue: %s\n",
ebuf);
+ rcu_read_unlock();
return KNOTD_ERROR;
}
+
+ rcu_read_unlock();
return KNOTD_EOK;
} else {
zd->xfr_in.wrkr = w;
@@ -798,6 +898,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
}
if (ret < 0) {
+ rcu_read_unlock();
pthread_mutex_unlock(&zd->xfr_in.lock);
if (fd >= 0) {
close(fd);
@@ -811,6 +912,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
/* Duplicate existing socket descriptor. */
data->session = dup(data->session);
if (data->session < 0) {
+ rcu_read_unlock();
pthread_mutex_unlock(&zd->xfr_in.lock);
log_server_warning("Not enough memory to duplicate \n"
"sockets.\n");
@@ -819,12 +921,11 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
}
/* Fetch zone contents. */
- rcu_read_lock();
const knot_zone_contents_t *contents = knot_zone_contents(zone);
if (!contents && data->type == XFR_TYPE_IIN) {
pthread_mutex_unlock(&zd->xfr_in.lock);
rcu_read_unlock();
- log_server_warning("%s Refusing to start IXFR/IN on zone with no "
+ log_server_warning("%s Refusing to start IXFR on zone with no "
"contents.\n", data->msgpref);
close(data->session);
data->session = -1;
@@ -958,7 +1059,7 @@ static int xfr_answer_ixfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
"Fallback to AXFR.\n",
xfr->msgpref);
xfr->type = XFR_TYPE_AOUT;
- xfr->msgpref[0] = 'A';
+ xfr->msgpref[XFR_MSG_DLTTR] = 'A';
return xfr_answer_axfr(ns, xfr);
} else if (chsload == KNOTD_EMALF) {
xfr->rcode = KNOT_RCODE_FORMERR;
@@ -973,7 +1074,7 @@ static int xfr_answer_ixfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
/* Finally, answer. */
if (chsload == KNOTD_EOK) {
ret = knot_ns_answer_ixfr(ns, xfr);
- dbg_xfr("xfr: ns_answer_ixfr() = %d.\n", ret);
+ dbg_xfr("xfr: ns_answer_ixfr() = %s.\n", knot_strerror(ret));
}
return ret;
@@ -987,6 +1088,7 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag)
return KNOTD_EINVAL;
}
+ rcu_read_lock();
char r_addr[SOCKADDR_STRLEN];
char *r_key = NULL;
int r_port = sockaddr_portnum(&req->addr);
@@ -1013,13 +1115,12 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag)
}
/* Prepare log message. */
- conf_read_lock();
const char *zname = req->zname;
if (zname == NULL && req->zone != NULL) {
zonedata_t *zd = (zonedata_t *)knot_zone_data(req->zone);
if (zd == NULL) {
free(r_key);
- conf_read_unlock();
+ rcu_read_unlock();
return KNOTD_EINVAL;
} else {
zname = zd->conf->name;
@@ -1028,16 +1129,16 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag)
const char *pformat = NULL;
switch (req->type) {
case XFR_TYPE_AIN:
- pformat = "AXFR transfer of '%s/IN' with '%s@%d'%s:";
+ pformat = "Incoming AXFR transfer of '%s' with '%s@%d'%s:";
break;
case XFR_TYPE_IIN:
- pformat = "IXFR transfer of '%s/IN' with '%s@%d'%s:";
+ pformat = "Incoming IXFR transfer of '%s' with '%s@%d'%s:";
break;
case XFR_TYPE_AOUT:
- pformat = "AXFR transfer of '%s/OUT' to '%s@%d'%s:";
+ pformat = "Outgoing AXFR transfer of '%s' to '%s@%d'%s:";
break;
case XFR_TYPE_IOUT:
- pformat = "IXFR transfer of '%s/OUT' to '%s@%d'%s:";
+ pformat = "Outgoing IXFR transfer of '%s' to '%s@%d'%s:";
break;
case XFR_TYPE_NOTIFY:
pformat = "NOTIFY query of '%s' to '%s@%d'%s:";
@@ -1065,7 +1166,7 @@ static int xfr_update_msgpref(knot_ns_xfr_t *req, const char *keytag)
req->msgpref = msg;
}
- conf_read_unlock();
+ rcu_read_unlock();
free(r_key);
return KNOTD_EOK;
}
@@ -1277,9 +1378,11 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
return KNOTD_EINVAL;
}
+ rcu_read_lock();
int ret = knot_ns_init_xfr(ns, xfr);
+
int xfr_failed = (ret != KNOT_EOK);
- const char * errstr = knot_strerror(ret);
+ const char *errstr = knot_strerror(ret);
// use the QNAME as the zone name to get names also for
// zones that are not in the server
@@ -1310,6 +1413,23 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
}
free(keytag);
+ /* Announce. */
+ log_server_info("%s Started.\n", xfr->msgpref);
+ switch (ret) {
+ case KNOT_EXFRDENIED:
+ log_server_info("%s TSIG required, but not found in query.\n",
+ xfr->msgpref);
+ break;
+ case KNOT_TSIG_EBADKEY:
+ log_server_info("%s Unsupported digest "
+ "algorithm requested, "
+ "treating as bad key.\n",
+ xfr->msgpref);
+ break;
+ default:
+ break;
+ }
+
/* Prepare place for TSIG data */
xfr->tsig_data = malloc(KNOT_NS_TSIG_DATA_MAX_SIZE);
if (xfr->tsig_data) {
@@ -1356,6 +1476,7 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *xfr)
free(xfr->tsig_data);
xfr->tsig_data = NULL;
xfr_request_deinit(xfr);
+ rcu_read_unlock();
/* Cleanup. */
free(xfr->digest);
@@ -1381,6 +1502,8 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
return KNOTD_ENOTRUNNING;
}
+ rcu_read_lock();
+
/* Update request. */
xfr.wire = buf;
xfr.wire_size = buflen;
@@ -1389,12 +1512,15 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
xfr_update_msgpref(&xfr, NULL);
/* Check if not already processing. */
- zonedata_t *zd = NULL;
- if (xfr.zone != NULL) {
- zd = (zonedata_t *)knot_zone_data(xfr.zone);
- }
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(xfr.zone);
- conf_read_lock();
+ /* Check if the zone is not discarded. */
+ if (knot_zone_flags(xfr.zone) & KNOT_ZONE_DISCARDED) {
+ xfr_request_deinit(&xfr);
+ knot_zone_release(xfr.zone);
+ rcu_read_unlock();
+ return KNOTD_EOK;
+ }
/* Handle request. */
knot_ns_xfr_t *task = NULL;
@@ -1423,6 +1549,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
log_server_error("%s %s\n",
xfr.msgpref, knotd_strerror(ret));
}
+ knot_zone_release(xfr.zone); /* No further access to zone. */
}
break;
@@ -1431,21 +1558,21 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
/* Register task. */
task = xfr_register_task(w, &xfr);
if (!task) {
+ knot_zone_release(xfr.zone); /* No further access to zone. */
ret = KNOTD_ENOMEM;
- break;
+ } else {
+ /* Add timeout. */
+ fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD);
+ log_server_info("%s Query issued.\n", xfr.msgpref);
+ ret = KNOTD_EOK;
}
-
- /* Add timeout. */
- fdset_set_watchdog(w->fdset, task->session, XFR_QUERY_WD);
- log_server_info("%s Query issued.\n", xfr.msgpref);
- ret = KNOTD_EOK;
break;
default:
log_server_error("Unknown XFR request type (%d).\n", xfr.type);
break;
}
- conf_read_unlock();
+ rcu_read_unlock();
/* Deinitialize (it is already registered, or discarded).
* Right now, this only frees temporary msgpref.
@@ -1506,7 +1633,6 @@ int xfr_worker(dthread_t *thread)
fdset_begin(w->fdset, &it);
int rfd_event = 0;
while(nfds > 0) {
-
/* Check if it request. */
if (it.fd == rfd) {
rfd_event = 1; /* Delay new tasks after processing. */
@@ -1527,6 +1653,8 @@ int xfr_worker(dthread_t *thread)
ret = xfr_process_event(w, it.fd, data, buf, buflen);
if (ret != KNOTD_EOK) {
xfr_free_task(data);
+ /*! \todo Refactor to allow erase on iterator.*/
+ break;
}
}
@@ -1551,6 +1679,11 @@ int xfr_worker(dthread_t *thread)
next_sweep.tv_sec += XFR_SWEEP_INTERVAL;
}
}
+
+ /* Check for interrupt request. */
+ if (ret == KNOTD_ENOTRUNNING) {
+ break;
+ }
}
/* Stop whole unit. */
@@ -1559,3 +1692,28 @@ int xfr_worker(dthread_t *thread)
thread->data = 0;
return KNOTD_EOK;
}
+
+int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key)
+{
+ if (xfr == NULL || key == NULL) {
+ return KNOTD_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ xfr->tsig_key = key;
+ xfr->tsig_size = tsig_wire_maxsize(key);
+ xfr->digest_max_size = tsig_alg_digest_length(
+ key->algorithm);
+ xfr->digest = malloc(xfr->digest_max_size);
+ if (xfr->digest == NULL) {
+ xfr->tsig_key = NULL;
+ xfr->tsig_size = 0;
+ xfr->digest_max_size = 0;
+ return KNOTD_ENOMEM;
+ }
+ memset(xfr->digest, 0 , xfr->digest_max_size);
+ dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n",
+ xfr->digest_max_size);
+
+ return ret;
+}
diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h
index 02eb189..e2f5643 100644..100755
--- a/src/knot/server/xfr-handler.h
+++ b/src/knot/server/xfr-handler.h
@@ -170,6 +170,17 @@ int xfr_answer(knot_nameserver_t *ns, knot_ns_xfr_t *req);
*/
int xfr_worker(dthread_t *thread);
+/*!
+ * \brief Prepare TSIG for XFR.
+ * \param xfr XFR request.
+ * \param key Used TSIG key.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on NULL parameters.
+ * \retval KNOTD_ENOMEM when out of memory.
+ */
+int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key);
+
#endif // _KNOTD_XFRHANDLER_H_
/*! @} */
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 37c1316..95ab2b1 100644..100755
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -39,6 +39,7 @@
#include "libknot/updates/changesets.h"
#include "libknot/tsig-op.h"
#include "libknot/packet/response.h"
+#include "libknot/zone/zone-diff.h"
static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100;
static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
@@ -46,7 +47,9 @@ static const size_t XFRIN_BOOTSTRAP_DELAY = 60; /*!< AXFR bootstrap avg. delay *
/* Forward declarations. */
static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *zf);
-
+static int zones_dump_zone_binary(knot_zone_contents_t *zone,
+ const char *zonedb,
+ const char *zonefile);
/*----------------------------------------------------------------------------*/
/*!
@@ -117,11 +120,13 @@ static int zonedata_destroy(knot_zone_t *zone)
/* Close IXFR db. */
journal_release(zd->ixfr_db);
+
+ /* Free assigned config. */
+ conf_free_zone(zd->conf);
free(zd);
/* Invalidate. */
- zone->dtor = 0;
zone->data = 0;
return KNOTD_EOK;
@@ -172,7 +177,7 @@ static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone)
char ebuf[256] = {0};
strerror_r(errno, ebuf, sizeof(ebuf));
log_server_warning("Couldn't open journal file for zone '%s', "
- "disabling IXFR/IN. (%s)\n", cfg->name, ebuf);
+ "disabling incoming IXFR. (%s)\n", cfg->name, ebuf);
}
/* Initialize IXFR database syncing event. */
@@ -180,7 +185,7 @@ static int zonedata_init(conf_zone_t *cfg, knot_zone_t *zone)
/* Set and install destructor. */
zone->data = zd;
- zone->dtor = zonedata_destroy;
+ knot_zone_set_dtor(zone, zonedata_destroy);
/* Set zonefile SOA serial. */
const knot_rrset_t *soa_rrs = 0;
@@ -305,6 +310,12 @@ static int zones_expire_ev(event_t *e)
zonedata_t *zd = (zonedata_t *)zone->data;
rcu_read_lock();
+ /* Check if zone is not discarded. */
+ if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) {
+ rcu_read_unlock();
+ return KNOTD_EOK;
+ }
+
/* Do not issue SOA query if transfer is pending. */
int locked = pthread_mutex_trylock(&zd->xfr_in.lock);
if (locked != 0) {
@@ -336,10 +347,12 @@ static int zones_expire_ev(event_t *e)
pthread_mutex_unlock(&zd->xfr_in.lock);
log_server_warning("Non-existent zone expired. Ignoring.\n");
rcu_read_unlock();
- return 0;
+ return KNOTD_EOK;
}
/* Publish expired zone. */
+ /* Need to keep a reference in case zone get's deleted in meantime. */
+ knot_zone_retain(zone);
rcu_read_unlock();
synchronize_rcu();
rcu_read_lock();
@@ -368,7 +381,10 @@ static int zones_expire_ev(event_t *e)
pthread_mutex_unlock(&zd->xfr_in.lock);
rcu_read_unlock();
- return 0;
+ /* Release holding reference. */
+ knot_zone_release(zone);
+
+ return KNOTD_EOK;
}
/*!
@@ -377,16 +393,23 @@ static int zones_expire_ev(event_t *e)
static int zones_refresh_ev(event_t *e)
{
dbg_zones("zones: REFRESH or RETRY timer event\n");
+ rcu_read_lock();
knot_zone_t *zone = (knot_zone_t *)e->data;
if (zone == NULL || zone->data == NULL) {
+ rcu_read_unlock();
return KNOTD_EINVAL;
}
/* Cancel pending timers. */
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
+
+ /* Check if zone is not discarded. */
+ if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) {
+ rcu_read_unlock();
+ return KNOTD_EOK;
+ }
/* Check for contents. */
- rcu_read_lock();
if (!knot_zone_contents(zone)) {
/* Bootstrap from XFR master. */
@@ -429,13 +452,20 @@ static int zones_refresh_ev(event_t *e)
++zd->xfr_in.scheduled;
pthread_mutex_unlock(&zd->xfr_in.lock);
+ /* Retain pointer to zone for processing. */
+ knot_zone_retain(xfr_req.zone);
+
/* Unlock zone contents. */
rcu_read_unlock();
/* Mark as finished to prevent stalling. */
evsched_event_finished(e->parent);
+ int ret = xfr_request(zd->server->xfr_h, &xfr_req);
+ if (ret != KNOTD_EOK) {
+ knot_zone_release(xfr_req.zone); /* Discard */
+ }
+ return ret;
- return xfr_request(zd->server->xfr_h, &xfr_req);
}
/* Do not issue SOA query if transfer is pending. */
@@ -485,17 +515,21 @@ static int zones_refresh_ev(event_t *e)
size_t buflen = SOCKET_MTU_SZ;
- /*! \todo [TSIG] CHANGE!!! only for compatibility now. */
- knot_ns_xfr_t xfr_req;
- memset(&xfr_req, 0, sizeof(knot_ns_xfr_t));
- xfr_req.wire = qbuf;
+ knot_ns_xfr_t req;
+ memset(&req, 0, sizeof(knot_ns_xfr_t));
+ req.wire = qbuf;
+
+ /* Select TSIG key. */
+ if (zd->xfr_in.tsig_key.name) {
+ xfr_prepare_tsig(&req, &zd->xfr_in.tsig_key);
+ }
/* Create query. */
int sock = -1;
char strbuf[256] = "Generic error.";
const char *errstr = strbuf;
sockaddr_t *master = &zd->xfr_in.master;
- int ret = xfrin_create_soa_query(zone->name, &xfr_req, &buflen);
+ int ret = xfrin_create_soa_query(zone->name, &req, &buflen);
if (ret == KNOT_EOK) {
/* Create socket on random port. */
@@ -548,26 +582,30 @@ static int zones_refresh_ev(event_t *e)
evsched_event_finished(e->parent);
/* Watch socket. */
- knot_ns_xfr_t req;
- memset(&req, 0, sizeof(req));
req.session = sock;
req.type = XFR_TYPE_SOA;
req.flags |= XFR_FLAG_UDP;
req.zone = zone;
+ req.wire = NULL;
memcpy(&req.addr, master, sizeof(sockaddr_t));
memcpy(&req.saddr, &zd->xfr_in.via, sizeof(sockaddr_t));
sockaddr_update(&req.addr);
sockaddr_update(&req.saddr);
+
+ /* Retain pointer to zone and issue. */
+ knot_zone_retain(req.zone);
if (ret == KNOTD_EOK) {
ret = xfr_request(zd->server->xfr_h, &req);
}
if (ret != KNOTD_EOK) {
+ free(req.digest);
+ knot_zone_release(req.zone); /* Discard */
log_server_warning("Failed to issue SOA query for zone '%s' (%s).\n",
zd->conf->name, errstr);
}
free(qbuf);
-
+
/* Unlock RCU. */
rcu_read_unlock();
@@ -580,21 +618,28 @@ static int zones_refresh_ev(event_t *e)
static int zones_notify_send(event_t *e)
{
dbg_notify("notify: NOTIFY timer event\n");
-
+ rcu_read_lock();
notify_ev_t *ev = (notify_ev_t *)e->data;
if (ev == NULL) {
+ rcu_read_unlock();
log_zone_error("NOTIFY invalid event received\n");
return KNOTD_EINVAL;
}
+
knot_zone_t *zone = ev->zone;
if (zone == NULL || zone->data == NULL) {
+ rcu_read_unlock();
log_zone_error("NOTIFY invalid event data received\n");
evsched_event_free(e->parent, e);
free(ev);
return KNOTD_EINVAL;
}
-
- rcu_read_lock();
+
+ /* Check if zone is not discarded. */
+ if (knot_zone_flags(zone) & KNOT_ZONE_DISCARDED) {
+ rcu_read_unlock(); /* Event will be freed on zonedata_destroy.*/
+ return KNOTD_EOK;
+ }
/* Check for answered/cancelled query. */
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
@@ -675,7 +720,13 @@ static int zones_notify_send(event_t *e)
req.zone = zone;
memcpy(&req.addr, &ev->addr, sizeof(sockaddr_t));
memcpy(&req.saddr, &ev->saddr, sizeof(sockaddr_t));
- xfr_request(zd->server->xfr_h, &req);
+
+ /* Retain pointer to zone and issue request. */
+ knot_zone_retain(req.zone);
+ ret = xfr_request(zd->server->xfr_h, &req);
+ if (ret != KNOTD_EOK) {
+ knot_zone_release(req.zone); /* Discard */
+ }
}
free(qbuf);
@@ -724,6 +775,8 @@ static int zones_zonefile_sync_ev(event_t *e)
journal_t *j = journal_retain(zd->ixfr_db);
int ret = zones_zonefile_sync(zone, j);
journal_release(j);
+
+ rcu_read_lock();
if (ret == KNOTD_EOK) {
log_zone_info("Applied differences of '%s' to zonefile.\n",
zd->conf->name);
@@ -732,13 +785,14 @@ static int zones_zonefile_sync_ev(event_t *e)
"to zonefile.\n",
zd->conf->name);
}
+ rcu_read_unlock();
/* Reschedule. */
- conf_read_lock();
+ rcu_read_lock();
evsched_schedule(e->parent, e, zd->conf->dbsync_timeout * 1000);
dbg_zones("zones: next IXFR database SYNC of '%s' in %d seconds\n",
zd->conf->name, zd->conf->dbsync_timeout);
- conf_read_unlock();
+ rcu_read_unlock();
return ret;
}
@@ -813,11 +867,12 @@ static int zones_set_acl(acl_t **acl, list* acl_list)
static int zones_load_zone(knot_zone_t **dst, const char *zone_name,
const char *source, const char *filename)
{
- if (dst == NULL) {
+ if (dst == NULL || zone_name == NULL || source == NULL) {
return KNOTD_EINVAL;
}
*dst = NULL;
+ /* Duplicate zone name. */
size_t zlen = strlen(zone_name);
char *zname = NULL;
if (zlen > 0) {
@@ -827,8 +882,13 @@ static int zones_load_zone(knot_zone_t **dst, const char *zone_name,
} else {
return KNOTD_EINVAL;
}
-
zname[zlen - 1] = '\0'; /* Trim last dot */
+ if (filename == NULL) {
+ log_server_error("No file name for zone '%s'.\n", zname);
+ free(zname);
+ return KNOTD_EINVAL;
+ }
+
/* Check if the compiled file still exists. */
struct stat st;
@@ -841,109 +901,90 @@ static int zones_load_zone(knot_zone_t **dst, const char *zone_name,
return KNOTD_EZONEINVAL;
}
- /* Check path */
- if (filename) {
- dbg_zones("zones: parsing zone database '%s'\n", filename);
- zloader_t *zl = 0;
- int ret = knot_zload_open(&zl, filename);
- switch(ret) {
- case KNOT_EOK:
- /* OK */
- break;
- case KNOT_EACCES:
- log_server_error("Failed to open compiled zone '%s' "
- "(Permission denied).\n", filename);
- free(zname);
- return KNOTD_EZONEINVAL;
- case KNOT_ENOENT:
- log_server_error("Couldn't find compiled zone. "
- "Please recompile '%s'.\n", zname);
- free(zname);
- return KNOTD_EZONEINVAL;
- case KNOT_ECRC:
- log_server_error("Compiled zone db CRC mismatch, "
- "db is corrupted or .crc file is "
- "deleted. Please recompile '%s'.\n",
- zname);
- free(zname);
- return KNOTD_EZONEINVAL;
- case KNOT_EMALF:
- log_server_error("Compiled db '%s' is too old. "
- "Please recompile '%s'.\n",
- filename, zname);
- free(zname);
- return KNOTD_EZONEINVAL;
- case KNOT_EFEWDATA:
- case KNOT_ERROR:
- case KNOT_ENOMEM:
- default:
- log_server_error("Failed to load compiled zone file "
- "'%s'.\n", filename);
- free(zname);
- return KNOTD_EZONEINVAL;
- }
-
- /* Check the source file */
- int src_changed = strcmp(source, zl->source) != 0;
- if (src_changed || knot_zload_needs_update(zl)) {
- log_server_warning("Database for zone '%s' is not "
- "up-to-date. Please recompile.\n",
- zname);
- }
-
- *dst = knot_zload_load(zl);
-
- /* Check loaded name. */
- const knot_dname_t *dname = knot_zone_name(*dst);
- knot_dname_t *dname_req = 0;
- dname_req = knot_dname_new_from_str(zone_name, zlen, 0);
- if (knot_dname_compare(dname, dname_req) != 0) {
- log_server_warning("Origin of the zone db file is "
- "different than '%s'\n",
- zone_name);
- knot_zone_deep_free(dst, 0);
-
- }
- knot_dname_free(&dname_req);
-
- /* CLEANUP */
-// knot_zone_contents_dump(zone->contents, 1);
-// int errs = knot_zone_contents_integrity_check(zone->contents);
-// fprintf(stderr, "INTEGRITY CHECK OF ZONE. ERRORS: %d\n", errs);
-
- if (*dst != NULL) {
- /* save the timestamp from the zone db file */
- struct stat s;
- if (stat(filename, &s) < 0) {
- dbg_zones("zones: failed to stat() zone db, "
- "something is seriously wrong\n");
- knot_zone_deep_free(dst, 0);
- } else {
- knot_zone_set_version(*dst, s.st_mtime);
- }
- }
-
+ /* Attempt to open compiled zone for loading. */
+ int ret = KNOTD_EOK;
+ zloader_t *zl = NULL;
+ dbg_zones("zones: parsing zone database '%s'\n", filename);
+ switch(knot_zload_open(&zl, filename)) {
+ case KNOT_EOK:
+ /* OK */
+ break;
+ case KNOT_EACCES:
+ log_server_error("Failed to open compiled zone '%s' "
+ "(Permission denied).\n", filename);
+ free(zname);
+ return KNOTD_EZONEINVAL;
+ case KNOT_ENOENT:
+ log_server_error("Couldn't find compiled zone. "
+ "Please recompile '%s'.\n", zname);
+ free(zname);
+ return KNOTD_EZONEINVAL;
+ case KNOT_ECRC:
+ log_server_error("Compiled zone db CRC mismatch, "
+ "db is corrupted or .crc file is "
+ "deleted. Please recompile '%s'.\n",
+ zname);
+ free(zname);
+ return KNOTD_EZONEINVAL;
+ case KNOT_EMALF:
+ log_server_error("Compiled db '%s' is too old. "
+ "Please recompile '%s'.\n",
+ filename, zname);
+ free(zname);
+ return KNOTD_EZONEINVAL;
+ case KNOT_EFEWDATA:
+ case KNOT_ERROR:
+ case KNOT_ENOMEM:
+ default:
+ log_server_error("Failed to load compiled zone file "
+ "'%s'.\n", filename);
+ free(zname);
+ return KNOTD_EZONEINVAL;
+ }
+
+ /* Check the source file */
+ assert(zl != NULL);
+ int src_changed = strcmp(source, zl->source) != 0;
+ if (src_changed || knot_zload_needs_update(zl)) {
+ log_server_warning("Database for zone '%s' is not "
+ "up-to-date. Please recompile.\n",
+ zname);
+ }
+
+ *dst = knot_zload_load(zl);
+ if (*dst == NULL) {
+ log_server_error("Failed to load db '%s' for zone '%s'.\n",
+ filename, zname);
knot_zload_close(zl);
-
- if (*dst == NULL) {
- log_server_error("Failed to load "
- "db '%s' for zone '%s'.\n",
- filename, zname);
- free(zname);
- return KNOTD_EZONEINVAL;
- }
- } else {
- /* db is null. */
- log_server_error("No file name for zone '%s'.\n", zname);
free(zname);
- return KNOTD_EINVAL;
+ return KNOTD_EZONEINVAL;
}
-
- /* CLEANUP */
-// knot_zone_dump(zone, 1);
+
+ /* Check if loaded origin matches. */
+ const knot_dname_t *dname = knot_zone_name(*dst);
+ knot_dname_t *dname_req = NULL;
+ dname_req = knot_dname_new_from_str(zone_name, zlen, 0);
+ if (knot_dname_compare(dname, dname_req) != 0) {
+ log_server_error("Origin of the zone db file is "
+ "different than '%s'\n",
+ zone_name);
+ knot_zone_deep_free(dst, 0);
+ ret = KNOTD_EZONEINVAL;
+ } else {
+ /* Save the timestamp from the zone db file. */
+ if (stat(filename, &st) < 0) {
+ dbg_zones("zones: failed to stat() zone db, "
+ "something is seriously wrong\n");
+ knot_zone_deep_free(dst, 0);
+ ret = KNOTD_EZONEINVAL;
+ } else {
+ knot_zone_set_version(*dst, st.st_mtime);
+ }
+ }
+ knot_dname_free(&dname_req);
+ knot_zload_close(zl);
free(zname);
-
- return KNOTD_EOK;
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1130,11 +1171,11 @@ static int zones_load_changesets(const knot_zone_t *zone,
dbg_zones_detail("Bad arguments: zd->ixfr_db=%p\n", zone->data);
return KNOTD_EINVAL;
}
-
- conf_read_lock();
+
+ rcu_read_lock();
dbg_xfr("xfr: loading changesets for zone '%s' from serial %u to %u\n",
zd->conf->name, from, to);
- conf_read_unlock();
+ rcu_read_unlock();
/* Retain journal for changeset loading. */
journal_t *j = journal_retain(zd->ixfr_db);
@@ -1167,7 +1208,7 @@ static int zones_load_changesets(const knot_zone_t *zone,
ret = knot_changesets_check_size(dst);
--dst->count;
if (ret != KNOT_EOK) {
- --dst->count;
+ //--dst->count;
dbg_xfr("xfr: failed to check changesets size: %s\n",
knot_strerror(ret));
journal_release(j);
@@ -1211,10 +1252,10 @@ static int zones_load_changesets(const knot_zone_t *zone,
/* Unpack binary data. */
int unpack_ret = zones_changesets_from_binary(dst);
- if (unpack_ret != KNOT_EOK) {
+ if (unpack_ret != KNOTD_EOK) {
dbg_xfr("xfr: failed to unpack changesets "
- "from binary, %s\n", knot_strerror(unpack_ret));
- return KNOTD_ERROR;
+ "from binary, %s\n", knotd_strerror(unpack_ret));
+ return unpack_ret;
}
/* Check for complete history. */
@@ -1288,7 +1329,7 @@ static int zones_journal_apply(knot_zone_t *zone)
&contents);
if (apply_ret != KNOT_EOK) {
log_server_error("Failed to apply changesets to"
- "'%s' - Apply failed: %s\n",
+ " '%s' - Apply failed: %s\n",
zd->conf->name,
knot_strerror(apply_ret));
ret = KNOTD_ERROR;
@@ -1297,27 +1338,27 @@ static int zones_journal_apply(knot_zone_t *zone)
xfrin_rollback_update(zone->contents,
&contents,
&chsets->changes);
- }
-
- /* Switch zone immediately. */
- rcu_read_unlock();
- apply_ret = xfrin_switch_zone(zone, contents,
- XFR_TYPE_IIN);
- rcu_read_lock();
- if (apply_ret == KNOT_EOK) {
- xfrin_cleanup_successful_update(
- &chsets->changes);
} else {
- log_server_error("Failed to apply changesets to"
- " '%s' - Switch failed: %s\n",
- zd->conf->name,
- knot_strerror(apply_ret));
- ret = KNOTD_ERROR;
-
- // Cleanup old and new contents
- xfrin_rollback_update(zone->contents,
- &contents,
- &chsets->changes);
+ /* Switch zone immediately. */
+ rcu_read_unlock();
+ apply_ret = xfrin_switch_zone(zone, contents,
+ XFR_TYPE_IIN);
+ rcu_read_lock();
+ if (apply_ret == KNOT_EOK) {
+ xfrin_cleanup_successful_update(
+ &chsets->changes);
+ } else {
+ log_server_error("Failed to apply "
+ "changesets to '%s' - Switch failed: "
+ "%s\n", zd->conf->name,
+ knot_strerror(apply_ret));
+ ret = KNOTD_ERROR;
+
+ // Cleanup old and new contents
+ xfrin_rollback_update(zone->contents,
+ &contents,
+ &chsets->changes);
+ }
}
}
} else {
@@ -1350,9 +1391,9 @@ static int zones_journal_apply(knot_zone_t *zone)
* \retval KNOTD_ERROR on unspecified error.
*/
static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
- knot_nameserver_t *ns, const knot_zonedb_t *db_old)
+ knot_nameserver_t *ns)
{
- if (z == NULL || dst == NULL || ns == NULL || db_old == NULL) {
+ if (z == NULL || dst == NULL || ns == NULL) {
return KNOTD_EINVAL;
}
@@ -1367,17 +1408,19 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
}
/* Try to find the zone in the current zone db. */
- knot_zone_t *zone = knot_zonedb_find_zone(db_old, dname);
+ rcu_read_lock();
+ knot_zone_t *zone = knot_zonedb_find_zone(ns->zone_db, dname);
+ rcu_read_unlock();
/* Attempt to bootstrap if db or source does not exist. */
int zone_changed = 0;
struct stat s = {};
int stat_ret = stat(z->file, &s);
- if (zone != NULL && stat_ret == 0) {
+ if (zone != NULL) {
/* if found, check timestamp of the file against the
* loaded zone
*/
- if (knot_zone_version(zone) < s.st_mtime) {
+ if (stat_ret == 0 && knot_zone_version(zone) < s.st_mtime) {
zone_changed = 1;
}
} else {
@@ -1385,6 +1428,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
}
/* Reload zone file. */
+ int is_bootstrapped = 0;
int ret = KNOTD_ERROR;
if (zone_changed) {
/* Zone file not exists and has master set. */
@@ -1398,9 +1442,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
zone = knot_zone_new_empty(owner);
if (zone != NULL) {
ret = KNOTD_EOK;
- log_server_info("Will attempt to bootstrap zone"
- " %s from AXFR master.\n",
- z->name);
+ is_bootstrapped = 1;
} else {
dbg_zones("zones: failed to create "
"stub zone '%s'.\n", z->name);
@@ -1410,9 +1452,21 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
dbg_zones_verb("zones: loading zone '%s' from '%s'\n",
z->name, z->db);
ret = zones_load_zone(&zone, z->name, z->file, z->db);
+ const knot_node_t *apex = NULL;
+ const knot_rrset_t *soa = NULL;
if (ret == KNOTD_EOK) {
- log_server_info("Loaded zone '%s'\n",
- z->name);
+ apex = knot_zone_contents_apex(
+ knot_zone_contents(zone));
+ soa = knot_node_rrset(apex,
+ KNOT_RRTYPE_SOA);
+ int64_t sn = 0;
+ if (apex && soa) {
+ sn = knot_rdata_soa_serial(
+ knot_rrset_rdata(soa));
+ if (sn < 0) sn = 0;
+ }
+ log_server_info("Loaded zone '%s' serial %u\n",
+ z->name, (uint32_t)sn);
}
}
@@ -1425,7 +1479,6 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
/* Initialize zone-related data. */
zonedata_init(z, zone);
*dst = zone;
-
}
} else {
dbg_zones_verb("zones: found '%s' in old database, "
@@ -1442,9 +1495,20 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
if (zone != NULL) {
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
assert(zd != NULL);
+
+ /* Log bootstrapped zone. */
+ if (is_bootstrapped) {
+ log_server_info("Will attempt to bootstrap zone"
+ " %s from AXFR master in %us.\n",
+ z->name,
+ zd->xfr_in.bootstrap_retry / 1000);
+ }
/* Update refs. */
- zd->conf = z;
+ if (zd->conf != z) {
+ conf_free_zone(zd->conf);
+ zd->conf = z;
+ }
/* Update ACLs. */
dbg_zones("Updating zone ACLs.\n");
@@ -1486,7 +1550,13 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
}
/* Apply changesets from journal. */
- zones_journal_apply(zone);
+ int ar = zones_journal_apply(zone);
+ if (ar != KNOTD_EOK && ar != KNOTD_ERANGE && ar != KNOTD_ENOENT) {
+ log_server_warning("Failed to apply changesets "
+ "for zone '%s': %s\n",
+ z->name, knotd_strerror(ar));
+ }
+
/* Update events scheduled for zone. */
evsched_t *sch = ((server_t *)knot_ns_get_data(ns))->sched;
@@ -1526,10 +1596,32 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
rcu_read_unlock();
}
+
+ /* Calculate differences. */
+ rcu_read_lock();
+ knot_zone_t *z_old = knot_zonedb_find_zone(ns->zone_db,
+ dname);
+ /* Ensure both new and old have zone contents. */
+ knot_zone_contents_t *zc = knot_zone_get_contents(zone);
+ knot_zone_contents_t *zc_old = knot_zone_get_contents(z_old);
+ if (z->build_diffs && zc != NULL && zc_old != NULL && zone_changed) {
+ int bd = zones_create_and_save_changesets(z_old, zone);
+ if (bd == KNOTD_ENODIFF) {
+ log_zone_warning("Zone file for '%s' changed, "
+ "but serial didn't - "
+ "won't create changesets.\n",
+ z->name);
+ } else if (bd != KNOTD_EOK) {
+ log_zone_warning("Failed to calculate differences"
+ " from the zone file update: "
+ "%s\n", knotd_strerror(bd));
+ }
+ }
+ rcu_read_unlock();
}
/* CLEANUP */
-// knot_zone_contents_dump(knot_zone_get_contents(zone), 1);
+// knot_zone_contents_dump(knot_zone_get_contents(zone), 1);
/* Directly discard zone. */
knot_dname_free(&dname);
@@ -1539,8 +1631,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
/*! \brief Structure for multithreaded zone loading. */
struct zonewalk_t {
knot_nameserver_t *ns;
- const knot_zonedb_t *db_old;
- knot_zonedb_t *db_new;
+ knot_zonedb_t *db_new;
pthread_mutex_t lock;
int inserted;
unsigned qhead;
@@ -1581,8 +1672,7 @@ static int zonewalker(dthread_t *thread)
continue;
}
- int ret = zones_insert_zone(zw->q[i], zones + inserted, zw->ns,
- zw->db_old);
+ int ret = zones_insert_zone(zw->q[i], zones + inserted, zw->ns);
if (ret == KNOTD_EOK) {
++inserted;
}
@@ -1592,11 +1682,15 @@ static int zonewalker(dthread_t *thread)
pthread_mutex_lock(&zw->lock);
zw->inserted += inserted;
for (int i = 0; i < inserted; ++i) {
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(zones[i]);
if (knot_zonedb_add_zone(zw->db_new, zones[i]) != KNOT_EOK) {
- zonedata_t *zd = (zonedata_t *)knot_zone_data(zones[i]);
log_server_error("Failed to insert zone '%s' "
"into database.\n", zd->conf->name);
knot_zone_deep_free(zones + i, 0);
+ } else {
+ /* Unlink zone config from conf(),
+ * transferring ownership to zonedata. */
+ rem_node(&zd->conf->n);
}
}
pthread_mutex_unlock(&zw->lock);
@@ -1613,14 +1707,12 @@ static int zonewalker(dthread_t *thread)
*
* \param ns Name server instance.
* \param zone_conf Zone configuration.
- * \param db_old Old zone database.
* \param db_new New zone database.
*
* \return Number of inserted zones.
*/
static int zones_insert_zones(knot_nameserver_t *ns,
const list *zone_conf,
- const knot_zonedb_t *db_old,
knot_zonedb_t *db_new)
{
int inserted = 0;
@@ -1636,7 +1728,6 @@ static int zones_insert_zones(knot_nameserver_t *ns,
if (zw != NULL) {
memset(zw, 0, zwlen);
zw->ns = ns;
- zw->db_old = db_old;
zw->db_new = db_new;
zw->inserted = 0;
if (pthread_mutex_init(&zw->lock, NULL) < 0) {
@@ -1713,6 +1804,18 @@ dbg_zones_exec(
"from database.\n", name);
free(name);
);
+ /* Invalidate ACLs - since we would need to copy each
+ * remote data and keep ownership, I think it's no harm
+ * to drop all ACLs for the discarded zone.
+ * refs #1976 */
+ zonedata_t *zd = (zonedata_t*)knot_zone_data(old_zone);
+ conf_zone_t *zconf = zd->conf;
+ WALK_LIST_FREE(zconf->acl.xfr_in);
+ WALK_LIST_FREE(zconf->acl.xfr_out);
+ WALK_LIST_FREE(zconf->acl.notify_in);
+ WALK_LIST_FREE(zconf->acl.notify_out);
+
+ /* Remove from zone db. */
knot_zone_t * rm = knot_zonedb_remove_zone(db_old,
knot_zone_name(old_zone));
assert(rm == old_zone);
@@ -1758,8 +1861,10 @@ static int zones_verify_tsig_query(const knot_packet_t *query,
/*
* 2) Find the particular key used by the TSIG.
+ * Check not only name, but also the algorithm.
*/
- if (key && kname && knot_dname_compare(key->name, kname) == 0) {
+ if (key && kname && knot_dname_compare(key->name, kname) == 0
+ && key->algorithm == alg) {
dbg_zones_verb("Found claimed TSIG key for comparison\n");
} else {
*rcode = KNOT_RCODE_NOTAUTH;
@@ -1848,18 +1953,19 @@ static int zones_check_tsig_query(const knot_zone_t *zone,
assert(rcode != NULL);
assert(tsig_key_zone != NULL);
- const knot_rrset_t *tsig = NULL;
+ const knot_rrset_t *tsig = knot_packet_tsig(query);
- if (knot_packet_additional_rrset_count(query) > 0) {
- /*! \todo warning */
- tsig = knot_packet_additional_rrset(query,
- knot_packet_additional_rrset_count(query) - 1);
- if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
- dbg_zones_verb("found TSIG in normal query\n");
- } else {
- tsig = NULL; /* Invalidate if not TSIG RRTYPE. */
- }
- }
+ // not required, TSIG is already found
+// if (knot_packet_additional_rrset_count(query) > 0) {
+// /*! \todo warning */
+// tsig = knot_packet_additional_rrset(query,
+// knot_packet_additional_rrset_count(query) - 1);
+// if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
+// dbg_zones_verb("found TSIG in normal query\n");
+// } else {
+// tsig = NULL; /* Invalidate if not TSIG RRTYPE. */
+// }
+// }
if (tsig == NULL) {
// no TSIG, this is completely valid
@@ -1894,7 +2000,7 @@ static int zones_check_tsig_query(const knot_zone_t *zone,
}
// save TSIG RR to query structure
- knot_packet_set_tsig(query, tsig);
+// knot_packet_set_tsig(query, tsig);
return ret;
}
@@ -1913,34 +2019,39 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns,
/* Lock RCU to ensure none will deallocate any data under our hands. */
rcu_read_lock();
-
+
/* Grab a pointer to the old database */
- *db_old = ns->zone_db;
- if (*db_old == NULL) {
+ if (ns->zone_db == NULL) {
+ rcu_read_unlock();
log_server_error("Missing zone database in nameserver structure"
".\n");
- rcu_read_unlock();
return KNOTD_ERROR;
}
+ rcu_read_unlock();
/* Create new zone DB */
knot_zonedb_t *db_new = knot_zonedb_new();
if (db_new == NULL) {
- rcu_read_unlock();
return KNOTD_ERROR;
}
log_server_info("Loading %d compiled zones...\n", conf->zones_count);
/* Insert all required zones to the new zone DB. */
- int inserted = zones_insert_zones(ns, &conf->zones, *db_old, db_new);
-
+ /*! \warning RCU must not be locked as some contents switching will
+ be required. */
+ int inserted = zones_insert_zones(ns, &conf->zones, db_new);
+
log_server_info("Loaded %d out of %d zones.\n", inserted,
conf->zones_count);
if (inserted != conf->zones_count) {
log_server_warning("Not all the zones were loaded.\n");
}
+
+ /* Lock RCU to ensure none will deallocate any data under our hands. */
+ rcu_read_lock();
+ *db_old = ns->zone_db;
dbg_zones_detail("zones: old db in nameserver: %p, old db stored: %p, "
"new db: %p\n", ns->zone_db, *db_old, db_new);
@@ -2020,19 +2131,29 @@ int zones_zonefile_sync(knot_zone_t *zone, journal_t *journal)
if (zd->zonefile_serial != serial_to) {
/* Save zone to zonefile. */
- conf_read_lock();
dbg_zones("zones: syncing '%s' differences to '%s' "
"(SOA serial %u)\n",
zd->conf->name, zd->conf->file, serial_to);
ret = zones_dump_zone_text(contents, zd->conf->file);
if (ret != KNOTD_EOK) {
- dbg_zones("zones: failed to sync '%s' to '%s'\n",
- zd->conf->name, zd->conf->file);
+ log_zone_warning("Failed to apply differences "
+ "'%s' to '%s'\n",
+ zd->conf->name, zd->conf->file);
pthread_mutex_unlock(&zd->lock);
rcu_read_unlock();
return ret;
}
- conf_read_unlock();
+
+ /* Save zone to binary db file. */
+ ret = zones_dump_zone_binary(contents, zd->conf->db, zd->conf->file);
+ if (ret != KNOTD_EOK) {
+ log_zone_warning("Failed to apply differences "
+ "'%s' to '%s'\n",
+ zd->conf->name, zd->conf->db);
+ pthread_mutex_unlock(&zd->lock);
+ rcu_read_unlock();
+ return KNOTD_ERROR;
+ }
/* Update journal entries. */
dbg_zones_verb("zones: unmarking all dirty nodes "
@@ -2144,15 +2265,16 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
? *rsize : 0);
// check for TSIG in the query
- if (knot_packet_additional_rrset_count(query) > 0) {
- /*! \todo warning */
- const knot_rrset_t *tsig = knot_packet_additional_rrset(query,
- knot_packet_additional_rrset_count(query) - 1);
- if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
- dbg_zones_verb("found TSIG in normal query\n");
- knot_packet_set_tsig(query, tsig);
- }
- }
+ // not required, TSIG is already found if it is there
+// if (knot_packet_additional_rrset_count(query) > 0) {
+// /*! \todo warning */
+// const knot_rrset_t *tsig = knot_packet_additional_rrset(query,
+// knot_packet_additional_rrset_count(query) - 1);
+// if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
+// dbg_zones_verb("found TSIG in normal query\n");
+// knot_packet_set_tsig(query, tsig);
+// }
+// }
knot_rcode_t rcode = 0;
@@ -2170,7 +2292,10 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
break;
}
- if (zone == NULL && knot_packet_tsig(query) == NULL) {
+ if (rcode == KNOT_RCODE_NOERROR
+ && ((zone == NULL && knot_packet_tsig(query) == NULL)
+ || (knot_packet_qclass(query) != KNOT_CLASS_IN
+ && knot_packet_qclass(query) != KNOT_CLASS_ANY))) {
/*! \todo If there is TSIG, this should be probably handled
* as a key error.
*/
@@ -2181,10 +2306,9 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
dbg_zones_verb("Failed preparing response structure: %s.\n",
knot_strerror(rcode));
if (resp == NULL) {
- knot_ns_error_response(nameserver,
- knot_packet_id(query),
- &query->header.flags1,
- rcode, resp_wire, rsize);
+ knot_ns_error_response_from_query(nameserver, query,
+ rcode, resp_wire,
+ rsize);
rcu_read_unlock();
return KNOT_EOK;
}
@@ -2198,7 +2322,8 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
assert(rcode == KNOT_RCODE_NOERROR);
uint16_t tsig_rcode = 0;
knot_key_t *tsig_key_zone = NULL;
- uint64_t tsig_prev_time_signed = 0; /*! \todo Verify, as it was uninitialized! */
+ uint64_t tsig_prev_time_signed = 0;
+ /*! \todo Verify, as it was uninitialized! */
size_t answer_size = *rsize;
int ret = KNOT_EOK;
@@ -2206,6 +2331,7 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
if (zone == NULL) {
assert(knot_packet_tsig(query) != NULL);
// treat as BADKEY error
+ /*! \todo Is this OK?? */
rcode = KNOT_RCODE_NOTAUTH;
tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
ret = KNOT_TSIG_EBADKEY;
@@ -2228,8 +2354,20 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
tsig_wire_maxsize(tsig_key_zone);
knot_packet_set_tsig_size(resp, tsig_max_size);
}
- ret = knot_ns_answer_normal(nameserver, zone, resp,
- resp_wire, &answer_size);
+
+ // handle IXFR queries
+ if (knot_packet_qtype(query) == KNOT_RRTYPE_IXFR) {
+ assert(transport == NS_TRANSPORT_UDP);
+ ret = knot_ns_answer_ixfr_udp(nameserver, zone,
+ resp, resp_wire,
+ &answer_size);
+ } else {
+ ret = knot_ns_answer_normal(nameserver, zone,
+ resp, resp_wire,
+ &answer_size,
+ transport ==
+ NS_TRANSPORT_UDP);
+ }
dbg_zones_detail("rsize = %zu\n", *rsize);
dbg_zones_detail("answer_size = %zu\n", answer_size);
@@ -2334,6 +2472,9 @@ int zones_normal_query_answer(knot_nameserver_t *nameserver,
tsig_rdata_mac_length(tsig),
digest, &digest_size, tsig_key_zone,
tsig_rcode, tsig_prev_time_signed);
+
+ // no need to keep the digest
+ free(digest);
*rsize = answer_size;
} else {
@@ -2385,6 +2526,7 @@ int zones_process_response(knot_nameserver_t *nameserver,
}
/* Find matching zone and ID. */
+ rcu_read_lock();
const knot_dname_t *zone_name = knot_packet_qname(packet);
/*! \todo Change the access to the zone db. */
knot_zone_t *zone = knot_zonedb_find_zone(
@@ -2392,7 +2534,6 @@ int zones_process_response(knot_nameserver_t *nameserver,
zone_name);
/* Get zone contents. */
- rcu_read_lock();
const knot_zone_contents_t *contents =
knot_zone_contents(zone);
@@ -2423,17 +2564,10 @@ int zones_process_response(knot_nameserver_t *nameserver,
evsched_t *sched =
((server_t *)knot_ns_get_data(nameserver))->sched;
if (ret == 0) {
- char r_addr[SOCKADDR_STRLEN];
- int r_port = sockaddr_portnum(from);
- sockaddr_tostr(from, r_addr, sizeof(r_addr));
- log_zone_info("SOA query of '%s' to '%s@%d': Answered, no "
- "transfer needed.\n",
- zd->conf->name, r_addr, r_port);
-
/* Reinstall timers. */
zones_timers_update(zone, zd->conf, sched);
rcu_read_unlock();
- return KNOTD_EOK;
+ return KNOTD_EUPTODATE;
}
assert(ret > 0);
@@ -2471,9 +2605,13 @@ int zones_process_response(knot_nameserver_t *nameserver,
/* Unlock zone contents. */
rcu_read_unlock();
- /* Enqueue XFR request. */
- return xfr_request(((server_t *)knot_ns_get_data(
- nameserver))->xfr_h, &xfr_req);
+ /* Retain pointer to zone for processing. */
+ knot_zone_retain(xfr_req.zone);
+ ret = xfr_request(((server_t *)knot_ns_get_data(
+ nameserver))->xfr_h, &xfr_req);
+ if (ret != KNOTD_EOK) {
+ knot_zone_release(xfr_req.zone); /* Discard */
+ }
}
return KNOTD_EOK;
@@ -2492,45 +2630,6 @@ knot_ns_xfr_type_t zones_transfer_to_use(zonedata_t *data)
/*----------------------------------------------------------------------------*/
-static int zones_find_zone_for_xfr(const knot_zone_contents_t *zone,
- const char **zonefile, const char **zonedb)
-{
- /* find the zone file name and zone db file name for the zone */
- conf_t *cnf = conf();
- node *n = NULL;
- WALK_LIST(n, cnf->zones) {
- conf_zone_t *zone_conf = (conf_zone_t *)n;
- knot_dname_t *zone_name = knot_dname_new_from_str(
- zone_conf->name, strlen(zone_conf->name), NULL);
- if (zone_name == NULL) {
- return KNOTD_ENOMEM;
- }
-
- int r = knot_dname_compare(zone_name, knot_node_owner(
- knot_zone_contents_apex(zone)));
-
- /* Directly discard dname, won't be needed. */
- knot_dname_free(&zone_name);
-
- if (r == 0) {
- /* found the right zone */
- *zonefile = zone_conf->file;
- *zonedb = zone_conf->db;
- return KNOTD_EOK;
- }
- }
-
- char *name = knot_dname_to_str(knot_node_owner(
- knot_zone_contents_apex(zone)));
- dbg_zones("zones: no zone found for the zone received by transfer "
- "(%s).\n", name);
- free(name);
-
- return KNOTD_ENOENT;
-}
-
-/*----------------------------------------------------------------------------*/
-
static int zones_open_free_filename(const char *old_name, char **new_name)
{
/* find zone name not present on the disk */
@@ -2571,7 +2670,6 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname)
FILE *f = fdopen(fd, "w");
if (f == NULL) {
log_zone_warning("Failed to open file descriptor for text zone.\n");
- close(fd);
unlink(new_fname);
free(new_fname);
return KNOTD_ERROR;
@@ -2580,11 +2678,14 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname)
if (zone_dump_text(zone, f) != KNOTD_EOK) {
log_zone_warning("Failed to save the transferred zone to '%s'.\n",
new_fname);
- close(fd);
+ fclose(f);
unlink(new_fname);
free(new_fname);
return KNOTD_ERROR;
}
+
+ /* Set zone file rights to 0640. */
+ fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
/* Swap temporary zonefile and new zonefile. */
fclose(f);
@@ -2596,8 +2697,7 @@ static int zones_dump_zone_text(knot_zone_contents_t *zone, const char *fname)
free(new_fname);
return KNOTD_ERROR;
}
-
-
+
free(new_fname);
return KNOTD_EOK;
}
@@ -2619,18 +2719,23 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone,
return KNOTD_ERROR;
}
- crc_t crc_value;
+ crc_t crc_value = 0;
if (knot_zdump_dump(zone, fd, zonefile, &crc_value) != KNOT_EOK) {
close(fd);
unlink(new_zonedb);
free(new_zonedb);
return KNOTD_ERROR;
}
+
+ /* Set compiled zone rights to 0640. */
+ fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+
+ /* Close compiled zone. */
+ close(fd);
/* Delete old CRC file. */
char *zonedb_crc = knot_zdump_crc_file(zonedb);
if (zonedb_crc == NULL) {
- close(fd);
unlink(new_zonedb);
free(new_zonedb);
return KNOTD_ENOMEM;
@@ -2643,7 +2748,6 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone,
dbg_zdump("Failed to create CRC file path from %s.\n",
new_zonedb);
free(zonedb_crc);
- close(fd);
unlink(new_zonedb);
free(new_zonedb);
return KNOTD_ENOMEM;
@@ -2654,13 +2758,18 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone,
if (f_crc == NULL) {
dbg_zdump("Cannot open CRC file %s!\n",
zonedb_crc);
+ free(zonedb_crc);
unlink(new_zonedb);
+ free(new_zonedb);
return KNOTD_ERROR;
} else {
fprintf(f_crc, "%lu\n",
(unsigned long)crc_value);
fclose(f_crc);
}
+
+ /* Set CRC file rights to 0640. */
+ chmod(new_zonedb_crc, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
/* Swap CRC files. */
int ret = KNOTD_EOK;
@@ -2689,7 +2798,6 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone,
free(new_zonedb_crc);
free(zonedb_crc);
- close(fd);
free(new_zonedb);
@@ -2700,33 +2808,48 @@ static int zones_dump_zone_binary(knot_zone_contents_t *zone,
int zones_save_zone(const knot_ns_xfr_t *xfr)
{
- if (xfr == NULL || xfr->new_contents == NULL) {
+ if (xfr == NULL || xfr->new_contents == NULL || xfr->zone == NULL) {
return KNOTD_EINVAL;
}
- knot_zone_contents_t *zone = xfr->new_contents;
-
- const char *zonefile = NULL;
- const char *zonedb = NULL;
+ rcu_read_lock();
- int ret = zones_find_zone_for_xfr(zone, &zonefile, &zonedb);
- if (ret != KNOTD_EOK) {
- return ret;
+ zonedata_t *zd = (zonedata_t *)knot_zone_data(xfr->zone);
+ knot_zone_contents_t *new_zone = xfr->new_contents;
+
+ const char *zonefile = zd->conf->file;
+ const char *zonedb = zd->conf->db;
+
+ /* Check if the new zone apex dname matches zone name. */
+ knot_dname_t *cur_name = knot_dname_new_from_str(zd->conf->name,
+ strlen(zd->conf->name),
+ NULL);
+ const knot_dname_t *new_name = NULL;
+ new_name = knot_node_owner(knot_zone_contents_apex(new_zone));
+ int r = knot_dname_compare(cur_name, new_name);
+ knot_dname_free(&cur_name);
+ if (r != 0) {
+ rcu_read_unlock();
+ return KNOTD_EINVAL;
}
assert(zonefile != NULL && zonedb != NULL);
/* dump the zone into text zone file */
- ret = zones_dump_zone_text(zone, zonefile);
+ int ret = zones_dump_zone_text(new_zone, zonefile);
if (ret != KNOTD_EOK) {
+ rcu_read_unlock();
return KNOTD_ERROR;
}
/* dump the zone into binary db file */
- ret = zones_dump_zone_binary(zone, zonedb, zonefile);
+ ret = zones_dump_zone_binary(new_zone, zonedb, zonefile);
if (ret != KNOTD_EOK) {
+ rcu_read_unlock();
return KNOTD_ERROR;
}
+ rcu_read_unlock();
+
return KNOTD_EOK;
}
@@ -2970,10 +3093,10 @@ static int zones_store_changeset(const knot_changeset_t *chs, journal_t *j,
/* Reschedule sync timer. */
if (tmr) {
/* Fetch sync timeout. */
- conf_read_lock();
+ rcu_read_lock();
int timeout = zd->conf->dbsync_timeout;
timeout *= 1000; /* Convert to ms. */
- conf_read_unlock();
+ rcu_read_unlock();
/* Reschedule. */
dbg_xfr_verb("xfr: resuming SYNC "
@@ -3145,6 +3268,79 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
/*----------------------------------------------------------------------------*/
+int zones_create_and_save_changesets(const knot_zone_t *old_zone,
+ const knot_zone_t *new_zone)
+{
+ if (old_zone == NULL || old_zone->contents == NULL
+ || new_zone == NULL || new_zone->contents == NULL) {
+ dbg_zones("zones: create_changesets: "
+ "NULL arguments.\n");
+ return KNOTD_EINVAL;
+ }
+
+ knot_ns_xfr_t xfr;
+ memset(&xfr, 0, sizeof(xfr));
+ xfr.zone = (knot_zone_t *)old_zone;
+ knot_changesets_t *changesets;
+ int ret = knot_zone_diff_create_changesets(old_zone->contents,
+ new_zone->contents,
+ &changesets);
+ if (ret != KNOT_EOK) {
+ if (ret == KNOT_ERANGE) {
+ dbg_zones_detail("zones: create_changesets: "
+ "New serial was lower than the old "
+ "one.\n");
+ knot_free_changesets(&changesets);
+ return KNOTD_ERANGE;
+ } else if (ret == KNOT_ENODIFF) {
+ dbg_zones_detail("zones: create_changesets: "
+ "New serial was the same as the old "
+ "one.\n");
+ knot_free_changesets(&changesets);
+ return KNOTD_ENODIFF;
+ } else {
+ dbg_zones("zones: create_changesets: "
+ "Could not create changesets. Reason: %s\n",
+ knot_strerror(ret));
+ knot_free_changesets(&changesets);
+ return KNOTD_ERROR;
+ }
+ }
+
+ xfr.data = changesets;
+ journal_t *journal = zones_store_changesets_begin(&xfr);
+ if (journal == NULL) {
+ dbg_zones("zones: create_changesets: "
+ "Could not start journal operation.\n");
+ return KNOTD_ERROR;
+ }
+
+ ret = zones_store_changesets(&xfr);
+ if (ret != KNOTD_EOK) {
+ zones_store_changesets_rollback(journal);
+ dbg_zones("zones: create_changesets: "
+ "Could not store in the journal. Reason: %s.\n",
+ knotd_strerror(ret));
+
+ return ret;
+ }
+
+ ret = zones_store_changesets_commit(journal);
+ if (ret != KNOTD_EOK) {
+ dbg_zones("zones: create_changesets: "
+ "Could not commit to journal. Reason: %s.\n",
+ knotd_strerror(ret));
+
+ return ret;
+ }
+
+ knot_free_changesets(&changesets);
+
+ return KNOTD_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
{
if (!sch || !zone) {
@@ -3180,7 +3376,7 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
pthread_mutex_unlock(&zd->lock);
/* Check XFR/IN master server. */
- conf_read_lock();
+ rcu_read_lock();
if (zd->xfr_in.master.ptr) {
/* Schedule REFRESH timer. */
@@ -3198,13 +3394,12 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
/* Do not issue NOTIFY queries if stub. */
if (!knot_zone_contents(zone)) {
- conf_read_unlock();
+ rcu_read_unlock();
return KNOTD_EOK;
}
/* Schedule NOTIFY to slaves. */
conf_remote_t *r = 0;
- conf_read_lock();
WALK_LIST(r, cfzone->acl.notify_out) {
/* Fetch remote. */
@@ -3257,7 +3452,7 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
tmr_s, cfg_if->address, cfg_if->port);
}
- conf_read_unlock();
+ rcu_read_unlock();
return KNOTD_EOK;
}
@@ -3275,7 +3470,10 @@ int zones_cancel_notify(zonedata_t *zd, notify_ev_t *ev)
event_t *tmr = ev->timer;
ev->timer = 0;
pthread_mutex_unlock(&zd->lock);
- evsched_cancel(tmr->parent, tmr);
+ if (evsched_cancel(tmr->parent, tmr) == 0) {
+ dbg_notify("notify: NOTIFY event %p designated for cancellation "
+ "not found\n", tmr);
+ }
/* Re-lock and find again (if not deleted). */
pthread_mutex_lock(&zd->lock);
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index bb95b93..65e5a61 100644..100755
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -59,7 +59,7 @@ typedef struct zonedata_t
/*! \brief Zone data lock for exclusive access. */
pthread_mutex_t lock;
-
+
/*! \brief Access control lists. */
acl_t *xfr_out; /*!< ACL for xfr-out.*/
acl_t *notify_in; /*!< ACL for notify-in.*/
@@ -272,6 +272,23 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
uint32_t serial_to);
/*!
+ * \brief Creates changesets from zones difference.
+ *
+ * Also saves changesets to journal, which is taken from old zone.
+ *
+ * \param old_zone Old zone, previously served by server.
+ * \param new_zone New zone, to be served by server, after creating changesets.
+ *
+ * \retval KNOTD_EOK on success.
+ * \retval KNOTD_EINVAL on invalid arguments.
+ * \retval KNOTD_ERANGE when new serial is lower than the old one.
+ * \retval KNOTD_ENODIFF when new zone's serial are equal.
+ * \retval KNOTD_ERROR when there was error creating changesets.
+ */
+int zones_create_and_save_changesets(const knot_zone_t *old_zone,
+ const knot_zone_t *new_zone);
+
+/*!
* \brief Update zone timers.
*
* REFRESH/RETRY/EXPIRE timers are updated according to SOA.
diff --git a/src/knot/stat/gatherer.c b/src/knot/stat/gatherer.c
index 5b8eab6..5b8eab6 100644..100755
--- a/src/knot/stat/gatherer.c
+++ b/src/knot/stat/gatherer.c
diff --git a/src/knot/stat/gatherer.h b/src/knot/stat/gatherer.h
index 62b3939..62b3939 100644..100755
--- a/src/knot/stat/gatherer.h
+++ b/src/knot/stat/gatherer.h
diff --git a/src/knot/stat/stat-common.h b/src/knot/stat/stat-common.h
index 032e32b..032e32b 100644..100755
--- a/src/knot/stat/stat-common.h
+++ b/src/knot/stat/stat-common.h
diff --git a/src/knot/stat/stat.c b/src/knot/stat/stat.c
index a473085..a473085 100644..100755
--- a/src/knot/stat/stat.c
+++ b/src/knot/stat/stat.c
diff --git a/src/knot/stat/stat.h b/src/knot/stat/stat.h
index 0cf1454..0cf1454 100644..100755
--- a/src/knot/stat/stat.h
+++ b/src/knot/stat/stat.h
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index fc20c29..20043df 100644..100755
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -16,8 +16,7 @@ static char *error_messages[(-ZC_ERR_ALLOC) + 1] = {
[-ZC_ERR_ALLOC] = "Memory allocation error!\n",
[-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!\n",
- [-ZC_ERR_MISSING_NS_DEL_POINT] = "NS record missing in zone apex or in "
- "delegation point!\n",
+ [-ZC_ERR_MISSING_NS_DEL_POINT] = "NS record missing in zone apex!\n",
[-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] =
"RRSIG: Type covered rdata field is wrong!\n",
@@ -169,7 +168,7 @@ int err_handler_handle_error(err_handler_t *handler,
return KNOT_EBADARG;
}
- /*!< \todo this is so wrong! This should not even return anything. */
+ /*!< \todo #1886 this is so wrong! Should not even return anything. */
if (error == ZC_ERR_ALLOC || error == 0) {
return KNOT_EBADARG;
}
@@ -262,9 +261,11 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
const knot_node_t *next_node = NULL;
uint i = 0;
-
- assert(tmp_rdata);
-
+
+ if (tmp_rdata == NULL) {
+ return KNOT_EOK;
+ }
+
const knot_dname_t *next_dname =
knot_rdata_cname_name(tmp_rdata);
/* (cname_name == dname_target) */
@@ -330,6 +331,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
} else if ((knot_dname_is_fqdn(next_dname_copy) &&
knot_dname_label_count(next_dname_copy) == 0)) {
knot_dname_free(&next_dname_copy);
+ knot_dname_free(&tmp_chopped);
/* Root domain, end of search. */
break;
}
@@ -367,7 +369,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
next_dname);
}
-/*!< \todo this might replace some of the code above. */
+/*!< \todo #1887 this might replace some of the code above. */
// /* Still NULL, try wildcards. */
// if (next_node == NULL && knot_dname_is_wildcard(next_dname)) {
// /* We can only use the wildcard so many times. */
@@ -390,7 +392,6 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
knot_dname_t *chopped_next =
knot_dname_left_chop(next_dname);
if (chopped_next == NULL) {
- /*!< \todo check. */
return KNOT_ERROR;
}
while (next_node == NULL && chopped_next != NULL) {
@@ -422,7 +423,7 @@ static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
if (next_node != NULL) {
next_rrset = knot_node_rrset(next_node,
rrset->type);
- if (next_rrset != NULL) {
+ if (next_rrset != NULL && next_rrset->rdata != NULL) {
next_dname =
knot_rdata_cname_name(next_rrset->rdata);
} else {
@@ -477,7 +478,6 @@ uint16_t type_covered_from_rdata(const knot_rdata_t *rdata)
static int check_dnskey_rdata(const knot_rdata_t *rdata)
{
/* check that Zone key bit it set - position 7 in net order */
- /*! \todo FIXME: endian? I swear I've fixed this already, it was 7 i guesss*/
uint16_t mask = 1 << 8; //0b0000000100000000;
uint16_t flags =
@@ -570,7 +570,6 @@ static int dnskey_to_wire(const knot_rdata_t *rdata, uint8_t **wire,
/* copy the wire octet by octet */
- /* TODO check if we really have that many items */
if (rdata->count < 4) {
free(*wire);
*wire = NULL;
@@ -754,28 +753,34 @@ static int check_rrsig_in_rrset(const knot_rrset_t *rrset,
return ZC_ERR_RRSIG_TTL;
}
- /* Check whether all rrsets have their rrsigs */
const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset);
const knot_rdata_t *tmp_rrsig_rdata = knot_rrset_rdata(rrsigs);
-
- assert(tmp_rdata);
- assert(tmp_rrsig_rdata);
+
+ assert(tmp_rrsig_rdata != NULL);
+ if (tmp_rdata == NULL) {
+ /* Only RRSIG, valid, but we can't check anything. */
+ return KNOT_EOK;
+ }
+
int ret = 0;
- char all_signed = tmp_rdata && tmp_rrsig_rdata;
do {
if ((ret = check_rrsig_rdata(tmp_rrsig_rdata,
rrset,
dnskey_rrset)) != 0) {
+ /*!< \todo This should go to handler. */
return ret;
}
-
- all_signed = tmp_rdata && tmp_rrsig_rdata;
- } while (((tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata))
- != NULL) &&
- ((tmp_rrsig_rdata =
- knot_rrset_rdata_next(rrsigs, tmp_rrsig_rdata))
- != NULL));
-
+ tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata);
+ tmp_rrsig_rdata = knot_rrset_rdata_next(rrsigs,
+ tmp_rrsig_rdata);
+ } while (tmp_rdata != NULL && tmp_rrsig_rdata != NULL);
+
+ /*!< \todo JK 03-08-2012 #1972
+ * This check is not very informative, its output
+ * does not contain any info about which RRSet is not completely signed.
+ * A rewrite is needed.
+ */
+ char all_signed = tmp_rdata == NULL && tmp_rrsig_rdata == NULL;
if (!all_signed) {
return ZC_ERR_RRSIG_NOT_ALL;
}
@@ -992,10 +997,6 @@ static int check_nsec3_node_in_zone(knot_zone_contents_t *zone, knot_node_t *nod
/* Directly discard. */
knot_dname_free(&next_dname);
- /*!< \todo These comments are not accurate anymore. */
- /* This is probably not sufficient, but again, it is covered in
- * zone load time */
-
uint count;
uint16_t *array = NULL;
if (rdata_nsec_to_type_array(
@@ -1104,8 +1105,9 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
}
}
- if (knot_rrset_rdata(cname_rrset)->next !=
- knot_rrset_rdata(cname_rrset)) {
+ if (knot_rrset_rdata(cname_rrset) &&
+ knot_rrset_rdata(cname_rrset)->next !=
+ knot_rrset_rdata(cname_rrset)) {
*fatal_error = 1;
err_handler_handle_error(handler, node,
ZC_ERR_CNAME_MULTIPLE);
@@ -1169,7 +1171,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
return KNOT_EOK;
}
- /*!< \todo Good Lord, move this to ist own function. */
+ /*!< \todo #1887 Good Lord, move this to ist own function. */
/* check for glue records at zone cuts and in apex. */
if (knot_node_is_deleg_point(node) || knot_zone_contents_apex(zone) ==
@@ -1184,10 +1186,12 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
//FIXME this should be an error as well ! (i guess)
knot_dname_t *ns_dname =
+ knot_dname_deep_copy(
knot_rdata_get_item(knot_rrset_rdata
- (ns_rrset), 0)->dname;
-
- assert(ns_dname);
+ (ns_rrset), 0)->dname);
+ if (ns_dname == NULL) {
+ return KNOT_ENOMEM;
+ }
const knot_node_t *glue_node =
knot_zone_contents_find_node(zone, ns_dname);
@@ -1199,6 +1203,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
knot_dname_t *wildcard =
knot_dname_new_from_str("*", 1, NULL);
if (wildcard == NULL) {
+ knot_dname_free(&ns_dname);
return KNOT_ENOMEM;
}
@@ -1206,6 +1211,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
if (knot_dname_cat(wildcard,
ns_dname) == NULL) {
+ knot_dname_free(&ns_dname);
knot_dname_free(&wildcard);
return KNOT_ENOMEM;
}
@@ -1236,6 +1242,7 @@ static int semantic_checks_plain(knot_zone_contents_t *zone,
}
}
}
+ knot_dname_free(&ns_dname);
}
return KNOT_EOK;
}
@@ -1512,7 +1519,7 @@ void log_cyclic_errors_in_zone(err_handler_t *handler,
free(next_dname_decoded);
- /*! \todo Free result and dname! */
+ /*! \todo #1887 Free result and dname! */
if (knot_dname_cat(next_dname,
knot_node_owner(knot_zone_contents_apex(zone))) ==
NULL) {
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
index 17b774f..2f6dad7 100644..100755
--- a/src/knot/zone/semantic-check.h
+++ b/src/knot/zone/semantic-check.h
@@ -20,7 +20,7 @@
*
* \brief DNS zone semantic checks.
*
- * \addtogroup dnslib
+ * \addtogroup zoneparser
* @{
*/
@@ -200,3 +200,5 @@ int zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks,
knot_node_t **last_node);
#endif // _KNOT_SEMANTIC_CHECK_H_
+
+/*! @} */
diff --git a/src/knot/zone/zone-dump-text.c b/src/knot/zone/zone-dump-text.c
index bc606d3..8f428a5 100644..100755
--- a/src/knot/zone/zone-dump-text.c
+++ b/src/knot/zone/zone-dump-text.c
@@ -305,12 +305,12 @@ static inline uint16_t rdata_item_size(knot_rdata_item_t item)
return item.raw_data[0];
}
-char *rdata_dname_to_string(knot_rdata_item_t item)
+static char *rdata_dname_to_string(knot_rdata_item_t item)
{
return knot_dname_to_str(item.dname);
}
-char *rdata_binary_dname_to_string(knot_rdata_item_t item)
+static char *rdata_binary_dname_to_string(knot_rdata_item_t item)
{
if (item.raw_data == NULL) {
return NULL;
@@ -334,7 +334,7 @@ char *rdata_binary_dname_to_string(knot_rdata_item_t item)
return str;
}
-char *rdata_dns_name_to_string(knot_rdata_item_t item)
+static char *rdata_dns_name_to_string(knot_rdata_item_t item)
{
return knot_dname_to_str(item.dname);
}
@@ -342,8 +342,7 @@ char *rdata_dns_name_to_string(knot_rdata_item_t item)
static char *rdata_txt_data_to_string(const uint8_t *data)
{
uint8_t length = data[0];
- size_t i;
-
+ size_t i = 0;
if (length == 0) {
return NULL;
}
@@ -360,38 +359,41 @@ static char *rdata_txt_data_to_string(const uint8_t *data)
}
memset(ret, 0, current_length);
- strncat(ret, "\"", 3);
+ strncat(ret, "\"", 2);
for (i = 1; i <= length; i++) {
char ch = (char) data[i];
if (isprint((int)ch)) {
if (ch == '"' || ch == '\\') {
- strncat(ret, "\"", 3);
+ strncat(ret, "\"", 2);
}
- /* for the love of god, how to this better,
- but w/o obscure self-made functions */
char tmp_str[2];
tmp_str[0] = ch;
- tmp_str[1] = 0;
+ tmp_str[1] = '\0';
strncat(ret, tmp_str, 2);
} else {
- strncat(ret, "\\", 3);
+ strncat(ret, "\\", 2);
char tmp_str[2];
tmp_str[0] = ch - '0';
- tmp_str[1] = 0;
-
+ tmp_str[1] = '\0';
strncat(ret, tmp_str, 2);
}
}
- strncat(ret, "\"", 3);
+ strncat(ret, "\"", 2);
return ret;
}
-char *rdata_text_to_string(knot_rdata_item_t item)
+static char *rdata_text_to_string(knot_rdata_item_t item)
{
uint16_t size = item.raw_data[0];
- char *ret = malloc(sizeof(char) * size * 2 + 1) ;
+ /*
+ * Times two because they can all be one char long
+ * and then it would be as much chars as spaces (and one final space).
+ */
+ size_t txt_size = size * 2 + 1;
+ /* + 1 ... space for (hypothetical) last \0. */
+ char *ret = malloc(txt_size + 1);
if (ret == NULL) {
ERR_ALLOC_FAILED;
return NULL;
@@ -399,6 +401,7 @@ char *rdata_text_to_string(knot_rdata_item_t item)
memset(ret, 0, sizeof(char) * size);
const uint8_t *data = (uint8_t *)(item.raw_data + 1);
size_t read_count = 0;
+ size_t tmp_str_current_length = 0; // Will be used with strncat.
while (read_count < size) {
assert(read_count <= size);
char *txt = rdata_txt_data_to_string(data + read_count);
@@ -406,20 +409,30 @@ char *rdata_text_to_string(knot_rdata_item_t item)
free(ret);
return NULL;
}
+ /*
+ * We can trust this strlen, as
+ * it is created in internal function.
+ */
read_count += strlen(txt) - 1;
/* Create delimiter. */
char del[2];
del[0] = ' ';
del[1] = '\0';
- strncat(ret, txt, strlen(txt));
- strncat(ret, del, 2);
+
+ /* We can only write to the remainder of string. */
+ strncat(ret, txt, txt_size - tmp_str_current_length);
+ /* Increase length of tmp string. */
+ tmp_str_current_length += strlen(txt);
+ strncat(ret, del, txt_size - tmp_str_current_length);
+ /* Increase length of tmp string by 1 ... space. */
+ tmp_str_current_length += + 1;
free(txt);
}
return ret;
}
-char *rdata_byte_to_string(knot_rdata_item_t item)
+static char *rdata_byte_to_string(knot_rdata_item_t item)
{
assert(item.raw_data[0] == 1);
uint8_t data = *((uint8_t *)(item.raw_data + 1));
@@ -428,7 +441,7 @@ char *rdata_byte_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_short_to_string(knot_rdata_item_t item)
+static char *rdata_short_to_string(knot_rdata_item_t item)
{
uint16_t data = knot_wire_read_u16(rdata_item_data(item));
char *ret = malloc(sizeof(char) * U16_MAX_STR_LEN);
@@ -438,7 +451,7 @@ char *rdata_short_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_long_to_string(knot_rdata_item_t item)
+static char *rdata_long_to_string(knot_rdata_item_t item)
{
uint32_t data = knot_wire_read_u32(rdata_item_data(item));
char *ret = malloc(sizeof(char) * U32_MAX_STR_LEN);
@@ -447,7 +460,7 @@ char *rdata_long_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_a_to_string(knot_rdata_item_t item)
+static char *rdata_a_to_string(knot_rdata_item_t item)
{
/* 200 seems like a little too much */
char *ret = malloc(sizeof(char) * 200);
@@ -458,7 +471,7 @@ char *rdata_a_to_string(knot_rdata_item_t item)
}
}
-char *rdata_aaaa_to_string(knot_rdata_item_t item)
+static char *rdata_aaaa_to_string(knot_rdata_item_t item)
{
char *ret = malloc(sizeof(char) * 200);
if (inet_ntop(AF_INET6, rdata_item_data(item), ret, 200)) {
@@ -468,7 +481,7 @@ char *rdata_aaaa_to_string(knot_rdata_item_t item)
}
}
-char *rdata_rrtype_to_string(knot_rdata_item_t item)
+static char *rdata_rrtype_to_string(knot_rdata_item_t item)
{
uint16_t type = knot_wire_read_u16(rdata_item_data(item));
const char *tmp = knot_rrtype_to_string(type);
@@ -477,7 +490,7 @@ char *rdata_rrtype_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_algorithm_to_string(knot_rdata_item_t item)
+static char *rdata_algorithm_to_string(knot_rdata_item_t item)
{
uint8_t id = *rdata_item_data(item);
char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN);
@@ -492,7 +505,7 @@ char *rdata_algorithm_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_certificate_type_to_string(knot_rdata_item_t item)
+static char *rdata_certificate_type_to_string(knot_rdata_item_t item)
{
uint16_t id = knot_wire_read_u16(rdata_item_data(item));
char *ret = malloc(sizeof(char) * MAX_RR_TYPE_LEN);
@@ -507,7 +520,7 @@ char *rdata_certificate_type_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_period_to_string(knot_rdata_item_t item)
+static char *rdata_period_to_string(knot_rdata_item_t item)
{
/* uint32 but read 16 XXX */
uint32_t period = knot_wire_read_u32(rdata_item_data(item));
@@ -516,7 +529,7 @@ char *rdata_period_to_string(knot_rdata_item_t item)
return ret;
}
-char *rdata_time_to_string(knot_rdata_item_t item)
+static char *rdata_time_to_string(knot_rdata_item_t item)
{
time_t time = (time_t) knot_wire_read_u32(rdata_item_data(item));
struct tm tm_conv;
@@ -532,7 +545,7 @@ char *rdata_time_to_string(knot_rdata_item_t item)
}
}
-char *rdata_base32_to_string(knot_rdata_item_t item)
+static char *rdata_base32_to_string(knot_rdata_item_t item)
{
int length;
size_t size = rdata_item_size(item);
@@ -555,7 +568,8 @@ char *rdata_base32_to_string(knot_rdata_item_t item)
}
}
-char *rdata_base64_to_string(knot_rdata_item_t item)
+/*!< \todo Replace with function from .../common after release. */
+static char *rdata_base64_to_string(knot_rdata_item_t item)
{
int length;
size_t size = rdata_item_size(item);
@@ -570,7 +584,7 @@ char *rdata_base64_to_string(knot_rdata_item_t item)
}
}
-char *hex_to_string(const uint8_t *data, size_t size)
+static char *knot_hex_to_string(const uint8_t *data, size_t size)
{
static const char hexdigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -593,7 +607,7 @@ char *hex_to_string(const uint8_t *data, size_t size)
char *rdata_hex_to_string(knot_rdata_item_t item)
{
- return hex_to_string(rdata_item_data(item), rdata_item_size(item));
+ return knot_hex_to_string(rdata_item_data(item), rdata_item_size(item));
}
char *rdata_hexlen_to_string(knot_rdata_item_t item)
@@ -605,8 +619,8 @@ char *rdata_hexlen_to_string(knot_rdata_item_t item)
ret[1] = '\0';
return ret;
} else {
- return hex_to_string(rdata_item_data(item) + 1,
- rdata_item_size(item) - 1);
+ return knot_hex_to_string(rdata_item_data(item) + 1,
+ rdata_item_size(item) - 1);
}
}
@@ -622,8 +636,8 @@ char *rdata_nsap_to_string(knot_rdata_item_t item)
/* String is already terminated. */
memcpy(ret, "0x", strlen("0x"));
- char *converted = hex_to_string(rdata_item_data(item),
- rdata_item_size(item));
+ char *converted = knot_hex_to_string(rdata_item_data(item),
+ rdata_item_size(item));
if (converted == NULL) {
return NULL;
}
@@ -707,9 +721,7 @@ char *rdata_services_to_string(knot_rdata_item_t item)
if (proto) {
int i;
- /*!< \todo #1863 see below, but we can trust getprotobynumber... */
strncpy(ret, proto->p_name, strlen(proto->p_name));
-
strncat(ret, " ", 2);
for (i = 0; i < bitmap_size * 8; ++i) {
@@ -718,12 +730,6 @@ char *rdata_services_to_string(knot_rdata_item_t item)
getservbyport((int)htons(i),
proto->p_name);
if (service) {
- /*!< \todo #1863
- * using strncat with strlen
- * does not make a whole lot of sense.
- * At least it will crash wil
- * Use max length of service name!
- */
strncat(ret, service->s_name,
strlen(service->s_name));
strncat(ret, " ", 2);
@@ -738,37 +744,6 @@ char *rdata_services_to_string(knot_rdata_item_t item)
}
return ret;
-
- /*
- int result = 0;
- uint8_t protocol_number = buffer_read_u8(&packet);
- ssize_t bitmap_size = buffer_remaining(&packet);
- uint8_t *bitmap = buffer_current(&packet);
- struct protoent *proto = getprotobynumber(protocol_number);
-
-
- if (proto) {
- int i;
-
- strcpy(ret, proto->p_name);
-
- for (i = 0; i < bitmap_size * 8; ++i) {
- if (get_bit(bitmap, i)) {
- struct servent *service =
- getservbyport((int)htons(i),
- proto->p_name);
- if (service) {
- buffer_printf(output, " %s",
- service->s_name);
- } else {
- buffer_printf(output, " %d", i);
- }
- }
- }
- result = 1;
- }
- return ret;
- */
}
char *rdata_ipsecgateway_to_string(knot_rdata_item_t item,
@@ -830,17 +805,15 @@ char *rdata_nxt_to_string(knot_rdata_item_t item)
char *rdata_nsec_to_string(knot_rdata_item_t item)
{
- /* CLEANUP */
-// int insert_space = 0;
-
char *ret = malloc(sizeof(char) * MAX_NSEC_BIT_STR_LEN);
-
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
memset(ret, 0, MAX_NSEC_BIT_STR_LEN);
-
uint8_t *data = rdata_item_data(item);
int increment = 0;
-
for (int i = 0; i < rdata_item_size(item); i += increment) {
increment = 0;
uint8_t window = data[i];
@@ -870,33 +843,6 @@ char *rdata_nsec_to_string(knot_rdata_item_t item)
}
return ret;
-
- /* CLEANUP */
-/* while (buffer_available(&packet, 2)) {
- uint8_t window = buffer_read_u8(&packet);
- uint8_t bitmap_size = buffer_read_u8(&packet);
- uint8_t *bitmap = buffer_current(&packet);
- int i;
-
- if (!buffer_available(&packet, bitmap_size)) {
- buffer_set_position(output, saved_position);
- return 0;
- }
-
- for (i = 0; i < bitmap_size * 8; ++i) {
- if (get_bit(bitmap, i)) {
- buffer_printf(output,
- "%s%s",
- insert_space ? " " : "",
- rrtype_to_string(
- window * 256 + i));
- insert_space = 1;
- }
- }
- buffer_skip(&packet, bitmap_size);
- }
-
- return 1; */
}
char *rdata_unknown_to_string(knot_rdata_item_t item)
@@ -912,7 +858,7 @@ char *rdata_unknown_to_string(knot_rdata_item_t item)
snprintf(ret + strlen("\\# "),
strlen("\\# ") + U16_MAX_STR_LEN + 1, "%lu ",
(unsigned long) size);
- char *converted = hex_to_string(rdata_item_data(item), size);
+ char *converted = knot_hex_to_string(rdata_item_data(item), size);
strncat(ret, converted, size * 2 + 1);
free(converted);
return ret;
@@ -959,14 +905,13 @@ char *rdata_item_to_string(knot_rdata_zoneformat_t type,
return item_to_string_table[type](item);
}
-/* CLEANUP */
-/*void knot_zone_tree_apply_inorder(knot_zone_t *zone,
- void (*function)(knot_node_t *node, void *data),
- void *data); */
-
int rdata_dump_text(const knot_rdata_t *rdata, uint16_t type, FILE *f,
const knot_rrset_t *rrset)
{
+ if (rdata == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
knot_rrtype_descriptor_t *desc =
knot_rrtype_descriptor_by_type(type);
char *item_str = NULL;
@@ -1040,19 +985,23 @@ int rrsig_set_dump_text(knot_rrset_t *rrsig, FILE *f)
int rrset_dump_text(const knot_rrset_t *rrset, FILE *f)
{
- dump_rrset_header(rrset, f);
- knot_rdata_t *tmp = rrset->rdata;
+ if (rrset->rdata != NULL) { // No sense in dumping empty RR
+ dump_rrset_header(rrset, f);
- while (tmp->next != rrset->rdata) {
- int ret = rdata_dump_text(tmp, rrset->type, f, rrset);
- if (ret != KNOTD_EOK) {
- return ret;
+ knot_rdata_t *tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata) {
+ int ret = rdata_dump_text(tmp, rrset->type, f, rrset);
+ if (ret != KNOTD_EOK) {
+ return ret;
+ }
+ dump_rrset_header(rrset, f);
+ tmp = tmp->next;
}
- dump_rrset_header(rrset, f);
- tmp = tmp->next;
+
+ rdata_dump_text(tmp, rrset->type, f, rrset);
}
- rdata_dump_text(tmp, rrset->type, f, rrset);
knot_rrset_t *rrsig_set = rrset->rrsigs;
if (rrsig_set != NULL) {
rrsig_set_dump_text(rrsig_set, f);
diff --git a/src/knot/zone/zone-dump-text.h b/src/knot/zone/zone-dump-text.h
index 9f9c8dd..c55f712 100644..100755
--- a/src/knot/zone/zone-dump-text.h
+++ b/src/knot/zone/zone-dump-text.h
@@ -20,7 +20,7 @@
*
* \brief Functions for dumping zone to text file.
*
- * \addtogroup dnslib
+ * \addtogroup zone-load-dump
* @{
*/
diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c
index aaf0165..708cd2b 100644..100755
--- a/src/knot/zone/zone-dump.c
+++ b/src/knot/zone/zone-dump.c
@@ -360,6 +360,11 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata,
rdata->items[i].dname) {
wildcard = rdata->items[i].dname->node->owner;
}
+
+ dbg_zdump_detail("zdump: dump_rdata: "
+ "Writing dname: %s.\n",
+ knot_dname_to_str(
+ rdata->items[i].dname));
if (use_ids) {
/* Write ID. */
@@ -392,6 +397,8 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata,
/*! \todo Does not have to be so complex.
* Create extra variable. */
if (rdata->items[i].dname->node != NULL && !wildcard) {
+ dbg_zdump("zdump: dump_rdata: "
+ "This dname is in the zone.\n");
if (!write_wrapper((uint8_t *)"\1",
sizeof(uint8_t), 1, fd,
stream, max_size,
@@ -401,6 +408,8 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata,
return KNOT_ERROR;
}
} else {
+ dbg_zdump("zdump: dump_rdata: "
+ "This dname is not in the zone.\n");
if (!write_wrapper((uint8_t *)"\0",
sizeof(uint8_t),
1, fd,
@@ -423,6 +432,9 @@ static int knot_rdata_dump_binary(knot_rdata_t *rdata,
}
uint32_t wildcard_id = wildcard->id;
+ dbg_zdump("zdump: dump_rdata: "
+ "This dname is covered by wc (%s).\n",
+ knot_dname_to_str(wildcard));
if (!write_wrapper(&wildcard_id,
sizeof(wildcard_id), 1,
fd, stream, max_size,
@@ -567,7 +579,11 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd,
"Dumping RRSet \\w owner: %s.\n",
name);
free(name);
- );
+ );
+
+ if (!use_ids) {
+ assert(rrset->rrsigs == NULL);
+ }
if (!use_ids) {
/*!< \todo IDs in changeset do no good. Change loading too. */
@@ -602,10 +618,14 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd,
/* Calculate rrset rdata count. */
knot_rdata_t *tmp_rdata = rrset->rdata;
- while(tmp_rdata->next != rrset->rdata) {
+ while(tmp_rdata && (tmp_rdata->next != rrset->rdata)) {
tmp_rdata = tmp_rdata->next;
rdata_count++;
}
+
+ if (rrset->rdata == NULL) {
+ rdata_count = 0;
+ }
if (!write_wrapper(&rdata_count, sizeof(rdata_count), 1, fd,
stream, max_size, written_bytes, crc)) {
@@ -619,29 +639,35 @@ static int knot_rrset_dump_binary(const knot_rrset_t *rrset, int fd,
}
dbg_zdump_verb("zdump: rrset_dump_binary: Static data dumped.\n");
+
+ if (rdata_count != 0) {
+
+ tmp_rdata = rrset->rdata;
- tmp_rdata = rrset->rdata;
-
- while (tmp_rdata->next != rrset->rdata) {
+ while (tmp_rdata->next != rrset->rdata) {
+ int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type,
+ fd, use_ids,
+ stream, max_size,
+ written_bytes, crc);
+ if (ret != KNOT_EOK) {
+ dbg_zdump("zdump: rrset_to_binary: Could not "
+ "dump "
+ "rdata. Reason: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+ tmp_rdata = tmp_rdata->next;
+ }
+
int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type,
fd, use_ids,
- stream, max_size,
- written_bytes, crc);
+ stream,
+ max_size, written_bytes, crc);
if (ret != KNOT_EOK) {
dbg_zdump("zdump: rrset_to_binary: Could not dump "
"rdata. Reason: %s.\n", knot_strerror(ret));
return ret;
}
- tmp_rdata = tmp_rdata->next;
- }
-
- int ret = knot_rdata_dump_binary(tmp_rdata, rrset->type, fd, use_ids,
- stream,
- max_size, written_bytes, crc);
- if (ret != KNOT_EOK) {
- dbg_zdump("zdump: rrset_to_binary: Could not dump "
- "rdata. Reason: %s.\n", knot_strerror(ret));
- return ret;
}
dbg_zdump_verb("zdump: rrset_dump_binary: Rdata dumped.\n");
@@ -894,6 +920,7 @@ int knot_zdump_binary(knot_zone_contents_t *zone, int fd,
int do_checks, const char *sfilename,
crc_t *crc)
{
+
if (fd < 0 || sfilename == NULL) {
dbg_zdump("zdump: Bad arguments.\n");
return KNOT_EBADARG;
diff --git a/src/knot/zone/zone-dump.h b/src/knot/zone/zone-dump.h
index fbebae9..02d0298 100644..100755
--- a/src/knot/zone/zone-dump.h
+++ b/src/knot/zone/zone-dump.h
@@ -20,7 +20,7 @@
*
* \brief Functions for dumping zone to binary file.
*
- * \addtogroup dnslib
+ * \addtogroup zone-load-dump
* @{
*/
@@ -38,7 +38,7 @@ enum {
};
/*! \brief Magic identifier: { "knot", maj_ver, min_ver, revision } */
-#define MAGIC_BYTES {'k', 'n', 'o', 't', '1', '0', '4'}
+#define MAGIC_BYTES {'k', 'n', 'o', 't', '1', '1', '0'}
/*!
* \brief Dumps given zone to binary file.
diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c
index 3a7134e..fedd38b 100644..100755
--- a/src/knot/zone/zone-load.c
+++ b/src/knot/zone/zone-load.c
@@ -62,39 +62,57 @@ static int timet_cmp(time_t x, time_t y)
* \retval 0 if failed.
*/
static inline int fread_safe_from_file(void *dst,
- size_t size, size_t n, FILE *fp)
+ size_t size, size_t n, void *source)
{
+ if (dst == NULL || source == NULL) {
+ dbg_zload("zload: fread_safe_from_file: NULL arguments.\n");
+ return 0;
+ }
+ FILE *fp = (FILE *)source;
int rc = fread(dst, size, n, fp);
if (rc != n) {
- fprintf(stderr, "fread: invalid read %d (expected %zu)\n", rc,
- n);
+ dbg_zload("zload: fread_safe_from_file: "
+ "invalid read %d (exp. %zu)\n",
+ rc, n);
}
return rc == n;
}
-static uint8_t *knot_zload_stream = NULL;
-static size_t knot_zload_stream_remaining = 0;
-static size_t knot_zload_stream_size = 0;
+struct load_stream {
+ uint8_t *stream;
+ size_t stream_remaining;
+ size_t stream_size;
+};
+
+typedef struct load_stream load_stream_t;
static inline int read_from_stream(void *dst,
- size_t size, size_t n, FILE *fp)
+ size_t size, size_t n, void *source)
{
- if (knot_zload_stream_remaining < (size * n)) {
+ if (dst == NULL || source == NULL) {
+ dbg_zload("zload: read_from_stream: NULL arguments.\n");
+ return 0;
+ }
+
+ /* Extract information from source data. */
+ load_stream_t *data = (load_stream_t *)source;
+
+
+ if (data->stream_remaining < (size * n)) {
+ dbg_zload("zload: read_from_stream: Buffer depleted.\n");
return 0;
}
memcpy(dst,
- knot_zload_stream +
- (knot_zload_stream_size - knot_zload_stream_remaining),
+ data->stream +
+ (data->stream_size - data->stream_remaining),
size * n);
- knot_zload_stream_remaining -= size * n;
+ data->stream_remaining -= size * n;
return 1;
}
-static int (*fread_wrapper)(void *dst, size_t size, size_t n, FILE *fp);
-
/*! \note Contents of dump file:
* MAGIC(knotxx) NUMBER_OF_NORMAL_NODES NUMBER_OF_NSEC3_NODES
* [normal_nodes] [nsec3_nodes]
@@ -136,23 +154,31 @@ static void load_rdata_purge(knot_rdata_t *rdata,
knot_dname_retain(items[i].dname);
break;
default:
+ /*!< \todo This would leak wire data! */
break;
}
}
/* Copy items to rdata and free the temporary rdata. */
knot_rdata_set_items(rdata, items, count);
- knot_rdata_deep_free(&rdata, type, 1);
+ knot_rdata_deep_free(&rdata, type, 0);
free(items);
}
-static knot_dname_t *read_dname_with_id(FILE *f)
+static knot_dname_t *read_dname_with_id(FILE *f, int use_ids)
{
if (f == NULL) {
dbg_zload("zload: read_dname_id: NULL file.\n");
}
knot_dname_t *ret = knot_dname_new();
CHECK_ALLOC_LOG(ret, NULL);
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ if (use_ids) {
+ fread_wrapper = fread_safe_from_file;
+ } else {
+ fread_wrapper = read_from_stream;
+ }
/* Read ID. */
uint32_t dname_id = 0;
@@ -173,9 +199,12 @@ static knot_dname_t *read_dname_with_id(FILE *f)
return NULL;
}
ret->size = dname_size;
- dbg_zload("loaded: dname length: %u\n", ret->size);
-
- assert(ret->size <= DNAME_MAX_WIRE_LENGTH);
+ dbg_zload_detail("loaded: dname length: %u\n", ret->size);
+ if (ret->size > DNAME_MAX_WIRE_LENGTH) {
+ dbg_zload("zload: read_dname_id: Name too long.\n");
+ knot_dname_release(ret);
+ return NULL;
+ }
/* Read wireformat of dname. */
ret->name = malloc(sizeof(uint8_t) * ret->size);
@@ -238,6 +267,18 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
knot_dname_t **id_array,
int use_ids)
{
+ if (f == NULL) {
+ dbg_zload("zload: load_rdata: NULL arguments.\n");
+ return NULL;
+ }
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ if (use_ids) {
+ fread_wrapper = fread_safe_from_file;
+ } else {
+ fread_wrapper = read_from_stream;
+ }
+
knot_rdata_t *rdata = knot_rdata_new();
if (rdata == NULL) {
dbg_zload("zload: load_rdata: Cannot create new rdata.\n");
@@ -262,12 +303,14 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
malloc(sizeof(knot_rdata_item_t) * rdata_count);
if (items == NULL) {
ERR_ALLOC_FAILED;
- free(items);
+ free(rdata);
return NULL;
}
if (rdata_count > desc->length) {
dbg_zload("zload: load_rdata: Read wrong count of RDATA.\n");
+ free(items);
+ free(rdata);
return NULL;
}
@@ -285,6 +328,7 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
/*!< \todo #1686
* Refactor these variables, some might be too big.
*/
+
uint32_t dname_id = 0;
uint8_t has_wildcard = 0;
@@ -304,8 +348,13 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
knot_dname_retain(id_array[dname_id]);
items[i].dname = id_array[dname_id];
} else {
- items[i].dname = read_dname_with_id(f);
+ items[i].dname = read_dname_with_id(f, use_ids);
}
+
+ dbg_zload_detail("zload: load_rdata: "
+ "Loading dname: %s.\n",
+ knot_dname_to_str(items[i].dname));
+
if(!fread_wrapper(&in_the_zone, sizeof(in_the_zone),
1, f)) {
@@ -322,6 +371,27 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
"Cannot read wildcard bit.\n");
return NULL;
}
+
+ dbg_zload_detail("zload: load_rdata: Has wildcard: "
+ "%d\n", has_wildcard);
+
+ if (use_ids && !in_the_zone) {
+ dbg_zload_detail("zload: load_rdata: "
+ "Freeing node owned by: %s.\n",
+ knot_dname_to_str(items[i].dname));
+ /* Destroy the node */
+ assert(!in_the_zone);
+ if (items[i].dname->node != NULL &&
+ /*
+ * This check is here to prevent freeing
+ * of previously set wildcard node.
+ */
+ (items[i].dname->node->owner ==
+ items[i].dname)) {
+ knot_node_free(&items[i].dname->node);
+ assert(items[i].dname->node == NULL);
+ }
+ }
if (use_ids && has_wildcard) {
if(!fread_wrapper(&dname_id, sizeof(dname_id),
@@ -332,16 +402,14 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
"Cannot read wc ID.\n");
return NULL;
}
+ dbg_zload_detail("zload: load_rdata: "
+ "Wildcard: %s\n",
+ knot_dname_to_str(
+ id_array[dname_id]));
items[i].dname->node =
id_array[dname_id]->node;
- } else if (use_ids && !in_the_zone) {
- /* destroy the node */
- if (id_array[dname_id]->node != NULL) {
- knot_node_free(&id_array[dname_id]->
- node, 0);
- }
- /* Also sets node to NULL! */
}
+
assert(items[i].dname);
} else {
if (!fread_wrapper(&raw_data_length,
@@ -379,10 +447,12 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
}
/* Each item has refcount already incremented for saving in rdata. */
- if (knot_rdata_set_items(rdata, items, rdata_count) != 0) {
- fprintf(stderr, "zload: read_rdata: Could not set items "
- "when loading rdata.\n");
- knot_rdata_deep_free(&rdata, type, 0);
+ int ret = knot_rdata_set_items(rdata, items, rdata_count);
+ if (ret != KNOT_EOK) {
+ dbg_zload("zload: read_rdata: Could not set items "
+ "when loading rdata. Reason: %\n.",
+ knot_strerror(ret));
+ load_rdata_purge(rdata, items, desc->length, desc, type);
return NULL;
}
@@ -403,7 +473,7 @@ static knot_rdata_t *knot_load_rdata(uint16_t type, FILE *f,
*
* \return pointer to created and read RRSIG on success, NULL otherwise.
*/
-static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array,
+static knot_rrset_t *knot_load_rrsig(void *f, knot_dname_t **id_array,
int use_ids)
{
if (f == NULL || id_array == NULL) {
@@ -411,6 +481,13 @@ static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array,
return NULL;
}
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ if (use_ids) {
+ fread_wrapper = fread_safe_from_file;
+ } else {
+ fread_wrapper = read_from_stream;
+ }
+
knot_rrset_t *rrsig = NULL;
uint16_t rrset_type = 0;
@@ -487,9 +564,21 @@ static knot_rrset_t *knot_load_rrsig(FILE *f, knot_dname_t **id_array,
*
* \return pointer to created and read RRSet on success, NULL otherwise.
*/
-static knot_rrset_t *knot_load_rrset(FILE *f, knot_dname_t **id_array,
+static knot_rrset_t *knot_load_rrset(void *f, knot_dname_t **id_array,
int use_ids)
{
+ if (f == NULL) {
+ dbg_zload("zload: load_rrset: NULL arguments.\n");
+ return NULL;
+ }
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ if (use_ids) {
+ fread_wrapper = fread_safe_from_file;
+ } else {
+ fread_wrapper = read_from_stream;
+ }
+
knot_rrset_t *rrset = NULL;
uint16_t rrset_type = 0;
@@ -504,7 +593,7 @@ static knot_rrset_t *knot_load_rrset(FILE *f, knot_dname_t **id_array,
if (!use_ids) {
dbg_zload_detail("zload: load_rrset: "
"Loading owner of new RRSet from wire.\n");
- owner = read_dname_with_id(f);
+ owner = read_dname_with_id(f, use_ids);
if (owner == NULL) {
dbg_zload("zload: load_rrset: Cannot load owner.\n");
return NULL;
@@ -587,6 +676,7 @@ dbg_zload_exec_detail(
return NULL;
}
} else {
+ dbg_zload("zload: load_rrset: Cannot load rdata.\n");
knot_rrset_deep_free(&rrset, 0, 1, 1);
return NULL;
}
@@ -628,6 +718,10 @@ static knot_node_t *knot_load_node(FILE *f, knot_dname_t **id_array)
dbg_zload("zload: load_node: Wrong parameters.\n");
return NULL;
}
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ fread_wrapper = fread_safe_from_file;
+
uint8_t flags = 0;
knot_node_t *node = NULL;
uint32_t parent_id = 0;
@@ -661,6 +755,10 @@ static knot_node_t *knot_load_node(FILE *f, knot_dname_t **id_array)
return NULL;
}
knot_dname_t *owner = id_array[dname_id];
+ if (owner == NULL) {
+ dbg_zload("zload: load_node: Wrong dname ID, cannot load.\n");
+ return NULL;
+ }
dbg_zload_detail("zload: load_node: Node owner id: %d.\n", dname_id);
dbg_zload_exec_detail(
@@ -701,7 +799,7 @@ dbg_zload_exec_detail(
for (int i = 0; i < rrset_count; i++) {
if ((tmp_rrset = knot_load_rrset(f, id_array, 1)) == NULL) {
- knot_node_free(&node, 0);
+ knot_node_free(&node);
/*!< \todo #1686
* Refactor freeing, might not be enough.
*/
@@ -770,6 +868,9 @@ static void find_and_set_wildcard_child(knot_zone_contents_t *zone,
static int knot_check_magic(FILE *f, const uint8_t* MAGIC, uint MAGIC_LENGTH)
{
uint8_t tmp_magic[MAGIC_LENGTH];
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ fread_wrapper = fread_safe_from_file;
if (!fread_wrapper(&tmp_magic, sizeof(uint8_t), MAGIC_LENGTH, f)) {
return 0;
@@ -791,6 +892,9 @@ static unsigned long calculate_crc(FILE *f)
fseek(f, 0L, SEEK_END);
size_t file_size = ftell(f);
fseek(f, 0L, SEEK_SET);
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ fread_wrapper = fread_safe_from_file;
const size_t chunk_size = 4096;
/* read chunks of 4 kB */
@@ -836,11 +940,12 @@ int knot_zload_open(zloader_t **dst, const char *filename)
dbg_zload("zload: open: Bad arguments.\n");
return KNOT_EBADARG;
}
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ fread_wrapper = fread_safe_from_file;
*dst = 0;
- fread_wrapper = fread_safe_from_file;
-
/* Open file for binary read. */
FILE *f = fopen(filename, "rb");
if (unlikely(!f)) {
@@ -856,6 +961,15 @@ int knot_zload_open(zloader_t **dst, const char *filename)
return KNOT_EFEWDATA; // No such file or directory (POSIX.1)
}
+
+ /* Calculate file size. */
+ fseek(f, 0L, SEEK_END);
+ size_t file_size = ftell(f);
+ fseek(f, 0L, SEEK_SET);
+ if (file_size < MAGIC_LENGTH) {
+ fclose(f);
+ return KNOT_EFEWDATA;
+ }
/* Calculate CRC and compare with filename.crc file */
unsigned long crc_calculated = calculate_crc(f);
@@ -983,8 +1097,14 @@ int knot_zload_open(zloader_t **dst, const char *filename)
static void cleanup_id_array(knot_dname_t **id_array,
const uint from, const uint to)
{
+ if (id_array == NULL) {
+ dbg_zload("zload: cleanup_id_array: NULL arguments.\n");
+ }
+
for (uint i = from; i < to; i++) {
- knot_dname_release(id_array[i]);
+ if (id_array[i] != NULL) {
+ knot_dname_release(id_array[i]);
+ }
}
free(id_array);
@@ -1044,7 +1164,7 @@ static knot_dname_t **create_dname_array(FILE *f, uint max_id)
memset(array, 0, sizeof(knot_dname_t *) * (max_id + 1));
for (uint i = 0; i < max_id - 1; i++) {
- knot_dname_t *read_dname = read_dname_with_id(f);
+ knot_dname_t *read_dname = read_dname_with_id(f, 1);
if (read_dname == NULL) {
dbg_zload("zload: create_dname_array: "
"Cannot read dname.\n" );
@@ -1090,8 +1210,6 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
return NULL;
}
- fread_wrapper = fread_safe_from_file;
-
FILE *f = loader->fp;
knot_node_t *tmp_node;
@@ -1099,7 +1217,10 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
uint32_t node_count;
uint32_t nsec3_node_count;
uint32_t auth_node_count;
-
+
+ int (*fread_wrapper)(void *dst, size_t size, size_t n, void *source);
+ fread_wrapper = fread_safe_from_file;
+
if (!fread_wrapper(&node_count, sizeof(node_count), 1, f)) {
dbg_zload("zload: load: Cannot read node count!\n");
return NULL;
@@ -1147,7 +1268,7 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
knot_node_t *apex = knot_load_node(f, id_array);
if (!apex) {
- fprintf(stderr, "zone: Could not load apex node (in %s)\n",
+ dbg_zload("zone: Could not load apex node (in %s)\n",
loader->filename);
cleanup_id_array(id_array, 1,
node_count + nsec3_node_count + 1);
@@ -1163,7 +1284,7 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
node_count + nsec3_node_count + 1);
dbg_zload("zload: load: Failed to create new "
"zone from apex!\n");
- knot_node_free(&apex, 0);
+ knot_node_free(&apex);
free(dname_table);
return NULL;
}
@@ -1178,19 +1299,26 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
knot_node_t *last_node = 0;
last_node = apex;
+ int ret = 0;
+
for (uint i = 1; i < node_count; i++) {
tmp_node = knot_load_node(f, id_array);
if (tmp_node != NULL) {
- if (knot_zone_contents_add_node(contents, tmp_node,
- 0, 0, 0) != 0) {
+ dbg_zload_detail("zload: load: Adding node owned by: "
+ "%s\n.",
+ knot_dname_to_str(tmp_node->owner));
+ if ((ret = knot_zone_contents_add_node(contents,
+ tmp_node,
+ 0, 0, 0)) != 0) {
cleanup_id_array(id_array, 1,
node_count +
nsec3_node_count + 1);
knot_zone_deep_free(&zone, 0);
dbg_zload("zload: load: Failed to add node "
- "to zone.\n");
+ "to zone: %s.\n", knot_strerror(ret));
return NULL;
}
+
if (knot_dname_is_wildcard(tmp_node->owner)) {
find_and_set_wildcard_child(contents,
tmp_node, 0);
@@ -1229,12 +1357,12 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
assert(nsec3_first != NULL);
- if (knot_zone_contents_add_nsec3_node(contents, nsec3_first,
- 0, 0, 0)
- != 0) {
+ if ((ret = knot_zone_contents_add_nsec3_node(contents,
+ nsec3_first,
+ 0, 0, 0)) != 0) {
dbg_zload("zload: load: "
"cannot add first nsec3 node, "
- "exiting.\n");
+ "exiting: %s.\n", knot_strerror(ret));
knot_zone_deep_free(&zone, 0);
cleanup_id_array(id_array, node_count + 1,
nsec3_node_count + 1);
@@ -1249,10 +1377,11 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
tmp_node = knot_load_node(f, id_array);
if (tmp_node != NULL) {
- if (knot_zone_contents_add_nsec3_node(contents,
- tmp_node, 0, 0, 0) != 0) {
+ if ((ret = knot_zone_contents_add_nsec3_node(contents,
+ tmp_node, 0, 0, 0)) != 0) {
dbg_zload("zload: load: Cannot add "
- "NSEC3 node.\n");
+ "NSEC3 node: %s.\n",
+ knot_strerror(ret));
knot_zone_deep_free(&zone, 0);
cleanup_id_array(id_array, node_count + 1,
nsec3_node_count + 1);
@@ -1265,6 +1394,10 @@ knot_zone_t *knot_zload_load(zloader_t *loader)
} else {
fprintf(stderr, "zone: Node error (in %s).\n",
loader->filename);
+ knot_zone_deep_free(&zone, 0);
+ cleanup_id_array(id_array, node_count + 1,
+ nsec3_node_count + 1);
+ return NULL;
}
}
@@ -1332,27 +1465,20 @@ int knot_zload_rrset_deserialize(knot_rrset_t **rrset,
return KNOT_EBADARG;
}
- fread_wrapper = read_from_stream;
+ load_stream_t data;
+ data.stream = stream;
+ data.stream_remaining = *size;
+ data.stream_size = *size;
- knot_zload_stream = stream;
- knot_zload_stream_remaining = knot_zload_stream_size = *size;
-
- knot_rrset_t *ret = knot_load_rrset(NULL, NULL, 0);
+ knot_rrset_t *ret = knot_load_rrset(&data, NULL, 0);
if (ret == NULL) {
dbg_zload("zload: rrset_deserialize: Cannot load RRSet.\n");
- knot_zload_stream = NULL;
- knot_zload_stream_remaining = 0;
- knot_zload_stream_size = 0;
return KNOT_EMALF;
}
- *size = knot_zload_stream_remaining;
+ *size = data.stream_remaining;
*rrset = ret;
- knot_zload_stream = NULL;
- knot_zload_stream_remaining = 0;
- knot_zload_stream_size = 0;
-
dbg_zload_detail("zload: rrset_deserialize: RRSet deserialized "
"successfully.\n");
return KNOT_EOK;
diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h
index 2fe318f..837d5f2 100644..100755
--- a/src/knot/zone/zone-load.h
+++ b/src/knot/zone/zone-load.h
@@ -20,7 +20,7 @@
*
* \brief Loader of previously parsed zone
*
- * \addtogroup dnslib
+ * \addtogroup zone-load-dump
* @{
*/
diff --git a/src/knotc.8 b/src/knotc.8
index b61bfd3..9cd3f08 100644..100755
--- a/src/knotc.8
+++ b/src/knotc.8
@@ -1,6 +1,6 @@
-.TH knotc "8" "November 2011" "CZ.NIC Labs" "Knot DNS, version 0.8"
+.TH knotc "8" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1"
.SH NAME
-.B knot
+.B knotc
\- Knot DNS control utility
.SH SYNOPSIS
.B knotc
@@ -28,27 +28,39 @@ Wait for the server to finish start/stop operations.
\fB\-i\fR, \fB\-\-interactive\fR
Interactive mode (do not daemonize).
.TP
+\fB\-a\fR, \fB\-\-auto\fR
+Enable automatic recompilation (start or reload).
+.TP
\fB\-h\fR, \fB\-\-help\fR
Print help and usage.
.SS "Actions:"
.TP
start
-Start knot server zone (no\-op if running).
+Start knot server daemon (no\-op if running).
.TP
stop
-Stop knot server (no\-op if not running).
+Stop knot server daemon (no\-op if not running).
.TP
restart
-Stops and then starts knot server.
+Stops and then starts knot server daemon.
.TP
reload
Reload knot configuration and compiled zones.
.TP
running
-check if server is running.
+Check if server is running.
.TP
compile
Compile zone file.
+.TP
+refresh
+Refresh all slave zones.
+.TP
+checkconf
+Check server configuration.
+.TP
+checkzone
+Check zones before compiling (accepts specific zones, f.e. 'knotc checkzone example1.com example2.com').
.SH "SEE ALSO"
The full documentation for
.B Knot
diff --git a/src/knotd.8 b/src/knotd.8
index df4b264..cdc450a 100644..100755
--- a/src/knotd.8
+++ b/src/knotd.8
@@ -1,4 +1,4 @@
-.TH "knotd" "8" "November 2011" "CZ.NIC Labs" "Knot DNS, version 0.8"
+.TH "knotd" "8" "August 2012" "CZ.NIC Labs" "Knot DNS, version 1.1"
.SH NAME
.B knotd
\- Knot DNS daemon
diff --git a/src/libknot/common.h b/src/libknot/common.h
index 9b2d8ae..9b2d8ae 100644..100755
--- a/src/libknot/common.h
+++ b/src/libknot/common.h
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
index 4249763..4249763 100644..100755
--- a/src/libknot/consts.h
+++ b/src/libknot/consts.h
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
index 80de030..7d59b6b 100644..100755
--- a/src/libknot/dname.c
+++ b/src/libknot/dname.c
@@ -163,7 +163,7 @@ static int knot_dname_str_to_wire(const char *name, uint size,
return -1;
}
- dbg_dname("Allocated space for wire format of dname: %p\n", wire);
+ dbg_dname_verb("Allocated space for wire format of dname: %p\n", wire);
if (root) {
*wire = '\0';
@@ -181,18 +181,18 @@ static int knot_dname_str_to_wire(const char *name, uint size,
assert(w - wire - 1 == ch - (const uint8_t *)name);
if (*ch == '.') {
- dbg_dname("Position %zd (%p): "
- "label length: %u\n",
- label_start - wire,
- label_start, label_length);
+ dbg_dname_detail("Position %zd (%p): "
+ "label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
*label_start = label_length;
labels[label_count++] = label_start - wire;
label_start = w;
label_length = 0;
} else {
assert(w - wire < wire_size);
- dbg_dname("Position %zd (%p): character: %c\n",
- w - wire, w, *ch);
+ dbg_dname_detail("Position %zd (%p): character: %c\n",
+ w - wire, w, *ch);
*w = *ch;
++label_length;
}
@@ -205,14 +205,13 @@ static int knot_dname_str_to_wire(const char *name, uint size,
--ch;
if (*ch == '.') { // put 0 for root label if the name ended with .
--w;
- dbg_dname("Position %zd (%p): character: (null)\n",
- w - wire, w);
+ dbg_dname_detail("Position %zd (%p): character: (null)\n",
+ w - wire, w);
*w = 0;
} else { // otherwise we did not save the last label length
- dbg_dname("Position %zd (%p): "
- "label length: %u\n",
- label_start - wire,
- label_start, label_length);
+ dbg_dname_detail("Position %zd (%p): label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
*label_start = label_length;
labels[label_count++] = label_start - wire;
}
@@ -277,7 +276,8 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
if (pos - name > size || *pos != '\0' ) {
dbg_dname("Wrong wire format of domain name!\n");
- dbg_dname("Position: %d, character: %d, expected size: %d\n", pos - name, *pos, size);
+ dbg_dname("Position: %d, character: %d, expected size: %d\n",
+ pos - name, *pos, size);
return -1;
}
@@ -298,12 +298,11 @@ static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
static int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2,
int cs)
{
-dbg_dname_exec(
+dbg_dname_exec_verb(
char *name1 = knot_dname_to_str(d1);
char *name2 = knot_dname_to_str(d2);
- dbg_dname("Comparing dnames %s and %s\n",
- name1, name2);
+ dbg_dname_verb("Comparing dnames %s and %s\n", name1, name2);
for (int i = 0; i < strlen(name1); ++i) {
name1[i] = knot_tolower(name1[i]);
@@ -312,8 +311,7 @@ dbg_dname_exec(
name2[i] = knot_tolower(name2[i]);
}
- dbg_dname("After to lower: %s and %s\n",
- name1, name2);
+ dbg_dname_detail("After to lower: %s and %s\n", name1, name2);
free(name1);
free(name2);
@@ -325,16 +323,16 @@ dbg_dname_exec(
int l1 = d1->label_count;
int l2 = d2->label_count;
- dbg_dname("Label counts: %d and %d\n", l1, l2);
+ dbg_dname_detail("Label counts: %d and %d\n", l1, l2);
assert(l1 >= 0);
assert(l2 >= 0);
// compare labels from last to first
while (l1 > 0 && l2 > 0) {
- dbg_dname("Comparing labels %d and %d\n",
- l1 - 1, l2 - 1);
- dbg_dname(" at offsets: %d and %d\n",
- d1->labels[l1 - 1], d2->labels[l2 - 1]);
+ dbg_dname_detail("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname_detail(" at offsets: %d and %d\n",
+ d1->labels[l1 - 1], d2->labels[l2 - 1]);
int res = knot_dname_compare_labels(
&d1->name[d1->labels[--l1]],
&d2->name[d2->labels[--l2]],
@@ -434,26 +432,6 @@ dbg_dname_exec_verb(
/*----------------------------------------------------------------------------*/
-//int knot_dname_from_wire(knot_dname_t *dname, const uint8_t *name,
-// uint size)
-//{
-// int i = 0;
-// uint8_t labels[KNOT_MAX_DNAME_LABELS];
-// int label_i = 0;
-
-// while (name[i] != 0) {
-// labels[label_i++] = i;
-// uint8_t label_length = name[i];
-// if (i + label_length >= size) {
-// return -2;
-// }
-// for (int j = 1; j <= label_length; ++j) {
-// }
-// }
-//}
-
-/*----------------------------------------------------------------------------*/
-
knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
struct knot_node *node)
{
@@ -510,12 +488,10 @@ knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
return NULL;
}
labels[l] = i;
- dbg_dname("Next label (%d.) position: %zu\n", l, i);
+ dbg_dname_detail("Next label (%d.) position: %zu\n", l, i);
if (knot_wire_is_pointer(wire + p)) {
// pointer.
-
-// printf("Pointer.\n");
size_t ptr = knot_wire_get_pointer(wire + p);
/* Check that the pointer points backwards
@@ -622,6 +598,10 @@ knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname)
/* dname_new_from_wire() does not accept non-FQDN dnames, so we
* do the copy by hand. It's faster anyway */
+ if (dname == NULL) {
+ return NULL;
+ }
+
knot_dname_t *copy = knot_dname_new();
CHECK_ALLOC(copy, NULL);
@@ -771,7 +751,44 @@ struct knot_node *knot_dname_get_node(const knot_dname_t *dname)
if (dname == NULL) {
return NULL;
}
- return dname->node;
+
+ knot_node_t *node = dname->node;
+
+ /*
+ * If the zone contains new zone contents (during an update), we should
+ * return new node. Check if the node has the new node set. If it does
+ * not, it means this is already the new node. If it has, return the
+ * new node. If the new node is empty, return NULL, as the node will be
+ * deleted later.
+ */
+dbg_dname_exec_detail(
+ dbg_dname_detail("Getting node from dname: node: %p, zone: %p\n", node,
+ knot_node_zone(node));
+ if (node != NULL && knot_node_zone(node) != NULL
+ && knot_zone_contents(knot_node_zone(node)) != NULL) {
+ dbg_dname_detail("zone contents gen: %d, new node of the node: "
+ "%p, is empty: %d\n",
+ knot_zone_contents_gen_is_new(knot_zone_contents(
+ knot_node_zone(node))),
+ knot_node_new_node(node),
+ knot_node_new_node(node)
+ ? knot_node_is_empty(knot_node_new_node(node))
+ : -1);
+ }
+);
+
+ if (node && knot_node_zone(node)
+ && knot_zone_contents(knot_node_zone(node))
+ && knot_zone_contents_gen_is_new(knot_zone_contents(
+ knot_node_zone(node)))
+ && knot_node_new_node(node) != NULL) {
+ node = knot_node_get_new_node(node);
+ if (knot_node_is_empty(node)) {
+ node = NULL;
+ }
+ }
+
+ return node;
}
/*----------------------------------------------------------------------------*/
@@ -785,7 +802,20 @@ void knot_dname_set_node(knot_dname_t *dname, knot_node_t *node)
void knot_dname_update_node(knot_dname_t *dname)
{
+dbg_dname_exec_detail(
+ char *name = knot_dname_to_str(dname);
+ dbg_dname_detail("Updating node pointer in dname %p: %s. Before: %p\n",
+ dname, name, dname->node);
+ free(name);
+);
+
knot_node_update_ref(&dname->node);
+ dbg_dname_detail("After: %p\n", dname->node);
+
+ if (knot_node_is_empty(dname->node)) {
+ dbg_dname_detail("Node is empty, setting to NULL.\n");
+ dname->node = NULL;
+ }
}
/*----------------------------------------------------------------------------*/
@@ -829,12 +859,6 @@ knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname)
return parent;
}
-
-// if (dname->label_count <= 1) {
-// /* Nothing to chop. */
-// return NULL;
-// }
-
parent->size = dname->size - dname->name[0] - 1;
parent->name = (uint8_t *)malloc(parent->size);
@@ -894,12 +918,11 @@ void knot_dname_left_chop_no_copy(knot_dname_t *dname)
int knot_dname_is_subdomain(const knot_dname_t *sub,
const knot_dname_t *domain)
{
-dbg_dname_exec(
+dbg_dname_exec_verb(
char *name1 = knot_dname_to_str(sub);
char *name2 = knot_dname_to_str(domain);
- dbg_dname("Checking if %s is subdomain of %s\n",
- name1, name2);
+ dbg_dname_verb("Checking if %s is subdomain of %s\n", name1, name2);
free(name1);
free(name2);
);
@@ -919,7 +942,7 @@ dbg_dname_exec(
int l1 = sub->label_count;
int l2 = domain->label_count;
- dbg_dname("Label counts: %d and %d\n", l1, l2);
+ dbg_dname_detail("Label counts: %d and %d\n", l1, l2);
if (l1 <= l2) { // if sub does not have more labes than domain
return 0; // it is not its subdomain
@@ -927,10 +950,10 @@ dbg_dname_exec(
// compare labels from last to first
while (l1 > 0 && l2 > 0) {
- dbg_dname("Comparing labels %d and %d\n",
- l1 - 1, l2 - 1);
- dbg_dname(" at offsets: %d and %d\n",
- sub->labels[l1 - 1], domain->labels[l2 - 1]);
+ dbg_dname_detail("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname_detail(" at offsets: %d and %d\n",
+ sub->labels[l1 - 1], domain->labels[l2 - 1]);
// if some labels do not match
if (knot_dname_compare_labels(&sub->name[sub->labels[--l1]],
&domain->name[domain->labels[--l2]], 0)
@@ -989,15 +1012,6 @@ int knot_dname_label_count(const knot_dname_t *dname)
uint8_t knot_dname_label_size(const knot_dname_t *dname, int i)
{
-// printf("Returning size of %d. label starting on %d\n",
-// i, dname->labels[i]);
-// printf("Label count: %d, size of %d. label: %d, size of %d.label: %d\n",
-// dname->label_count, i, dname->labels[i], i + 1,
-// dname->labels[i+1]);
-// printf("Size from the name: %u\n", dname->name[dname->labels[i]]);
-// printf("Size from label offsets: %u\n",
-// dname->labels[i + 1] - dname->labels[i]);
-
assert(i >= 0);
assert(dname->size == 1 || i + 1 == dname->label_count
|| dname->labels[i + 1] - dname->labels[i] - 1
@@ -1007,17 +1021,16 @@ uint8_t knot_dname_label_size(const knot_dname_t *dname, int i)
/*----------------------------------------------------------------------------*/
-knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname,
- int size,
- const knot_dname_t *suffix)
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname, int size,
+ const knot_dname_t *suffix)
{
-dbg_dname_exec(
+dbg_dname_exec_verb(
char *name = knot_dname_to_str(dname);
- dbg_dname("Replacing suffix of name %s, size %d with ", name,
- size);
+ dbg_dname_verb("Replacing suffix of name %s, size %d with ", name,
+ size);
free(name);
name = knot_dname_to_str(suffix);
- dbg_dname("%s (size %d)\n", name, suffix->size);
+ dbg_dname_verb("%s (size %d)\n", name, suffix->size);
free(name);
);
knot_dname_t *res = knot_dname_new();
@@ -1025,7 +1038,7 @@ dbg_dname_exec(
res->size = dname->size - size + suffix->size;
- dbg_dname("Allocating %d bytes...\n", res->size);
+ dbg_dname_detail("Allocating %d bytes...\n", res->size);
res->name = (uint8_t *)malloc(res->size);
if (res->name == NULL) {
knot_dname_free(&res);
@@ -1034,12 +1047,12 @@ dbg_dname_exec(
dbg_dname_hex((char *)res->name, res->size);
- dbg_dname("Copying %d bytes from the original name.\n",
- dname->size - size);
+ dbg_dname_detail("Copying %d bytes from the original name.\n",
+ dname->size - size);
memcpy(res->name, dname->name, dname->size - size);
dbg_dname_hex((char *)res->name, res->size);
- dbg_dname("Copying %d bytes from the suffix.\n", suffix->size);
+ dbg_dname_detail("Copying %d bytes from the suffix.\n", suffix->size);
memcpy(res->name + dname->size - size, suffix->name, suffix->size);
dbg_dname_hex((char *)res->name, res->size);
@@ -1057,13 +1070,6 @@ void knot_dname_free(knot_dname_t **dname)
return;
}
-// char *name = knot_dname_to_str((*dname));
-
-// printf("freeing in dname: %s %p\n", name, *dname);
-
-// free(name);
-
-
if ((*dname)->name != NULL) {
free((*dname)->name);
}
@@ -1116,13 +1122,13 @@ knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
return NULL;
}
- dbg_dname("1: copying %d bytes from adress %p to %p\n",
- d1->size, d1->name, new_dname);
+ dbg_dname_detail("1: copying %d bytes from adress %p to %p\n",
+ d1->size, d1->name, new_dname);
memcpy(new_dname, d1->name, d1->size);
- dbg_dname("2: copying %d bytes from adress %p to %p\n",
- d2->size, d2->name, new_dname + d1->size);
+ dbg_dname_detail("2: copying %d bytes from adress %p to %p\n",
+ d2->size, d2->name, new_dname + d1->size);
memcpy(new_dname + d1->size, d2->name, d2->size);
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
index 473bca7..43bc4d2 100644..100755
--- a/src/libknot/dname.h
+++ b/src/libknot/dname.h
@@ -224,8 +224,6 @@ const struct knot_node *knot_dname_node(const knot_dname_t *dname);
struct knot_node *knot_dname_get_node(const knot_dname_t *dname);
-void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
-
void knot_dname_update_node(knot_dname_t *dname);
void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
diff --git a/src/libknot/edns.c b/src/libknot/edns.c
index ea630dd..8e1efcc 100644..100755
--- a/src/libknot/edns.c
+++ b/src/libknot/edns.c
@@ -71,21 +71,20 @@ int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
// owner of EDNS OPT RR must be root (0)
if (*pos != 0) {
dbg_edns("EDNS packet malformed (expected root "
- "domain as owner).\n");
+ "domain as owner).\n");
return KNOT_EMALF;
}
pos += 1;
// check the type of the record (must be OPT)
if (knot_wire_read_u16(pos) != KNOT_RRTYPE_OPT) {
- dbg_edns("EDNS packet malformed (expected OPT type"
- ".\n");
+ dbg_edns("EDNS packet malformed (expected OPT type.\n");
return KNOT_EMALF;
}
pos += 2;
opt_rr->payload = knot_wire_read_u16(pos);
- dbg_edns("Parsed payload: %u\n", opt_rr->payload);
+ dbg_edns_verb("Parsed payload: %u\n", opt_rr->payload);
pos += 2;
opt_rr->ext_rcode = *(pos++);
@@ -107,18 +106,18 @@ int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
while (parsed < rdlength + KNOT_EDNS_MIN_SIZE) {
if (max_size - parsed < 4) {
dbg_edns("Not enough data to parse OPT RR"
- " OPTION header.\n");
+ " OPTION header.\n");
return KNOT_EFEWDATA;
}
uint16_t code = knot_wire_read_u16(pos);
pos += 2;
uint16_t length = knot_wire_read_u16(pos);
pos += 2;
- dbg_edns("EDNS OPTION: Code: %u, Length: %u\n",
- code, length);
+ dbg_edns_verb("EDNS OPTION: Code: %u, Length: %u\n",
+ code, length);
if (max_size - parsed - 4 < length) {
dbg_edns("Not enough data to parse OPT RR"
- " OPTION data.\n");
+ " OPTION data.\n");
return KNOT_EFEWDATA;
}
int ret;
@@ -144,28 +143,28 @@ int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
return KNOT_EBADARG;
}
- dbg_edns("Parsing payload.\n");
+ dbg_edns_verb("Parsing payload.\n");
opt_rr->payload = knot_rrset_class(rrset);
// the TTL has switched bytes
uint32_t ttl;
- dbg_edns("TTL: %u\n", knot_rrset_ttl(rrset));
+ dbg_edns_detail("TTL: %u\n", knot_rrset_ttl(rrset));
knot_wire_write_u32((uint8_t *)&ttl, knot_rrset_ttl(rrset));
// first byte of TTL is extended RCODE
- dbg_edns("TTL: %u\n", ttl);
+ dbg_edns_detail("TTL: %u\n", ttl);
memcpy(&opt_rr->ext_rcode, &ttl, 1);
- dbg_edns("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode);
+ dbg_edns_detail("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode);
// second is the version
memcpy(&opt_rr->version, (const uint8_t *)(&ttl) + 1, 1);
- dbg_edns("Parsed version: %u.\n", opt_rr->version);
+ dbg_edns_detail("Parsed version: %u.\n", opt_rr->version);
// third and fourth are flags
opt_rr->flags = knot_wire_read_u16((const uint8_t *)(&ttl) + 2);
- dbg_edns("Parsed flags: %u.\n", opt_rr->flags);
+ dbg_edns_detail("Parsed flags: %u.\n", opt_rr->flags);
// size of the header, options are counted elsewhere
opt_rr->size = 11;
int rc = 0;
- dbg_edns("Parsing options.\n");
+ dbg_edns_verb("Parsing options.\n");
const knot_rdata_t *rdata = knot_rrset_rdata(rrset);
// in OPT RR, all RDATA are in one RDATA item stored as BINARY data,
@@ -206,7 +205,7 @@ int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
}
- dbg_edns("EDNS created.\n");
+ dbg_edns_verb("EDNS created.\n");
return KNOT_EOK;
}
@@ -316,10 +315,10 @@ int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code,
free(old_options);
}
- dbg_edns("Adding option.\n");
- dbg_edns("Code: %u.\n", code);
- dbg_edns("Length: %u.\n", length);
- dbg_edns("Data: %p.\n", data);
+ dbg_edns_verb("Adding option.\n");
+ dbg_edns_verb("Code: %u.\n", code);
+ dbg_edns_verb("Length: %u.\n", length);
+ dbg_edns_verb("Data: %p.\n", data);
opt_rr->options[opt_rr->option_count].data = (uint8_t *)malloc(length);
CHECK_ALLOC_LOG(opt_rr->options[opt_rr->option_count].data, KNOT_ENOMEM);
@@ -370,9 +369,9 @@ short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire,
uint8_t *pos = wire;
- dbg_edns_detail("Putting OPT RR to the wire format. Size: %d, "
- "position: %zu\n",
- opt_rr->size, (size_t)(pos - wire));
+ dbg_edns_verb("Putting OPT RR to the wire format. Size: %d, "
+ "position: %zu\n",
+ opt_rr->size, (size_t)(pos - wire));
*(pos++) = 0;
knot_wire_write_u16(pos, KNOT_RRTYPE_OPT);
diff --git a/src/libknot/edns.h b/src/libknot/edns.h
index 022ac36..022ac36 100644..100755
--- a/src/libknot/edns.h
+++ b/src/libknot/edns.h
diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c
index 9db32bf..7358e14 100644..100755
--- a/src/libknot/hash/cuckoo-hash-table.c
+++ b/src/libknot/hash/cuckoo-hash-table.c
@@ -30,7 +30,6 @@
#include "util/debug.h"
#include "hash/cuckoo-hash-table.h"
#include "hash/hash-functions.h"
-#include "common/dynamic-array.h"
/*----------------------------------------------------------------------------*/
/* Macros and inline functions */
@@ -283,6 +282,8 @@ static int ck_stash_is_full(const ck_hash_table_t *table)
*/
static inline void ck_clear_item(ck_hash_table_item_t **item)
{
+ dbg_stash("[EMPTY STASH] [CREATE] setting item %p (%p) to NULL.\n",
+ item, *item);
*item = NULL;
}
@@ -320,49 +321,11 @@ static inline void ck_swap_items(ck_hash_table_item_t **item1,
static inline void ck_put_item(ck_hash_table_item_t **to,
ck_hash_table_item_t *item)
{
- *to = item;
-}
-
-/*----------------------------------------------------------------------------*/
-/*!
- * \brief Checks if the hash was already used twice.
- *
- * If yes, it means we entered a loop in the hashing process, so we must stop.
- * Otherwise it remembers that we used the hash.
- *
- * \note According to Kirsch, et al. a check that at most one hash was used
- * twice should be sufficient. We will retain our version for now.
- *
- * \param used Array of used table indices (hashes).
- * \param hash Hash to check.
- *
- * \retval -1 if the hash was already used twice.
- * \retval -2 if an error occured.
- * \retval 0 if the hash was not used twice yet.
- */
-static uint ck_check_used_twice(da_array_t *used, uint32_t hash)
-{
- uint i = 0, found = 0;
- while (i < da_get_count(used) && found < 2) {
- if (((uint *)(da_get_items(used)))[i] == hash) {
- ++found;
- }
- ++i;
- }
-
- if (found == 2) {
- dbg_ck_hash("Hashing entered infinite loop.\n");
- return -1;
- } else {
- if (da_reserve(used, 1) < 0) {
- ERR_ALLOC_FAILED;
- return -2;
- }
- ((uint *)da_get_items(used))[da_get_count(used)] = hash;
- da_occupy(used, 1);
- assert(da_get_count(used) < RELOCATIONS_MAX);
- return 0;
+ if (item == NULL) {
+ dbg_stash("[EMPTY STASH] [CREATE] Putting NULL to item %p.\n",
+ to);
}
+ *to = item;
}
/*----------------------------------------------------------------------------*/
@@ -419,12 +382,14 @@ static ck_hash_table_item_t **ck_find_in_stash(const ck_hash_table_t *table,
* non-NULL.
*/
if (item->item && ck_items_match(item->item, key, length)) {
- dbg_ck("Comparing item in stash (key: %.*s (size %zu))"
- "with searched item (key %.*s (size %u)).\n",
- (int)item->item->key_length, item->item->key,
- item->item->key_length, (int)length, key,
- length);
+ dbg_ck_detail("Comparing item in stash (key: %.*s (size"
+ " %zu)) with searched item (key %.*s (size %u)).\n",
+ (int)item->item->key_length, item->item->key,
+ item->item->key_length, (int)length, key, length);
return &item->item;
+ } else if (item->item == NULL) {
+ dbg_stash("[EMPTY STASH] [FIND] STASH ITEM IS EMPTY: "
+ "%p (%p)\n", item, item->item);
}
item = item->next;
}
@@ -448,18 +413,18 @@ static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table,
size_t length, uint8_t generation)
{
uint32_t hash;
- dbg_ck("Finding item in generation: %u\n", generation);
+ dbg_ck_verb("Finding item in generation: %u\n", generation);
// check hash tables
for (uint t = 0; t < table->table_count; ++t) {
hash = HASH(&table->hash_system, key, length,
table->table_size_exp, generation, t);
- dbg_ck("Hash: %u, key: %.*s\n", hash, (int)length, key);
- dbg_ck("Table %d, hash: %u, item: %p\n", t + 1, hash,
- table->tables[t][hash]);
+ dbg_ck_detail("Hash: %u, key: %.*s\n", hash, (int)length, key);
+ dbg_ck_detail("Table %d, hash: %u, item: %p\n", t + 1, hash,
+ table->tables[t][hash]);
if (table->tables[t][hash] != NULL) {
- dbg_ck("Table %u, key: %.*s, value: %p, key "
+ dbg_ck_detail("Table %u, key: %.*s, value: %p, key "
"length: %zu\n",
t + 1, (int)table->tables[t][hash]->key_length,
table->tables[t][hash]->key,
@@ -475,16 +440,16 @@ static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table,
}
// try to find in stash
- dbg_ck("Searching in stash...\n");
+ dbg_ck_verb("Searching in stash...\n");
ck_hash_table_item_t **found =
ck_find_in_stash(table, key, length);
- dbg_ck("Found pointer: %p\n", found);
+ dbg_ck_verb("Found pointer: %p\n", found);
if (found != NULL) {
- dbg_ck("Stash, key: %.*s, value: %p, key length: %zu\n",
- (int)(*found)->key_length, (*found)->key,
- (*found)->value, (*found)->key_length);
+ dbg_ck_verb("Stash, key: %.*s, value: %p, key length: %zu\n",
+ (int)(*found)->key_length, (*found)->key,
+ (*found)->value, (*found)->key_length);
}
// ck_find_in_buffer returns NULL if not found, otherwise pointer to
@@ -520,6 +485,136 @@ static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table,
}
/*----------------------------------------------------------------------------*/
+/* Lightweight dynamic array for keeping track of used items. */
+/*----------------------------------------------------------------------------*/
+
+typedef struct ck_used {
+ uint32_t *items;
+ uint array_count;
+ size_t *counts;
+ size_t allocated;
+} ck_used_t;
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_used_create(ck_used_t *used, uint table_count)
+{
+ // all tables in one array
+ used->items = malloc(table_count * RELOCATIONS_DEFAULT
+ * sizeof(uint32_t));
+ if (used->items == NULL) {
+ return -1;
+ }
+
+ used->counts = malloc(table_count * sizeof(size_t));
+ if (used->counts == NULL) {
+ free(used->items);
+ return -1;
+ }
+
+ used->array_count = table_count;
+ used->allocated = RELOCATIONS_DEFAULT;
+
+ for (int i = 0; i < table_count; ++i) {
+ used->counts[i] = 0;
+ }
+
+ memset(used->items, 0,
+ used->array_count * used->allocated * sizeof(uint32_t));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void ck_used_free(ck_used_t *used)
+{
+ free(used->items);
+ free(used->counts);
+ used->allocated = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_used_add(ck_used_t *used, uint table_nr, uint32_t to_add)
+{
+ dbg_ck_hash_verb("1) Table nr: %u, count: %zu, allocated: %zu\n",
+ table_nr, used->counts[table_nr], used->allocated);
+
+ if (used->counts[table_nr] == used->allocated) {
+ dbg_ck_hash_verb("Reallocating...\n");
+ size_t allocated_new = used->allocated * 2;
+ uint32_t *items_new = malloc(used->array_count * allocated_new
+ * sizeof(uint32_t));
+ if (items_new == NULL) {
+ return -1;
+ }
+
+ memcpy(items_new, used->items,
+ used->allocated * used->array_count);
+
+ uint32_t *old_items = used->items;
+
+ used->items = items_new;
+ used->allocated = allocated_new;
+
+ free(old_items);
+ }
+
+ dbg_ck_hash_verb("2) Table nr: %u, count: %zu, allocated: %zu\n",
+ table_nr, used->counts[table_nr], used->allocated);
+
+ assert(used->counts[table_nr] < used->allocated);
+ used->items[table_nr * used->allocated + used->counts[table_nr]]
+ = to_add;
+ ++used->counts[table_nr];
+
+ dbg_ck_hash_verb("3)Table nr: %u, count: %zu, allocated: %zu\n",
+ table_nr, used->counts[table_nr], used->allocated);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the hash was already used twice.
+ *
+ * If yes, it means we entered a loop in the hashing process, so we must stop.
+ * Otherwise it remembers that we used the hash.
+ *
+ * \note According to Kirsch, et al. a check that at most one hash was used
+ * twice should be sufficient. We will retain our version for now.
+ *
+ * \param used Array of used table indices (hashes).
+ * \param hash Hash to check.
+ *
+ * \retval -1 if the hash was already used twice.
+ * \retval -2 if an error occured.
+ * \retval 0 if the hash was not used twice yet.
+ */
+static int ck_check_used_twice(ck_used_t *used, uint table_nr,
+ uint32_t hash)
+{
+ uint i = 0, found = 0;
+
+ while (i < used->counts[table_nr] && found < 2) {
+ if (used->items[table_nr * used->allocated + i] == hash) {
+ ++found;
+ }
+ ++i;
+ }
+
+ if (found == 2) {
+ dbg_ck_hash("Hashing entered infinite loop.\n");
+ return -1;
+ } else {
+ return ck_used_add(used, table_nr, hash);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
/*!
* \brief Hashes the given item using the given generation.
*
@@ -532,20 +627,26 @@ static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table,
*
* \retval 0 if successful and no loop occured.
* \retval 1 if a loop occured and the item was inserted to the \a free place.
+ * \retval < 0 if an error occured.
*/
static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
ck_hash_table_item_t **free, uint8_t generation)
{
- da_array_t used[table->table_count];
- for (uint i = 0; i < table->table_count; ++i) {
- da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint));
+// da_array_t used[table->table_count];
+// for (uint i = 0; i < table->table_count; ++i) {
+// da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint));
+// }
+ ck_used_t used;
+ int ret = ck_used_create(&used, table->table_count);
+ if (ret != 0) {
+ return -1;
}
// hash until empty cell is encountered or until loop appears
- dbg_ck_hash("Hashing key: %.*s of size %zu.\n",
- (int)(*to_hash)->key_length, (*to_hash)->key,
- (*to_hash)->key_length);
+ dbg_ck_hash_verb("Hashing key: %.*s of size %zu.\n",
+ (int)(*to_hash)->key_length, (*to_hash)->key,
+ (*to_hash)->key_length);
uint next_table = 0;
@@ -553,21 +654,24 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
(*to_hash)->key_length, table->table_size_exp,
generation, next_table);
- dbg_ck_hash("New hash: %u.\n", hash);
+ dbg_ck_hash_detail("New hash: %u.\n", hash);
assert(hash < hashsize(table->table_size_exp));
- ((uint *)da_get_items(&used[next_table]))
- [da_get_count(&used[next_table])] = hash;
+ ret = ck_used_add(&used, next_table, hash);
+ if (ret != 0) {
+ return -2;
+ }
+
ck_hash_table_item_t **next = &table->tables[next_table][hash];
- dbg_ck_hash("Item to be moved: %p, place in table: %p\n",
- *next, next);
+ dbg_ck_hash_detail("Item to be moved: %p, place in table: %p\n",
+ *next, next);
ck_hash_table_item_t **moving = to_hash;
int loop = 0;
while (*next != NULL) {
- dbg_ck_hash("Swapping items to hash: %p and Moving: %p\n",
- to_hash, moving);
+ dbg_ck_hash_detail("Swapping items: To hash: %p, Moving: %p\n",
+ to_hash, moving);
ck_swap_items(to_hash, moving); // first time it's unnecessary
// set the generation of the inserted item to the next
@@ -575,9 +679,10 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
moving = next;
- dbg_ck_hash("Moving item from table %u, key: %.*s, hash %u ",
- next_table + 1, (int)(*moving)->key_length,
- (*moving)->key, hash);
+ dbg_ck_hash_detail("Moving item from table %u, key: %.*s, hash "
+ "%u \n", next_table + 1,
+ (int)(*moving)->key_length,
+ (*moving)->key, hash);
// if rehashing and the 'next' item is from the old generation,
// start from table 1
@@ -594,31 +699,31 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
next = &table->tables[next_table][hash];
- dbg_ck_hash("to table %u, hash %u, item: %p, place: %p\n",
- next_table + 1, hash, *next, next);
+ dbg_ck_hash_detail("To table %u, hash %u, item: %p, place: %p"
+ "\n", next_table + 1, hash, *next, next);
if ((*next) != NULL) {
- dbg_ck_hash("Table %u, hash: %u, key: %.*s\n",
- next_table + 1, hash,
- (int)(*next)->key_length, (*next)->key);
+ dbg_ck_hash_detail("Table %u, hash: %u, key: %.*s\n",
+ next_table + 1, hash,
+ (int)(*next)->key_length, (*next)->key);
}
// check if this cell wasn't already used in this item's hashing
- if (ck_check_used_twice(&used[next_table], hash) != 0) {
+ if (ck_check_used_twice(&used, next_table, hash) != 0) {
next = free;
loop = -1;
break;
}
}
- dbg_ck_hash("Putting pointer %p (*moving) to item %p (next).\n",
- *moving, next);
+ dbg_ck_hash_detail("Putting pointer %p (*moving) to item %p (next).\n",
+ *moving, next);
ck_put_item(next, *moving);
// set the new generation for the inserted item
SET_GENERATION(&(*next)->timestamp, generation);
- dbg_ck_hash("Putting pointer %p (*old) to item %p (moving).\n",
- *to_hash, moving);
+ dbg_ck_hash_detail("Putting pointer %p (*old) to item %p (moving).\n",
+ *to_hash, moving);
ck_put_item(moving, *to_hash);
@@ -626,9 +731,7 @@ static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
SET_GENERATION(&(*moving)->timestamp, generation);
*to_hash = NULL;
- for (uint i = 0; i < table->table_count; ++i) {
- da_destroy(&used[i]);
- }
+ ck_used_free(&used);
return loop;
}
@@ -670,6 +773,10 @@ static void ck_rollback_rehash(ck_hash_table_t *table)
*/
int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item)
{
+ if (item == NULL) {
+ dbg_stash("[EMPTY STASH] [CREATE] ADDING NULL ITEM TO STASH\n");
+ }
+
ck_stash_item_t *new_item
= (ck_stash_item_t *)malloc(sizeof(ck_stash_item_t));
if (new_item == NULL) {
@@ -681,8 +788,8 @@ int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item)
new_item->next = table->stash;
table->stash = new_item;
- dbg_ck_hash("First item in stash (now inserted): key: %.*s (size %zu)"
- ", value: %p\n", (int)table->stash->item->key_length,
+ dbg_ck_hash_verb("First item in stash (now inserted): key: %.*s (size"
+ " %zu), value: %p\n", (int)table->stash->item->key_length,
table->stash->item->key, table->stash->item->key_length,
table->stash->item->value);
@@ -739,12 +846,12 @@ ck_hash_table_t *ck_create_table(uint items)
dbg_ck("Creating hash table for %u items.\n", items);
dbg_ck("Exponent: %u, number of tables: %u\n ",
- table->table_size_exp, table->table_count);
+ table->table_size_exp, table->table_count);
dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n",
- hashsize(table->table_size_exp),
- sizeof(ck_hash_table_item_t *),
- hashsize(table->table_size_exp)
- * sizeof(ck_hash_table_item_t *));
+ hashsize(table->table_size_exp),
+ sizeof(ck_hash_table_item_t *),
+ hashsize(table->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
// create tables
for (uint t = 0; t < table->table_count; ++t) {
@@ -805,18 +912,6 @@ void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value),
}
// destroy items in stash
-// ck_hash_table_item_t **stash =
-// ((ck_hash_table_item_t **)(da_get_items(&(*table)->stash)));
-// for (uint i = 0; i < da_get_count(&(*table)->stash); ++i) {
-// assert(stash[i] != NULL);
-// if (dtor_value) {
-// dtor_value(stash[i]->value);
-// }
-// if (delete_key != 0) {
-// free((void *)stash[i]->key);
-// }
-// free((void *)stash[i]);
-// }
ck_stash_item_t *item = (*table)->stash;
while (item != NULL) {
// disconnect the item
@@ -842,8 +937,6 @@ void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value),
for (uint t = 0; t < (*table)->table_count; ++t) {
free((*table)->tables[t]);
}
- // destroy stash
-// da_destroy(&(*table)->stash);
pthread_mutex_unlock(&(*table)->mtx_table);
// destroy mutex, assuming that here noone will lock the mutex again
@@ -884,7 +977,7 @@ void ck_table_free(ck_hash_table_t **table)
int ck_resize_table(ck_hash_table_t *table)
{
dbg_ck("Resizing hash table.\n");
-
+
/*
* Easiest is just to increment the exponent, resulting in doubling
* the table sizes. This is not very memory-effective, but should do
@@ -900,7 +993,7 @@ int ck_resize_table(ck_hash_table_t *table)
ck_hash_table_item_t **tables_old[MAX_TABLES];
int exp_new = table->table_size_exp + 1;
- dbg_ck("New tables exponent: %d\n", exp_new);
+ dbg_ck_verb("New tables exponent: %d\n", exp_new);
for (int t = 0; t < table->table_count; ++t) {
if (ck_new_table(&tables_new[t], exp_new) != 0) {
@@ -919,14 +1012,14 @@ int ck_resize_table(ck_hash_table_t *table)
* sizeof(ck_hash_table_item_t *);
// copy the old table items
- dbg_ck("Copying to: %p, from %p, size: %zu\n",
- tables_new[t], table->tables[t], old_size);
+ dbg_ck_verb("Copying to: %p, from %p, size: %zu\n",
+ tables_new[t], table->tables[t], old_size);
memcpy(tables_new[t], table->tables[t], old_size);
// set the rest to 0
- dbg_ck("Setting to 0 from %p, size %zu\n",
- tables_new[t] + hashsize(table->table_size_exp),
- (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
- - old_size);
+ dbg_ck_verb("Setting to 0 from %p, size %zu\n",
+ tables_new[t] + hashsize(table->table_size_exp),
+ (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
+ - old_size);
memset(tables_new[t] + hashsize(table->table_size_exp), 0,
(hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
- old_size);
@@ -948,7 +1041,6 @@ int ck_resize_table(ck_hash_table_t *table)
}
return ck_rehash(table);
- //return 0;
}
int ck_insert_item(ck_hash_table_t *table, const char *key,
@@ -980,9 +1072,6 @@ int ck_insert_item(ck_hash_table_t *table, const char *key,
}
}
- // there should be at least 2 free places
- //assert(da_try_reserve(&table->stash, 2) == 0);
- //da_reserve(&table->stash, 1);
ck_hash_table_item_t *free_place = NULL;
if (ck_hash_item(table, &new_item, &free_place,
table->generation) != 0) {
@@ -1104,6 +1193,8 @@ ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key,
int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
{
+ dbg_ck("ck_shallow_copy()\n");
+
if (from == NULL || to == NULL) {
return -1;
}
@@ -1176,26 +1267,27 @@ int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
return -2;
}
- dbg_ck("Copying stash item: %p with item %p, ", si, si->item);
- dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+ dbg_ck_detail("Copying stash item: %p with item %p, key: %.*s"
+ "\n", si, si->item, (int)si->item->key_length,
+ si->item->key);
si_new->item = si->item;
*pos = si_new;
pos = &si_new->next;
si = si->next;
-
- dbg_ck("Old stash item: %p with item %p, ", si,
- ((si == NULL) ? NULL : si->item));
+dbg_ck_exec_detail(
+ dbg_ck_detail("Old stash item: %p with item %p, \n", si,
+ ((si == NULL) ? NULL : si->item));
if (si != NULL) {
- dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
- } else {
- dbg_ck("\n");
+ dbg_ck_detail("key: %.*s\n", (int)si->item->key_length,
+ si->item->key);
}
- dbg_ck("New stash item: %p with item %p, ", si_new,
- si_new->item);
- dbg_ck("key: %.*s\n", (int)si_new->item->key_length,
- si_new->item->key);
+ dbg_ck_detail("New stash item: %p with item %p, ", si_new,
+ si_new->item);
+ dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length,
+ si_new->item->key);
+);
}
*pos = NULL;
@@ -1273,11 +1365,12 @@ void ck_deep_copy_cleanup(ck_hash_table_t *table, int table_count)
int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to)
{
+ dbg_ck("ck_deep_copy()\n");
+
if (from == NULL || to == NULL) {
return -1;
}
- dbg_ck("Allocating new table...\n");
*to = (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t));
if (*to == NULL) {
@@ -1341,10 +1434,12 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to)
return -2;
}
- dbg_ck("Copying stash item: %p with item %p, ", si, si->item);
-// dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+ dbg_ck_detail("Copying stash item: %p with item %p, ", si,
+ si->item);
if (si->item == NULL) {
+ dbg_stash("[EMPTY STASH] [FIND] STASH ITEM IS EMPTY: "
+ "%p (%p)\n", si, si->item);
si_new->item = NULL;
si_new->next = NULL;
} else {
@@ -1367,22 +1462,20 @@ int ck_deep_copy(ck_hash_table_t *from, ck_hash_table_t **to)
pos = &si_new->next;
si = si->next;
-dbg_ck_exec(
- dbg_ck("Old stash item: %p with item %p, ", si,
- ((si == NULL) ? NULL : si->item));
+dbg_ck_exec_detail(
+ dbg_ck_detail("Old stash item: %p with item %p, \n", si,
+ ((si == NULL) ? NULL : si->item));
if (si != NULL && si->item != NULL) {
- dbg_ck("key: %.*s\n", (int)si->item->key_length,
- si->item->key);
- } else {
- dbg_ck("\n");
+ dbg_ck_detail("key: %.*s\n", (int)si->item->key_length,
+ si->item->key);
}
- dbg_ck("New stash item: %p with item %p, ", si_new,
- (si_new) ? si_new->item : NULL);
+ dbg_ck_detail("New stash item: %p with item %p, ", si_new,
+ (si_new) ? si_new->item : NULL);
assert(si_new != NULL);
assert(si_new->item != NULL);
- dbg_ck("key: %.*s\n", (int)si_new->item->key_length,
- si_new->item->key);
+ dbg_ck_detail("key: %.*s\n", (int)si_new->item->key_length,
+ si_new->item->key);
);
}
@@ -1446,13 +1539,13 @@ int ck_rehash(ck_hash_table_t *table)
do {
// 1) Rehash items from stash
- dbg_ck_rehash("Rehashing items from stash.\n");
+ dbg_ck_hash_verb("Rehashing items from stash.\n");
ck_stash_item_t *item = table->stash;
ck_stash_item_t **item_place = &table->stash;
// terminate when at the end; this way the newly added items
// (added to the beginning) will be properly ignored
while (item != NULL) {
- dbg_ck_rehash("Rehashing item with "
+ dbg_ck_hash_detail("Rehashing item with "
"key (length %zu): %.*s, generation: %hu, "
"table generation: %hu.\n", item->item->key_length,
(int)item->item->key_length, item->item->key,
@@ -1481,6 +1574,8 @@ int ck_rehash(ck_hash_table_t *table)
assert(item->item == NULL);
// and the item should be hashed too
// assert(table->hashed == NULL);
+ dbg_stash("[EMPTY STASH] [CREATE] Created empty"
+ " item: %p (%p)\n", item, item->item);
// fix the pointer from the previous hash item
*item_place = item->next;
@@ -1501,12 +1596,11 @@ int ck_rehash(ck_hash_table_t *table)
// which will be put to the stash
ck_hash_table_item_t *free = NULL;
assert(table->hashed == NULL);
-// ck_hash_table_item_t *old = table->hashed;
for (uint t = 0; t < table->table_count; ++t) {
uint rehashed = 0;
- dbg_ck_rehash("Rehashing table %d.\n", t);
+ dbg_ck_hash_verb("Rehashing table %d.\n", t);
while (rehashed < hashsize(table->table_size_exp)) {
@@ -1516,13 +1610,13 @@ int ck_rehash(ck_hash_table_t *table)
|| !(EQUAL_GENERATIONS(
table->tables[t][rehashed]->timestamp,
table->generation))) {
- dbg_ck_rehash("Skipping item.\n");
+ dbg_ck_hash_detail("Skipping item.\n");
++rehashed;
continue;
}
- dbg_ck_rehash("Rehashing item with hash %u, "
- "key (length %zu): %.*s, generation: %hu, "
+ dbg_ck_hash_detail("Rehashing item with hash %u"
+ ", key (length %zu): %.*s, generation: %hu, "
"table generation: %hu.\n", rehashed,
table->tables[t][rehashed]->key_length,
(int)(table->tables[t][rehashed]->key_length),
@@ -1537,8 +1631,8 @@ int ck_rehash(ck_hash_table_t *table)
// get rehashed again
ck_clear_item(&table->tables[t][rehashed]);
- dbg_ck_rehash("Table generation: %hu, next "
- "generation: %hu.\n",
+ dbg_ck_hash_detail("Table generation: %hu, next"
+ " generation: %hu.\n",
GET_GENERATION(table->generation),
NEXT_GENERATION(table->generation));
@@ -1546,8 +1640,8 @@ int ck_rehash(ck_hash_table_t *table)
NEXT_GENERATION(table->generation)) != 0) {
// loop occured
dbg_ck_hash("Hashing entered a loop."
- "\n");
- dbg_ck_rehash("Item with key %.*s "
+ "\n");
+ dbg_ck_hash_verb("Item with key %.*s "
"inserted into the free slot.\n",
free->key_length, free->key);
@@ -1561,6 +1655,12 @@ int ck_rehash(ck_hash_table_t *table)
free_stash_items;
free_stash_items = item->next;
+ if (free == NULL) {
+ dbg_stash("[EMPTY STASH] "
+ "[CREATE] STORING NULL"
+ " in the stash\n");
+ }
+
item->item = free;
item->next = table->stash;
table->stash = item;
@@ -1579,15 +1679,15 @@ int ck_rehash(ck_hash_table_t *table)
}
}
- dbg_ck_rehash("Old table generation: %u\n",
- GET_GENERATION(table->generation));
+ dbg_ck_hash("Old table generation: %u\n",
+ GET_GENERATION(table->generation));
// rehashing completed, switch generation of the table
SET_NEXT_GENERATION(&table->generation);
- dbg_ck_rehash("New table generation: %u\n",
- GET_GENERATION(table->generation));
+ dbg_ck_hash("New table generation: %u\n",
+ GET_GENERATION(table->generation));
// generate new hash functions for the old generation
- dbg_ck_rehash("Generating coeficients for generation: %u\n",
- NEXT_GENERATION(table->generation));
+ dbg_ck_hash("Generating coeficients for generation: %u\n",
+ NEXT_GENERATION(table->generation));
us_next(&table->hash_system,
NEXT_GENERATION(table->generation));
@@ -1609,248 +1709,6 @@ int ck_rehash(ck_hash_table_t *table)
}
/*----------------------------------------------------------------------------*/
-/*!
- * \brief Rehashes the whole table.
- *
- * \param table Hash table to be rehashed.
- *
- * \note While rehashing no item should be inserted as it will result in a
- * deadlock.
- *
- * \retval 0 No error.
- * \retval -1 Rehashing failed. Some items may have been already moved and the
- * rehashing flag remains set.
- *
- * \todo What if the stash is reallocated during ck_hash_item()? We'd be using
- * the old stash for saving items! The old stash would not get deallocated
- * (due to RCU - maybe put some rcu_read_lock() here), but the item
- * would not be saved into the new stash!
- * Maybe add a function for getting a pointer to particular item from
- * the dynamic array and protect it using rcu_read_lock().
- * Other option: Do not use pointer to an item in stash in the call to
- * ck_hash_item(). Use some new place & put the item to the stash
- * afterwards, protecting it using rcu_read_lock() and rcu_assign_pointer.
- */
-//int ck_rehash(ck_hash_table_t *table)
-//{
-// dbg_ck_rehash("Rehashing items in table.\n");
-// SET_REHASHING_ON(&table->generation);
-
-// // we already have functions for the next generation, begin rehashing
-// // we wil use the last item in the buffer as free cell for hashing
-// assert(da_try_reserve(&table->stash, 1) == 0);
-// ck_hash_table_item_t *old = (ck_hash_table_item_t *)
-// (malloc(sizeof(ck_hash_table_item_t)));
-
-// do {
-// dbg_ck_hash("Rehash!\n");
-
-// if (da_get_count(&table->stash) > STASH_SIZE) {
-// dbg_ck_hash("STASH RESIZED!!! (new stash size: %d)\n",
-// da_get_count(&table->stash));
-// }
-
-// // rehash items from stash, starting from the last old item
-// int stash_i = da_get_count(&table->stash) - 1;
-// while (stash_i >= 0) {
-// // if item's generation is the new generation, skip
-// if (STASH_ITEMS(&table->stash)[stash_i] == NULL
-// || !(EQUAL_GENERATIONS(STASH_ITEMS(&table->stash)
-// [stash_i]->timestamp,
-// table->generation))) {
-// dbg_ck_rehash("Skipping item.\n");
-// --stash_i;
-// continue;
-// }
-
-// dbg_ck_rehash("Rehashing item from buffer position %u"
-// ", key (length %u): %.*s, generation: "
-// "%hu, table generation: %hu.\n",
-// stash_i,
-// STASH_ITEMS(&table->stash)[stash_i]->key_length,
-// (int)STASH_ITEMS(&table->stash)[stash_i]->key_length,
-// STASH_ITEMS(&table->stash)[stash_i]->key,
-// GET_GENERATION(
-// STASH_ITEMS(&table->stash)[stash_i]->timestamp),
-// GET_GENERATION(table->generation));
-
-// // otherwise copy the item for rehashing
-// ck_put_item(&old, STASH_ITEMS(&table->stash)[stash_i]);
-// // clear the place so that this item will not get
-// // rehashed again
-// ck_clear_item(&STASH_ITEMS(&table->stash)[stash_i]);
-// da_release(&table->stash, 1);
-
-// // there should be at least one place in the stash
-// assert(da_try_reserve(&table->stash, 1) == 0);
-// da_reserve(&table->stash, 1);
-
-// assert(STASH_ITEMS(&table->stash)[stash_i] == NULL);
-
-// // and start rehashing
-// if (ck_hash_item(table, &old,
-// &STASH_ITEMS(&table->stash)[stash_i],
-// NEXT_GENERATION(table->generation)) != 0) {
-// // loop occured
-// dbg_ck_hash("Hashing entered a loop.\n");
-
-// dbg_ck_rehash("Item with key %.*s inserted "
-// "into the stash on position %d.\n",
-// STASH_ITEMS(&table->stash)
-// [stash_i]->key_length,
-// STASH_ITEMS(&table->stash)
-// [stash_i]->key,
-// da_get_count(&table->stash));
-
-// // hashing unsuccessful, the item was inserted
-// // into the stash
-// da_occupy(&table->stash, 1);
-// assert(STASH_ITEMS(&table->stash)[stash_i]
-// != NULL);
-
-// // if only one place left, resize the stash
-// // TODO: Why???
-// if (da_reserve(&table->stash, 2) < 0) {
-// // stash could not be resized => !!!
-// dbg_ck_hash("Failed to rehash items "
-// "from "
-// "table, no other rehash possible!\n");
-// // so rollback
-// ck_rollback_rehash(table);
-// // clear the 'old' item
-// ck_clear_item(&old);
-// return -1;
-// }
-// }
-
-// // clear the 'old' item
-// ck_clear_item(&old);
-// // decrement the index
-// --stash_i;
-// }
-
-// uint i = 0;
-// while (i < da_get_count(&table->stash)) {
-// assert(STASH_ITEMS(&table->stash)[i] != NULL);
-// ++i;
-// }
-// dbg_ck_hash("OK\n");
-// assert(da_try_reserve(&table->stash, 1) == 0);
-// assert(STASH_ITEMS(&table->stash)[da_get_count(&table->stash)]
-// == NULL);
-
-// // rehash items from hash tables
-// for (uint t = TABLE_FIRST;
-// t <= TABLE_LAST(table->table_count); ++t) {
-// dbg_ck_rehash("Rehashing items from table %d.\n",
-// t + 1);
-// uint rehashed = 0;
-
-// while (rehashed < hashsize(table->table_size_exp)) {
-
-// // if item's generation is the new generation,
-// // skip
-// if (table->tables[t][rehashed] == NULL
-// || !(EQUAL_GENERATIONS(
-// table->tables[t][rehashed]->timestamp,
-// table->generation))) {
-// dbg_ck_rehash("Skipping item.\n");
-// ++rehashed;
-// continue;
-// }
-
-// dbg_ck_rehash("Rehashing item with hash %u, "
-// "key (length %u): %.*s, generation: %hu, "
-// "table generation: %hu.\n", rehashed,
-// table->tables[t][rehashed]->key_length,
-// (int)(table->tables[t][rehashed]->key_length),
-// table->tables[t][rehashed]->key,
-// GET_GENERATION(
-// table->tables[t][rehashed]->timestamp),
-// GET_GENERATION(table->generation));
-
-// // otherwise copy the item for rehashing
-// ck_put_item(&old, table->tables[t][rehashed]);
-// // clear the place so that this item will not
-// // get rehashed again
-// ck_clear_item(&table->tables[t][rehashed]);
-
-// dbg_ck_rehash("Table generation: %hu, next "
-// "generation: %hu.\n",
-// GET_GENERATION(table->generation),
-// NEXT_GENERATION(table->generation));
-
-// // and start rehashing
-// assert(&old != &STASH_ITEMS(&table->stash)[
-// da_get_count(&table->stash)]);
-// assert(da_try_reserve(&table->stash, 1) == 0);
-// da_reserve(&table->stash, 1);
-
-// if (ck_hash_item(table, &old,
-// &STASH_ITEMS(&table->stash)[
-// da_get_count(&table->stash)],
-// NEXT_GENERATION(table->generation)) != 0) {
-// // loop occured
-// dbg_ck_hash("Hashing entered a loop."
-// "\n");
-// dbg_ck_rehash("Item with key %.*s "
-// "inserted into the stash on position "
-// "%d.\n", STASH_ITEMS(&table->stash)[
-// da_get_count(&table->stash)]
-// ->key_length,
-// STASH_ITEMS(&table->stash)[
-// da_get_count(&table->stash)]->key,
-// da_get_count(&table->stash));
-
-// assert(STASH_ITEMS(&table->stash)[
-// da_get_count(&table->stash)] != NULL);
-// // loop occured, the item is already at
-// // its new place in the buffer, so just
-// // increment the index
-// da_occupy(&table->stash, 1);
-
-// // if only one place left, resize the
-// // stash TODO: Why?
-// if (da_reserve(&table->stash, 2) < 0) {
-// // stash could not be resized
-// dbg_ck_hash("Failed to rehash"
-// " items from table, no other "
-// "rehash possible!\n");
-// // so rollback
-// ck_rollback_rehash(table);
-// // clear the 'old' item
-// ck_clear_item(&old);
-// return -1;
-// }
-// }
-// ++rehashed;
-// }
-// }
-
-// dbg_ck_rehash("Old table generation: %u\n",
-// GET_GENERATION(table->generation));
-// // rehashing completed, switch generation of the table
-// SET_NEXT_GENERATION(&table->generation);
-// dbg_ck_rehash("New table generation: %u\n",
-// GET_GENERATION(table->generation));
-// // generate new hash functions for the old generation
-// dbg_ck_rehash("Generating coeficients for generation: %u\n",
-// NEXT_GENERATION(table->generation));
-// us_next(NEXT_GENERATION(table->generation));
-
-// // repeat rehashing while there are more items in the stash than
-// // its initial size
-// if (da_get_count(&table->stash) > STASH_SIZE) {
-// dbg_ck_rehash("Rehashing again!\n");
-// }
-// } while (da_get_count(&table->stash) > STASH_SIZE);
-
-// SET_REHASHING_OFF(&table->generation);
-
-// return 0;
-//}
-
-/*----------------------------------------------------------------------------*/
void ck_dump_table(const ck_hash_table_t *table)
{
@@ -1872,15 +1730,6 @@ void ck_dump_table(const ck_hash_table_t *table)
}
dbg_ck("Stash:\n");
-// for (i = 0; i < da_get_count(&table->stash); ++i) {
-// dbg_ck("Index: %u, Key: %.*s Value: %p.\n", i,
-// ((ck_hash_table_item_t **)
-// da_get_items(&table->stash))[i]->key_length,
-// ((ck_hash_table_item_t **)
-// da_get_items(&table->stash))[i]->key,
-// ((ck_hash_table_item_t **)
-// da_get_items(&table->stash))[i]->value);
-// }
ck_stash_item_t *item = table->stash;
while (item != NULL) {
dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i,
diff --git a/src/libknot/hash/cuckoo-hash-table.h b/src/libknot/hash/cuckoo-hash-table.h
index eaa6a89..c0fe9cc 100644..100755
--- a/src/libknot/hash/cuckoo-hash-table.h
+++ b/src/libknot/hash/cuckoo-hash-table.h
@@ -42,7 +42,6 @@
#include <pthread.h>
#include "hash/universal-system.h"
-#include "common/dynamic-array.h"
/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/hash/hash-functions.c b/src/libknot/hash/hash-functions.c
index a33dd6b..a33dd6b 100644..100755
--- a/src/libknot/hash/hash-functions.c
+++ b/src/libknot/hash/hash-functions.c
diff --git a/src/libknot/hash/hash-functions.h b/src/libknot/hash/hash-functions.h
index f23730b..f23730b 100644..100755
--- a/src/libknot/hash/hash-functions.h
+++ b/src/libknot/hash/hash-functions.h
diff --git a/src/libknot/hash/universal-system.c b/src/libknot/hash/universal-system.c
index 096974c..096974c 100644..100755
--- a/src/libknot/hash/universal-system.c
+++ b/src/libknot/hash/universal-system.c
diff --git a/src/libknot/hash/universal-system.h b/src/libknot/hash/universal-system.h
index 25330de..25330de 100644..100755
--- a/src/libknot/hash/universal-system.h
+++ b/src/libknot/hash/universal-system.h
diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h
index a401be7..a401be7 100644..100755
--- a/src/libknot/libknot.h
+++ b/src/libknot/libknot.h
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 7a6bc4d..6924f44 100644..100755
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -126,10 +126,9 @@ static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb,
static knot_rrset_t *ns_synth_from_wildcard(
const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname)
{
- dbg_ns("Synthetizing RRSet from wildcard...\n");
+ dbg_ns_verb("Synthetizing RRSet from wildcard...\n");
knot_dname_t *owner = knot_dname_deep_copy(qname);
-// printf("Copied owner ptr: %p\n", owner);
knot_rrset_t *synth_rrset = knot_rrset_new(
owner, knot_rrset_type(wildcard_rrset),
@@ -143,7 +142,7 @@ static knot_rrset_t *ns_synth_from_wildcard(
return NULL;
}
- dbg_ns("Created RRSet header:\n");
+ dbg_ns_verb("Created RRSet header:\n");
knot_rrset_dump(synth_rrset, 1);
// copy all RDATA
@@ -159,15 +158,15 @@ static knot_rrset_t *ns_synth_from_wildcard(
return NULL;
}
- dbg_ns("Copied RDATA:\n");
+ dbg_ns_verb("Copied RDATA:\n");
knot_rdata_dump(rdata_copy,
knot_rrset_type(synth_rrset), 1);
- knot_rrset_add_rdata(synth_rrset, rdata_copy);
+ int ret = knot_rrset_add_rdata(synth_rrset, rdata_copy);
+ assert(ret == KNOT_EOK);
rdata = knot_rrset_rdata_next(wildcard_rrset, rdata);
}
-// printf("Synthetized RRSet pointer: %p\n", synth_rrset);
return synth_rrset;
}
@@ -182,8 +181,8 @@ static knot_rrset_t *ns_synth_from_wildcard(
* temporary RRSet).
* \param rrset RRSet to check (and possibly replace).
*/
-static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
- knot_rrset_t **rrset)
+static int ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
+ knot_rrset_t **rrset)
{
assert(name != NULL);
assert(resp != NULL);
@@ -193,11 +192,26 @@ static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
if (knot_dname_is_wildcard((*rrset)->owner)) {
knot_rrset_t *synth_rrset =
ns_synth_from_wildcard(*rrset, name);
- dbg_ns("Synthetized RRSet:\n");
+ if (synth_rrset == NULL) {
+ dbg_ns("Failed to synthetize RRSet from wildcard.\n");
+ return KNOT_ERROR;
+ }
+
+dbg_ns_exec_verb(
+ dbg_ns_verb("Synthetized RRSet:\n");
knot_rrset_dump(synth_rrset, 1);
- knot_packet_add_tmp_rrset(resp, synth_rrset);
+);
+
+ int ret = knot_packet_add_tmp_rrset(resp, synth_rrset);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add sythetized RRSet to tmp list.\n");
+ knot_rrset_deep_free(&synth_rrset, 1, 1, 1);
+ return ret;
+ }
*rrset = synth_rrset;
}
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -230,23 +244,28 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp,
{
knot_rrset_t *rrsigs;
- dbg_ns("Adding RRSIGs for RRSet, type: %s.\n",
- knot_rrtype_to_string(knot_rrset_type(rrset)));
+ dbg_ns_verb("Adding RRSIGs for RRSet, type: %s.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
assert(resp != NULL);
assert(add_rrset_to_resp != NULL);
- dbg_ns("DNSSEC requested: %d\n",
- knot_query_dnssec_requested(knot_packet_query(resp)));
- dbg_ns("RRSIGS: %p\n", knot_rrset_rrsigs(rrset));
+ dbg_ns_detail("DNSSEC requested: %d\n",
+ knot_query_dnssec_requested(knot_packet_query(resp)));
+ dbg_ns_detail("RRSIGS: %p\n", knot_rrset_rrsigs(rrset));
if (DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp))
&& (rrsigs = knot_rrset_get_rrsigs(rrset)) != NULL) {
if (name != NULL) {
- ns_check_wildcard(name, resp, &rrsigs);
+ int ret = ns_check_wildcard(name, resp, &rrsigs);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
}
- return add_rrset_to_resp(resp, rrsigs, tc, 0, 0, 1);
+ return add_rrset_to_resp(resp, rrsigs, tc, 1, 0, 1);
}
return KNOT_EOK;
@@ -265,7 +284,7 @@ static int ns_add_rrsigs(knot_rrset_t *rrset, knot_packet_t *resp,
* \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the
* TC bit in the response.
*/
-static void ns_follow_cname(const knot_node_t **node,
+static int ns_follow_cname(const knot_node_t **node,
const knot_dname_t **qname,
knot_packet_t *resp,
int (*add_rrset_to_resp)(knot_packet_t *,
@@ -273,19 +292,22 @@ static void ns_follow_cname(const knot_node_t **node,
int, int, int, int),
int tc)
{
- dbg_ns("Resolving CNAME chain...\n");
+ dbg_ns_verb("Resolving CNAME chain...\n");
knot_rrset_t *cname_rrset;
+ int ret = 0;
+
while (*node != NULL
&& (cname_rrset = knot_node_get_rrset(*node, KNOT_RRTYPE_CNAME))
- != NULL) {
+ != NULL
+ && (knot_rrset_rdata(cname_rrset) != NULL)) {
/* put the CNAME record to answer, but replace the possible
wildcard name with qname */
assert(cname_rrset != NULL);
- dbg_ns("CNAME RRSet: %p, owner: %p\n", cname_rrset,
- cname_rrset->owner);
+ dbg_ns_detail("CNAME RRSet: %p, owner: %p\n", cname_rrset,
+ cname_rrset->owner);
knot_rrset_t *rrset = cname_rrset;
@@ -294,27 +316,67 @@ static void ns_follow_cname(const knot_node_t **node,
/* if wildcard node, we must copy the RRSet and
replace its owner */
rrset = ns_synth_from_wildcard(cname_rrset, *qname);
- knot_packet_add_tmp_rrset(resp, rrset);
- add_rrset_to_resp(resp, rrset, tc, 0, 0, 1);
- ns_add_rrsigs(cname_rrset, resp, *qname,
- add_rrset_to_resp, tc);
+ if (rrset == NULL) {
+ dbg_ns("Failed to synthetize RRSet from "
+ "wildcard RRSet followed from CNAME.\n");
+ return KNOT_ERROR; /*! \todo Better error. */
+ }
+
+ ret = knot_packet_add_tmp_rrset(resp, rrset);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add synthetized RRSet (CNAME "
+ "follow) to the tmp RRSets in response."
+ "\n");
+ knot_rrset_deep_free(&rrset, 1, 1, 1);
+ return ret;
+ }
+
+ ret = add_rrset_to_resp(resp, rrset, tc, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add synthetized RRSet (CNAME "
+ "follow) to the response.\n");
+ return ret;
+ }
+
+ ret = ns_add_rrsigs(cname_rrset, resp, *qname,
+ add_rrset_to_resp, tc);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIG for the synthetized"
+ "RRSet (CNAME follow) to the response."
+ "\n");
+ return ret;
+ }
int ret = knot_response_add_wildcard_node(
resp, *node, *qname);
-
- /*! \todo Fix when return values are handled! */
if (ret != KNOT_EOK) {
- assert(0);
+ dbg_ns("Failed to add wildcard node for later "
+ "processing.\n");
+ return ret;
}
} else {
- add_rrset_to_resp(resp, rrset, tc, 0, 0, 1);
- ns_add_rrsigs(rrset, resp, *qname, add_rrset_to_resp,
- tc);
+ ret = add_rrset_to_resp(resp, rrset, tc, 0, 0, 1);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add followed RRSet into"
+ "the response.\n");
+ return ret;
+ }
+
+ ret = ns_add_rrsigs(rrset, resp, *qname,
+ add_rrset_to_resp, tc);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIG for followed RRSet "
+ "into the response.\n");
+ return ret;
+ }
}
- dbg_ns("Using RRSet: %p, owner: %p\n", rrset, rrset->owner);
+ dbg_ns_detail("Using RRSet: %p, owner: %p\n", rrset,
+ rrset->owner);
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_rrset_owner(rrset));
dbg_ns("CNAME record for owner %s put to response.\n", name);
free(name);
@@ -323,18 +385,16 @@ dbg_ns_exec(
// get the name from the CNAME RDATA
const knot_dname_t *cname = knot_rdata_cname_name(
knot_rrset_rdata(cname_rrset));
- dbg_ns("CNAME name from RDATA: %p\n", cname);
+ dbg_ns_detail("CNAME name from RDATA: %p\n", cname);
// change the node to the node of that name
*node = knot_dname_node(cname);
- dbg_ns("This name's node: %p\n", *node);
-// // it is not an old node and if yes, skip it
-// if (knot_node_is_old(*node)) {
-// *node = knot_node_new_node(*node);
-// }
+ dbg_ns_detail("This name's node: %p\n", *node);
// save the new name which should be used for replacing wildcard
*qname = cname;
- };
+ }
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -350,47 +410,70 @@ dbg_ns_exec(
*
* \return Number of RRSets added.
*/
-static int ns_put_answer(const knot_node_t *node, const knot_dname_t *name,
- uint16_t type, knot_packet_t *resp)
+static int ns_put_answer(const knot_node_t *node,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ uint16_t type, knot_packet_t *resp, int *added,
+ int check_any)
{
- int added = 0;
-dbg_ns_exec(
+ *added = 0;
+dbg_ns_exec_verb(
char *name_str = knot_dname_to_str(node->owner);
- dbg_ns("Putting answers from node %s.\n", name_str);
+ dbg_ns_verb("Putting answers from node %s.\n", name_str);
free(name_str);
);
+ int ret = KNOT_EOK;
+
switch (type) {
case KNOT_RRTYPE_ANY: {
- dbg_ns("Returning all RRTYPES.\n");
+ dbg_ns_verb("Returning all RRTYPES.\n");
+
+ // if ANY not allowed, set TC bit
+ if (check_any && knot_zone_contents_any_disabled(zone)) {
+ knot_response_set_tc(resp);
+ break;
+ }
+
knot_rrset_t **rrsets = knot_node_get_rrsets(node);
if (rrsets == NULL) {
break;
}
int i = 0;
- int ret = 0;
knot_rrset_t *rrset;
while (i < knot_node_rrset_count(node)) {
assert(rrsets[i] != NULL);
rrset = rrsets[i];
- dbg_ns(" Type: %s\n",
+ dbg_ns_detail(" Type: %s\n",
knot_rrtype_to_string(knot_rrset_type(rrset)));
- ns_check_wildcard(name, resp, &rrset);
+ ret = ns_check_wildcard(name, resp, &rrset);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard.\n");
+ break;
+ }
+
ret = knot_response_add_rrset_answer(resp, rrset, 1,
0, 0, 1);
- if (ret >= 0 && (added += 1)
- && (ret = ns_add_rrsigs(rrset, resp, name,
- knot_response_add_rrset_answer, 1))
- >=0 ) {
- added += 1;
- } else {
- free(rrsets);
- rrsets = NULL;
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed add Answer RRSet: %s\n",
+ knot_strerror(ret));
+ break;
+ }
+
+ *added += 1;
+
+ ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed add RRSIGs for Answer RRSet: %s"
+ "\n", knot_strerror(ret));
break;
}
+ *added += 1;
+
++i;
}
if (rrsets != NULL) {
@@ -399,7 +482,7 @@ dbg_ns_exec(
break;
}
case KNOT_RRTYPE_RRSIG: {
- dbg_ns("Returning all RRSIGs.\n");
+ dbg_ns_verb("Returning all RRSIGs.\n");
knot_rrset_t **rrsets = knot_node_get_rrsets(node);
if (rrsets == NULL) {
break;
@@ -416,15 +499,21 @@ dbg_ns_exec(
continue;
}
- ns_check_wildcard(name, resp, &rrset);
+ ret = ns_check_wildcard(name, resp, &rrset);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard.\n");
+ break;
+ }
+
ret = knot_response_add_rrset_answer(resp, rrset, 1,
0, 0, 1);
-
- if (ret < 0) {
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed add Answer RRSet: %s\n",
+ knot_strerror(ret));
break;
}
- added += 1;
+ *added += 1;
++i;
}
free(rrsets);
@@ -434,23 +523,41 @@ dbg_ns_exec(
int ret = 0;
knot_rrset_t *rrset = knot_node_get_rrset(node, type);
knot_rrset_t *rrset2 = rrset;
- if (rrset != NULL) {
- dbg_ns("Found RRSet of type %s\n",
- knot_rrtype_to_string(type));
- ns_check_wildcard(name, resp, &rrset2);
+ if (rrset != NULL && knot_rrset_rdata(rrset) != NULL) {
+ dbg_ns_verb("Found RRSet of type %s\n",
+ knot_rrtype_to_string(type));
+
+ ret = ns_check_wildcard(name, resp, &rrset2);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard.\n");
+ break;
+ }
+
ret = knot_response_add_rrset_answer(resp, rrset2, 1,
0, 0, 1);
- if (ret >= 0 && (added += 1)
- && (ret = ns_add_rrsigs(rrset, resp, name,
- knot_response_add_rrset_answer, 1)) > 0) {
- added += 1;
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed add Answer RRSet: %s\n",
+ knot_strerror(ret));
+ break;
}
+
+ *added += 1;
+
+ ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed add RRSIGs for Answer RRSet: %s"
+ "\n", knot_strerror(ret));
+ break;
+ }
+
+ *added += 1;
}
}
}
- knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
- return added;
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -471,34 +578,36 @@ dbg_ns_exec(
* \param resp Response where to add the Additional data.
* \param rrset RRSet to get the Additional data for.
*/
-static void ns_put_additional_for_rrset(knot_packet_t *resp,
- const knot_rrset_t *rrset)
+static int ns_put_additional_for_rrset(knot_packet_t *resp,
+ const knot_rrset_t *rrset)
{
const knot_node_t *node = NULL;
const knot_rdata_t *rdata = NULL;
const knot_dname_t *dname = NULL;
+ int ret = 0;
+
// for all RRs in the RRset
rdata = knot_rrset_rdata(rrset);
while (rdata != NULL) {
- dbg_ns("Getting name from RDATA, type %s..\n",
- knot_rrtype_to_string(knot_rrset_type(rrset)));
+ dbg_ns_verb("Getting name from RDATA, type %s..\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
dname = knot_rdata_get_name(rdata, knot_rrset_type(rrset));
+
+dbg_ns_exec_detail(
+ char *name = knot_dname_to_str(dname);
+ dbg_ns_detail("Name: %s\n", name);
+ free(name);
+);
assert(dname != NULL);
node = knot_dname_node(dname);
-// dbg_ns_detail("Node saved in RDATA dname: %p\n", node);
-// char *name = knot_dname_to_str(dname);
-// dbg_ns_detail("Owner of the node: %p, dname: %p (%s)\n",
-// node->owner, dname, name);
-// free(name);
-// knot_node_dump((knot_node_t *)node, (void *)1);
-
+ dbg_ns_detail("Node saved in RDATA dname: %p\n", node);
if (node != NULL && node->owner != dname) {
- // the stored node should be the closest encloser
- assert(knot_dname_is_subdomain(dname, node->owner));
- // try the wildcard child, if any
- node = knot_node_wildcard_child(node);
+ // the stored node should be the wildcard covering the
+ // name
+ dbg_ns_detail("Node is wildcard.\n");
+ assert(knot_dname_is_wildcard(knot_node_owner(node)));
}
knot_rrset_t *rrset_add;
@@ -506,43 +615,90 @@ static void ns_put_additional_for_rrset(knot_packet_t *resp,
if (node != NULL) {
dbg_ns_exec(
char *name = knot_dname_to_str(node->owner);
- dbg_ns("Putting additional from node %s\n", name);
+ dbg_ns_verb("Putting additional from node %s\n", name);
free(name);
);
- dbg_ns("Checking CNAMEs...\n");
- if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)
- != NULL) {
- dbg_ns("Found CNAME in node, following...\n");
+ dbg_ns_detail("Checking CNAMEs...\n");
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+ dbg_ns_detail("Found CNAME in node.\n");
const knot_dname_t *dname
= knot_node_owner(node);
- ns_follow_cname(&node, &dname, resp,
+ ret = ns_follow_cname(&node, &dname, resp,
knot_response_add_rrset_additional, 0);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to follow CNAME.\n");
+ return ret;
+ }
}
// A RRSet
- dbg_ns("A RRSets...\n");
+ dbg_ns_detail("A RRSets...\n");
rrset_add = knot_node_get_rrset(node, KNOT_RRTYPE_A);
if (rrset_add != NULL) {
- dbg_ns("Found A RRsets.\n");
+ dbg_ns_detail("Found A RRsets.\n");
knot_rrset_t *rrset_add2 = rrset_add;
- ns_check_wildcard(dname, resp, &rrset_add2);
- knot_response_add_rrset_additional(
+ ret = ns_check_wildcard(dname, resp,
+ &rrset_add2);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard for"
+ "Additional section: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ ret = knot_response_add_rrset_additional(
resp, rrset_add2, 0, 1, 0, 1);
- ns_add_rrsigs(rrset_add, resp, dname,
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add A RRSet to "
+ "Additional section: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ ret = ns_add_rrsigs(rrset_add, resp, dname,
knot_response_add_rrset_additional, 0);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIGs for A RR"
+ "Set to Additional section: %s."
+ "\n", knot_strerror(ret));
+ return ret;
+ }
}
// AAAA RRSet
- dbg_ns("AAAA RRSets...\n");
+ dbg_ns_detail("AAAA RRSets...\n");
rrset_add = knot_node_get_rrset(node, KNOT_RRTYPE_AAAA);
if (rrset_add != NULL) {
- dbg_ns("Found AAAA RRsets.\n");
+ dbg_ns_detail("Found AAAA RRsets.\n");
knot_rrset_t *rrset_add2 = rrset_add;
- ns_check_wildcard(dname, resp, &rrset_add2);
- knot_response_add_rrset_additional(
+ ret = ns_check_wildcard(dname, resp,
+ &rrset_add2);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to process wildcard for"
+ "Additional section: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ ret = knot_response_add_rrset_additional(
resp, rrset_add2, 0, 1, 0, 1);
- ns_add_rrsigs(rrset_add, resp, dname,
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add AAAA RRSet to "
+ "Additional section.\n");
+ return ret;
+ }
+
+ ret = ns_add_rrsigs(rrset_add, resp, dname,
knot_response_add_rrset_additional, 0);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIG for AAAA RR"
+ "Set to Additional section.\n");
+ return ret;
+ }
}
}
@@ -550,6 +706,8 @@ dbg_ns_exec(
assert(rdata != NULL);
rdata = knot_rrset_rdata_next(rrset, rdata);
}
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -580,26 +738,37 @@ static int ns_additional_needed(uint16_t qtype)
*
* \param resp Response to process.
*/
-static void ns_put_additional(knot_packet_t *resp)
+static int ns_put_additional(knot_packet_t *resp)
{
- dbg_ns("ADDITIONAL SECTION PROCESSING\n");
+ dbg_ns_verb("ADDITIONAL SECTION PROCESSING\n");
const knot_rrset_t *rrset = NULL;
+ int ret = 0;
for (int i = 0; i < knot_packet_answer_rrset_count(resp); ++i) {
rrset = knot_packet_answer_rrset(resp, i);
assert(rrset != NULL);
if (ns_additional_needed(knot_rrset_type(rrset))) {
- ns_put_additional_for_rrset(resp, rrset);
+ ret = ns_put_additional_for_rrset(resp, rrset);
+ if (ret != KNOT_EOK) {
+ // if error, do not try to add other RRSets
+ return ret;
+ }
}
}
for (int i = 0; i < knot_packet_authority_rrset_count(resp); ++i) {
rrset = knot_packet_authority_rrset(resp, i);
if (ns_additional_needed(knot_rrset_type(rrset))) {
- ns_put_additional_for_rrset(resp, rrset);
+ ret = ns_put_additional_for_rrset(resp, rrset);
+ if (ret != KNOT_EOK) {
+ // if error, do not try to add other RRSets
+ return ret;
+ }
}
}
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -609,18 +778,35 @@ static void ns_put_additional(knot_packet_t *resp)
* \param zone Zone to take the authority NS RRSet from.
* \param resp Response where to add the RRSet.
*/
-static void ns_put_authority_ns(const knot_zone_contents_t *zone,
+static int ns_put_authority_ns(const knot_zone_contents_t *zone,
knot_packet_t *resp)
{
+ dbg_ns_verb("PUTTING AUTHORITY NS\n");
+
knot_rrset_t *ns_rrset = knot_node_get_rrset(
knot_zone_contents_apex(zone), KNOT_RRTYPE_NS);
if (ns_rrset != NULL) {
- knot_response_add_rrset_authority(resp, ns_rrset, 0, 1, 0, 1);
- ns_add_rrsigs(ns_rrset, resp, knot_node_owner(
+ int ret = knot_response_add_rrset_authority(resp, ns_rrset, 0,
+ 1, 0, 1);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add Authority NSs to response.\n");
+ return ret;
+ }
+
+ ret = ns_add_rrsigs(ns_rrset, resp, knot_node_owner(
knot_zone_contents_apex(zone)),
- knot_response_add_rrset_authority, 1);
+ knot_response_add_rrset_authority, 1);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIGs for Authority NSs to "
+ "response.\n");
+ return ret;
+ }
}
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -633,6 +819,8 @@ static void ns_put_authority_ns(const knot_zone_contents_t *zone,
static int ns_put_authority_soa(const knot_zone_contents_t *zone,
knot_packet_t *resp)
{
+ dbg_ns_verb("PUTTING AUTHORITY SOA\n");
+
int ret;
knot_rrset_t *soa_rrset = knot_node_get_rrset(
@@ -644,7 +832,7 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone,
uint32_t min = knot_rdata_soa_minimum(knot_rrset_rdata(soa_rrset));
if (min < knot_rrset_ttl(soa_rrset)) {
knot_rrset_t *soa_copy = NULL;
- ret = knot_rrset_deep_copy(soa_rrset, &soa_copy);
+ ret = knot_rrset_deep_copy(soa_rrset, &soa_copy, 0);
if (ret != KNOT_EOK) {
return ret;
@@ -655,7 +843,11 @@ static int ns_put_authority_soa(const knot_zone_contents_t *zone,
knot_rrset_set_ttl(soa_copy, min);
soa_rrset = soa_copy;
/* Need to add it as temporary, so it get's freed. */
- knot_packet_add_tmp_rrset(resp, soa_copy);
+ ret = knot_packet_add_tmp_rrset(resp, soa_copy);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&soa_copy, 1, 1, 1);
+ return ret;
+ }
}
assert(soa_rrset != NULL);
@@ -717,20 +909,36 @@ static knot_dname_t *ns_next_closer(const knot_dname_t *closest_encloser,
* \param node Node to get the NSEC3 RRSet from.
* \param resp Response where to add the RRSets.
*/
-static void ns_put_nsec3_from_node(const knot_node_t *node,
+static int ns_put_nsec3_from_node(const knot_node_t *node,
knot_packet_t *resp)
{
assert(DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp)));
- knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC3);
- assert(rrset != NULL);
+ knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC3);
+ //assert(rrset != NULL);
- int res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0, 1);
+ if (rrset == NULL) {
+ // bad zone, ignore
+ return KNOT_EOK;
+ }
+
+ int res = KNOT_EOK;
+ if (knot_rrset_rdata(rrset) != NULL) {
+ res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0,
+ 1);
+ }
// add RRSIG for the RRSet
- if (res == 0 && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL) {
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (res == KNOT_EOK && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL
+ && knot_rrset_rdata(rrset) != NULL) {
+ res = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0,
+ 1);
}
+
+ /*! \note TC bit is already set, if something went wrong. */
+
+ // return the error code, so that other code may be skipped
+ return res;
}
/*----------------------------------------------------------------------------*/
@@ -753,31 +961,25 @@ static int ns_put_covering_nsec3(const knot_zone_contents_t *zone,
const knot_node_t *prev, *node;
/*! \todo Check version. */
int match = knot_zone_contents_find_nsec3_for_name(zone, name,
- &node, &prev);
- assert(match >= 0);
-// node = knot_node_current(node);
-// prev = knot_node_current(prev);
+ &node, &prev);
+ //assert(match >= 0);
+ if (match < 0) {
+ // ignoring, what can we do anyway?
+ return KNOT_EOK;
+ }
- if (match == KNOT_ZONE_NAME_FOUND){
- // run-time collision => SERVFAIL
+ if (match == KNOT_ZONE_NAME_FOUND || prev == NULL){
+ // if run-time collision => SERVFAIL
return KNOT_EOK;
}
-// // check if the prev node is not old and if yes, take the new one
-// if (knot_node_is_old(prev)) {
-// prev = knot_node_new_node(prev);
-// assert(prev != NULL);
-// }
-
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(prev->owner);
- dbg_ns("Covering NSEC3 node: %s\n", name);
+ dbg_ns_verb("Covering NSEC3 node: %s\n", name);
free(name);
);
- ns_put_nsec3_from_node(prev, resp);
-
- return KNOT_EOK;
+ return ns_put_nsec3_from_node(prev, resp);
}
/*----------------------------------------------------------------------------*/
@@ -812,19 +1014,24 @@ static int ns_put_nsec3_closest_encloser_proof(
assert(qname != NULL);
assert(resp != NULL);
+ // this function should be called only if NSEC3 is enabled in the zone
+ assert(knot_zone_contents_nsec3params(zone) != NULL);
+
+ dbg_ns_verb("Adding closest encloser proof\n");
+
if (knot_zone_contents_nsec3params(zone) == NULL) {
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_node_owner(
knot_zone_contents_apex(zone)));
- dbg_ns("No NSEC3PARAM found in zone %s.\n", name);
+ dbg_ns_verb("No NSEC3PARAM found in zone %s.\n", name);
free(name);
);
return KNOT_EOK;
}
-dbg_ns_exec(
+dbg_ns_exec_detail(
char *name = knot_dname_to_str(knot_node_owner(*closest_encloser));
- dbg_ns("Closest encloser: %s\n", name);
+ dbg_ns_detail("Closest encloser: %s\n", name);
free(name);
);
@@ -845,28 +1052,30 @@ dbg_ns_exec(
assert(nsec3_node != NULL);
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(nsec3_node->owner);
- dbg_ns("NSEC3 node: %s\n", name);
+ dbg_ns_verb("NSEC3 node: %s\n", name);
free(name);
name = knot_dname_to_str((*closest_encloser)->owner);
- dbg_ns("Closest provable encloser: %s\n", name);
+ dbg_ns_verb("Closest provable encloser: %s\n", name);
free(name);
if (next_closer != NULL) {
name = knot_dname_to_str(next_closer);
- dbg_ns("Next closer name: %s\n", name);
+ dbg_ns_verb("Next closer name: %s\n", name);
free(name);
} else {
- dbg_ns("Next closer name: none\n");
+ dbg_ns_verb("Next closer name: none\n");
}
);
- ns_put_nsec3_from_node(nsec3_node, resp);
+ int ret = ns_put_nsec3_from_node(nsec3_node, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
/*
* 2) NSEC3 that covers the "next closer" name.
*/
- int ret = 0;
if (next_closer == NULL) {
// create the "next closer" name by appending from qname
next_closer = ns_next_closer(
@@ -875,9 +1084,9 @@ dbg_ns_exec(
if (next_closer == NULL) {
return NS_ERR_SERVFAIL;
}
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(next_closer);
- dbg_ns("Next closer name: %s\n", name);
+ dbg_ns_verb("Next closer name: %s\n", name);
free(name);
);
ret = ns_put_covering_nsec3(zone, next_closer, resp);
@@ -914,9 +1123,9 @@ static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name)
return NULL;
}
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(wildcard);
- dbg_ns("Wildcard: %s\n", name);
+ dbg_ns_verb("Wildcard: %s\n", name);
free(name);
);
return wildcard;
@@ -971,26 +1180,55 @@ static int ns_put_nsec3_no_wildcard_child(const knot_zone_contents_t *zone,
* RRSets of the requested type).
* \param resp Response where to add the NSECs or NSEC3s.
*/
-static void ns_put_nsec_nsec3_nodata(const knot_node_t *node,
- knot_packet_t *resp)
+static int ns_put_nsec_nsec3_nodata(const knot_zone_contents_t *zone,
+ const knot_node_t *node,
+ knot_packet_t *resp)
{
if (!DNSSEC_ENABLED ||
!knot_query_dnssec_requested(knot_packet_query(resp))) {
- return;
+ return KNOT_EOK;
}
- knot_node_t *nsec3_node = knot_node_get_nsec3_node(node);
+ /*! \todo Maybe distinguish different errors. */
+ int ret = KNOT_ERROR;
+
knot_rrset_t *rrset = NULL;
- if ((rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC)) != NULL
- || (nsec3_node != NULL && (rrset =
- knot_node_get_rrset(nsec3_node, KNOT_RRTYPE_NSEC3)) != NULL)) {
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
- // add RRSIG for the RRSet
- if ((rrset = knot_rrset_get_rrsigs(rrset)) != NULL) {
- knot_response_add_rrset_authority(resp, rrset, 1,
- 0, 0, 1);
+
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ knot_node_t *nsec3_node = knot_node_get_nsec3_node(node);
+ dbg_ns_verb("Adding NSEC3 for NODATA, NSEC3 node: %p\n",
+ nsec3_node);
+
+ if (nsec3_node != NULL
+ && (rrset = knot_node_get_rrset(nsec3_node,
+ KNOT_RRTYPE_NSEC3)) != NULL
+ && knot_rrset_rdata(rrset) != NULL) {
+ dbg_ns_detail("Putting the RRSet to Authority\n");
+ ret = knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0, 1);
}
+ } else {
+ dbg_ns_verb("Adding NSEC for NODATA\n");
+ if ((rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NSEC))
+ != NULL
+ && knot_rrset_rdata(rrset) != NULL) {
+ dbg_ns_detail("Putting the RRSet to Authority\n");
+ ret = knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0, 1);
+ }
+ }
+
+ if (ret != KNOT_EOK) {
+ return ret;
}
+
+ dbg_ns_detail("Putting RRSet's RRSIGs to Authority\n");
+ if (rrset != NULL && (rrset = knot_rrset_get_rrsigs(rrset)) != NULL) {
+ ret = knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0, 1);
+ }
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1030,9 +1268,11 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
}
}
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(previous->owner);
- dbg_ns("Previous node: %s\n", name);
+ dbg_ns_verb("Previous node: %s\n", name);
free(name);
+);
// 1) NSEC proving that there is no node with the searched name
rrset = knot_node_get_rrset(previous, KNOT_RRTYPE_NSEC);
@@ -1043,11 +1283,21 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
}
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ int ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add NSEC for NXDOMAIN to response: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
rrset = knot_rrset_get_rrsigs(rrset);
assert(rrset != NULL);
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
-
+ ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIGs for NSEC for NXDOMAIN to response:"
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
// 2) NSEC proving that there is no wildcard covering the name
// this is only different from 1) if the wildcard would be
// before 'previous' in canonical order, i.e. we can
@@ -1064,9 +1314,9 @@ static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
while (knot_dname_compare(knot_node_owner(prev_new),
wildcard) > 0) {
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_node_owner(prev_new));
- dbg_ns("Previous node: %s\n", name);
+ dbg_ns_verb("Previous node: %s\n", name);
free(name);
);
assert(prev_new != knot_zone_contents_apex(zone));
@@ -1075,9 +1325,9 @@ dbg_ns_exec(
assert(knot_dname_compare(knot_node_owner(prev_new),
wildcard) < 0);
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_node_owner(prev_new));
- dbg_ns("Previous node: %s\n", name);
+ dbg_ns_verb("Previous node: %s\n", name);
free(name);
);
@@ -1086,11 +1336,27 @@ dbg_ns_exec(
if (prev_new != previous) {
rrset = knot_node_get_rrset(prev_new, KNOT_RRTYPE_NSEC);
- assert(rrset != NULL);
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (rrset == NULL || knot_rrset_rdata(rrset) == NULL) {
+ // bad zone, ignore
+ return KNOT_EOK;
+ }
+ ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add second NSEC for NXDOMAIN to "
+ "response: %s\n", knot_strerror(ret));
+ return ret;
+ }
rrset = knot_rrset_get_rrsigs(rrset);
- assert(rrset != NULL);
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (rrset == NULL || knot_rrset_rdata(rrset) == NULL) {
+ // bad zone, ignore
+ return KNOT_EOK;
+ }
+ ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to add RRSIGs for second NSEC for "
+ "NXDOMAIN to response: %s\n", knot_strerror(ret));
+ return ret;
+ }
}
return KNOT_EOK;
@@ -1118,13 +1384,12 @@ static int ns_put_nsec3_nxdomain(const knot_zone_contents_t *zone,
knot_packet_t *resp)
{
// 1) Closest encloser proof
- dbg_ns("Putting closest encloser proof.\n");
int ret = ns_put_nsec3_closest_encloser_proof(zone, &closest_encloser,
qname, resp);
// 2) NSEC3 covering non-existent wildcard
if (ret == KNOT_EOK && closest_encloser != NULL) {
- dbg_ns("Putting NSEC3 for no wildcard child of closest "
- "encloser.\n");
+ dbg_ns_verb("Putting NSEC3 for no wildcard child of closest "
+ "encloser.\n");
ret = ns_put_nsec3_no_wildcard_child(zone, closest_encloser,
resp);
}
@@ -1207,16 +1472,16 @@ static int ns_put_nsec3_wildcard(const knot_zone_contents_t *zone,
* NSEC3 that covers the "next closer" name.
*/
// create the "next closer" name by appending from qname
- dbg_ns("Finding next closer name for wildcard NSEC3.\n");
+ dbg_ns_verb("Finding next closer name for wildcard NSEC3.\n");
knot_dname_t *next_closer =
ns_next_closer(closest_encloser->owner, qname);
if (next_closer == NULL) {
return NS_ERR_SERVFAIL;
}
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(next_closer);
- dbg_ns("Next closer name: %s\n", name);
+ dbg_ns_verb("Next closer name: %s\n", name);
free(name);
);
int ret = ns_put_covering_nsec3(zone, next_closer, resp);
@@ -1241,10 +1506,10 @@ dbg_ns_exec(
* \param previous Previous node of \a qname in canonical order.
* \param resp Response to put the NSEC3s into.
*/
-static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
- const knot_dname_t *qname,
- const knot_node_t *previous,
- knot_packet_t *resp)
+static int ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ const knot_node_t *previous,
+ knot_packet_t *resp)
{
assert(DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp)));
@@ -1261,13 +1526,22 @@ static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
knot_rrset_t *rrset =
knot_node_get_rrset(previous, KNOT_RRTYPE_NSEC);
- if (rrset != NULL) {
+
+ int ret = KNOT_EOK;
+
+ if (rrset != NULL && knot_rrset_rdata(rrset) != NULL) {
// NSEC proving that there is no node with the searched name
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
- rrset = knot_rrset_get_rrsigs(rrset);
- assert(rrset != NULL);
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ ret = knot_response_add_rrset_authority(resp, rrset, 1, 0,
+ 0, 1);
+ if (ret == KNOT_EOK) {
+ rrset = knot_rrset_get_rrsigs(rrset);
+ assert(rrset != NULL);
+ ret = knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0, 1);
+ }
}
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1306,10 +1580,10 @@ static int ns_put_nsec_nsec3_wildcard_nodata(const knot_node_t *node,
if (ret == KNOT_EOK
&& (nsec3_node = knot_node_nsec3_node(node))
!= NULL) {
- ns_put_nsec3_from_node(nsec3_node, resp);
+ ret = ns_put_nsec3_from_node(nsec3_node, resp);
}
} else {
- ns_put_nsec_wildcard(zone, qname, previous, resp);
+ ret = ns_put_nsec_wildcard(zone, qname, previous, resp);
}
}
return ret;
@@ -1339,18 +1613,18 @@ static int ns_put_nsec_nsec3_wildcard_answer(const knot_node_t *node,
const knot_dname_t *qname,
knot_packet_t *resp)
{
- int r = KNOT_EOK;
+ int ret = KNOT_EOK;
if (DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp))
&& knot_dname_is_wildcard(knot_node_owner(node))) {
if (knot_zone_contents_nsec3_enabled(zone)) {
- r = ns_put_nsec3_wildcard(zone, closest_encloser, qname,
- resp);
+ ret = ns_put_nsec3_wildcard(zone, closest_encloser,
+ qname, resp);
} else {
- ns_put_nsec_wildcard(zone, qname, previous, resp);
+ ret = ns_put_nsec_wildcard(zone, qname, previous, resp);
}
}
- return r;
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1403,26 +1677,27 @@ static inline int ns_referral(const knot_node_t *node,
knot_packet_t *resp,
uint16_t qtype)
{
- dbg_ns("Referral response.\n");
+ dbg_ns_verb("Referral response.\n");
while (!knot_node_is_deleg_point(node)) {
assert(knot_node_parent(node) != NULL);
node = knot_node_parent(node);
}
+ int ret = KNOT_EOK;
+
// Special handling of DS queries
if (qtype == KNOT_RRTYPE_DS) {
knot_rrset_t *ds_rrset = knot_node_get_rrset(node,
KNOT_RRTYPE_DS);
- int ret = KNOT_EOK;
if (ds_rrset) {
- knot_response_add_rrset_answer(resp, ds_rrset, 1, 0,
- 0, 1);
- if (DNSSEC_ENABLED
+ ret = knot_response_add_rrset_answer(resp, ds_rrset, 1,
+ 0, 0, 1);
+ if (ret == KNOT_EOK && DNSSEC_ENABLED
&& knot_query_dnssec_requested(
knot_packet_query(resp))) {
- ns_add_rrsigs(ds_rrset, resp, node->owner,
+ ret = ns_add_rrsigs(ds_rrset, resp, node->owner,
knot_response_add_rrset_authority,
1);
}
@@ -1430,18 +1705,13 @@ static inline int ns_referral(const knot_node_t *node,
// normal NODATA response
/*! \todo Handle in some generic way. */
- dbg_ns("Adding NSEC/NSEC3 for NODATA.\n");
- ns_put_nsec_nsec3_nodata(node, resp);
+ dbg_ns_verb("Adding NSEC/NSEC3 for NODATA.\n");
+ ret = ns_put_nsec_nsec3_nodata(zone, node, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
- // wildcard delegations not supported!
-// if (knot_dname_is_wildcard(node->owner)) {
-// dbg_ns("Putting NSEC/NSEC3 for wildcard"
-// " NODATA\n");
-// ret = ns_put_nsec_nsec3_wildcard_nodata(node,
-// closest_encloser, previous, zone, qname,
-// resp);
-// }
- ns_put_authority_soa(zone, resp);
+ ret = ns_put_authority_soa(zone, resp);
}
return ret;
@@ -1449,39 +1719,42 @@ static inline int ns_referral(const knot_node_t *node,
knot_rrset_t *rrset = knot_node_get_rrset(node, KNOT_RRTYPE_NS);
assert(rrset != NULL);
-
- // TODO: wildcards??
- //ns_check_wildcard(name, resp, &rrset);
- knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
- ns_add_rrsigs(rrset, resp, node->owner,
- knot_response_add_rrset_authority, 1);
+ ret = knot_response_add_rrset_authority(resp, rrset, 1, 0, 0, 1);
+ if (ret == KNOT_EOK) {
+ ret = ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+ }
- int ret = KNOT_EOK;
// add DS records
- dbg_ns("DNSSEC requested: %d\n",
+ dbg_ns_verb("DNSSEC requested: %d\n",
knot_query_dnssec_requested(knot_packet_query(resp)));
- dbg_ns("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS));
- if (DNSSEC_ENABLED
+ dbg_ns_verb("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS));
+ if (ret == KNOT_EOK && DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp))) {
rrset = knot_node_get_rrset(node, KNOT_RRTYPE_DS);
if (rrset != NULL) {
- knot_response_add_rrset_authority(resp, rrset, 1, 0,
- 0, 1);
- ns_add_rrsigs(rrset, resp, node->owner,
- knot_response_add_rrset_authority, 1);
+ ret = knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0, 1);
+ if (ret == KNOT_EOK) {
+ ret = ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+ }
} else {
// no DS, add NSEC3 or NSEC
// if NSEC3 enabled, search for NSEC3
if (knot_zone_contents_nsec3_enabled(zone)) {
const knot_node_t *nsec3_node =
knot_node_nsec3_node(node);
- dbg_ns("There is no DS, putting NSEC3s...\n");
+ dbg_ns_detail("There is no DS, putting NSEC3s."
+ "\n");
if (nsec3_node != NULL) {
- dbg_ns("Putting NSEC3s from the node.\n");
- ns_put_nsec3_from_node(nsec3_node, resp);
+ dbg_ns_detail("Putting NSEC3s from the node.\n");
+ ret = ns_put_nsec3_from_node(nsec3_node,
+ resp);
} else {
- dbg_ns("Putting Opt-Out NSEC3s.\n");
+ dbg_ns_detail("Putting Opt-Out NSEC3s."
+ "\n");
// no NSEC3 (probably Opt-Out)
// TODO: check if the zone is Opt-Out
ret = ns_put_nsec3_closest_encloser_proof(zone,
@@ -1492,10 +1765,11 @@ static inline int ns_referral(const knot_node_t *node,
node, KNOT_RRTYPE_NSEC);
if (nsec) {
/*! \todo Check return value? */
- knot_response_add_rrset_authority(
+ ret = knot_response_add_rrset_authority(
resp, nsec, 1, 1, 0, 1);
- if ((nsec = knot_rrset_get_rrsigs(nsec)) != NULL) {
- knot_response_add_rrset_authority(
+ if (ret == KNOT_EOK &&
+ (nsec = knot_rrset_get_rrsigs(nsec)) != NULL) {
+ ret = knot_response_add_rrset_authority(
resp, nsec, 1, 1, 0, 1);
}
}
@@ -1503,10 +1777,13 @@ static inline int ns_referral(const knot_node_t *node,
}
}
- if (ret == KNOT_EOK) {
-// ns_put_additional(resp);
+ if (ret == KNOT_ESPACE) {
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ ret = KNOT_EOK;
+ } else if (ret == KNOT_EOK) {
knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
}
+
return ret;
}
@@ -1540,65 +1817,65 @@ static int ns_answer_from_node(const knot_node_t *node,
const knot_node_t *previous,
const knot_zone_contents_t *zone,
const knot_dname_t *qname, uint16_t qtype,
- knot_packet_t *resp)
+ knot_packet_t *resp, int check_any)
{
- dbg_ns("Putting answers from found node to the response...\n");
- int answers = ns_put_answer(node, qname, qtype, resp);
+ dbg_ns_verb("Putting answers from found node to the response...\n");
+ int answers = 0;
+
+ int ret = ns_put_answer(node, zone, qname, qtype, resp, &answers,
+ check_any);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ assert(ret == KNOT_EOK);
- int ret = KNOT_EOK;
if (answers == 0) { // if NODATA response, put SOA
+ ret = ns_put_authority_soa(zone, resp);
if (knot_node_rrset_count(node) == 0
&& !knot_zone_contents_nsec3_enabled(zone)) {
// node is an empty non-terminal => NSEC for NXDOMAIN
//assert(knot_node_rrset_count(closest_encloser) > 0);
- dbg_ns("Adding NSEC/NSEC3 for NXDOMAIN.\n");
+ dbg_ns_verb("Adding NSEC/NSEC3 for NXDOMAIN.\n");
ret = ns_put_nsec_nsec3_nxdomain(zone,
knot_node_previous(node), closest_encloser,
qname, resp);
} else {
- dbg_ns("Adding NSEC/NSEC3 for NODATA.\n");
- ns_put_nsec_nsec3_nodata(node, resp);
+ dbg_ns_verb("Adding NSEC/NSEC3 for NODATA.\n");
+ ret = ns_put_nsec_nsec3_nodata(zone, node, resp);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed adding NSEC/NSEC3 for NODATA: %s"
+ "\n", knot_strerror(ret));
+ return ret;
+ }
+
if (knot_dname_is_wildcard(node->owner)) {
- dbg_ns("Putting NSEC/NSEC3 for wildcard"
- " NODATA\n");
+ dbg_ns_verb("Putting NSEC/NSEC3 for wildcard"
+ " NODATA\n");
ret = ns_put_nsec_nsec3_wildcard_nodata(node,
closest_encloser, previous, zone, qname,
resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
}
}
- ns_put_authority_soa(zone, resp);
} else { // else put authority NS
// if wildcard answer, add NSEC / NSEC3
- dbg_ns("Adding NSEC/NSEC3 for wildcard answer.\n");
-
-// char *n = knot_dname_to_str(knot_node_owner(node));
-// char *ce = (closest_encloser)
-// ? knot_dname_to_str(knot_node_owner(closest_encloser))
-// : "(nil)";
-// char *prev = (previous)
-// ? knot_dname_to_str(knot_node_owner(previous))
-// : "(nil)";
-// printf("Node: %s, closest encloser: %s, previous: %s\n",
-// n, ce, prev);
-// free(n);
-// if (closest_encloser) {
-// free(ce);
-// }
-// if (previous) {
-// free(prev);
-// }
+ dbg_ns_verb("Adding NSEC/NSEC3 for wildcard answer.\n");
+
assert(previous == NULL);
assert(closest_encloser == knot_node_parent(node)
|| !knot_dname_is_wildcard(knot_node_owner(node)));
ret = ns_put_nsec_nsec3_wildcard_answer(node, closest_encloser,
previous, zone, qname, resp);
- ns_put_authority_ns(zone, resp);
+
+ if (ret == KNOT_EOK) {
+ ret = ns_put_authority_ns(zone, resp);
+ }
}
-// if (ret == KNOT_EOK) {
-// ns_put_additional(resp);
-// }
return ret;
}
@@ -1617,7 +1894,7 @@ static int ns_answer_from_node(const knot_node_t *node,
static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
const knot_dname_t *qname)
{
- dbg_ns("Synthetizing CNAME from DNAME...\n");
+ dbg_ns_verb("Synthetizing CNAME from DNAME...\n");
// create new CNAME RRSet
@@ -1642,15 +1919,25 @@ static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
knot_rdata_get_item(knot_rrset_rdata(dname_rrset), 0)->dname);
dbg_ns_exec(
char *name = knot_dname_to_str(cname);
- dbg_ns("CNAME canonical name: %s.\n", name);
+ dbg_ns_verb("CNAME canonical name: %s.\n", name);
free(name);
);
knot_rdata_t *cname_rdata = knot_rdata_new();
knot_rdata_item_t cname_rdata_item;
cname_rdata_item.dname = cname;
- knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1);
+ int ret = knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&cname_rrset, 1, 1, 1);
+ knot_rdata_deep_free(&cname_rdata, KNOT_RRTYPE_CNAME, 1);
+ return NULL;
+ }
- knot_rrset_add_rdata(cname_rrset, cname_rdata);
+ ret = knot_rrset_add_rdata(cname_rrset, cname_rdata);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&cname_rrset, 1, 1, 1);
+ knot_rdata_deep_free(&cname_rdata, KNOT_RRTYPE_CNAME, 1);
+ return NULL;
+ }
return cname_rrset;
}
@@ -1694,38 +1981,59 @@ static int ns_dname_is_too_long(const knot_rrset_t *dname_rrset,
* \param qname Searched name.
* \param resp Response.
*/
-static void ns_process_dname(knot_rrset_t *dname_rrset,
- const knot_dname_t *qname,
+static int ns_process_dname(knot_rrset_t *dname_rrset,
+ const knot_dname_t **qname,
knot_packet_t *resp)
{
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_rrset_owner(dname_rrset));
- dbg_ns("Processing DNAME for owner %s...\n", name);
+ dbg_ns_verb("Processing DNAME for owner %s...\n", name);
free(name);
);
// TODO: check the number of RRs in the RRSet??
// put the DNAME RRSet into the answer
- knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0, 1);
- ns_add_rrsigs(dname_rrset, resp, qname,
- knot_response_add_rrset_answer, 1);
+ int ret = knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
- if (ns_dname_is_too_long(dname_rrset, qname)) {
+ ret = ns_add_rrsigs(dname_rrset, resp, *qname,
+ knot_response_add_rrset_answer, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (ns_dname_is_too_long(dname_rrset, *qname)) {
knot_response_set_rcode(resp, KNOT_RCODE_YXDOMAIN);
- return;
+ return KNOT_EOK;
}
// synthetize CNAME (no way to tell that client supports DNAME)
- knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, qname);
+ knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, *qname);
// add the synthetized RRSet to the Answer
- knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0, 1);
+ ret = knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
// no RRSIGs for this RRSet
// add the synthetized RRSet into list of temporary RRSets of response
- knot_packet_add_tmp_rrset(resp, synth_cname);
+ ret = knot_packet_add_tmp_rrset(resp, synth_cname);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // get the next SNAME from the CNAME RDATA
+ const knot_dname_t *cname = knot_rdata_cname_name(
+ knot_rrset_rdata(synth_cname));
+ dbg_ns_verb("CNAME name from RDATA: %p\n", cname);
- // do not search for the name in new zone (out-of-bailiwick)
+ // save the new name which should be used for replacing wildcard
+ *qname = cname;
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -1735,15 +2043,23 @@ dbg_ns_exec(
* \param apex Zone apex node.
* \param resp Response.
*/
-static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
+static int ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
{
knot_rrset_t *rrset =
knot_node_get_rrset(apex, KNOT_RRTYPE_DNSKEY);
+
+ int ret = KNOT_EOK;
+
if (rrset != NULL) {
- knot_response_add_rrset_additional(resp, rrset, 0, 0, 0, 1);
- ns_add_rrsigs(rrset, resp, apex->owner,
- knot_response_add_rrset_additional, 0);
+ ret = knot_response_add_rrset_additional(resp, rrset, 0, 0,
+ 0, 1);
+ if (ret == KNOT_EOK) {
+ ret = ns_add_rrsigs(rrset, resp, apex->owner,
+ knot_response_add_rrset_additional, 0);
+ }
}
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1763,7 +2079,7 @@ static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
* \todo Describe the answering logic in detail.
*/
static int ns_answer_from_zone(const knot_zone_contents_t *zone,
- knot_packet_t *resp)
+ knot_packet_t *resp, int check_any)
{
const knot_node_t *node = NULL, *closest_encloser = NULL,
*previous = NULL;
@@ -1774,13 +2090,9 @@ static int ns_answer_from_zone(const knot_zone_contents_t *zone,
search:
#ifdef USE_HASH_TABLE
- /*! \todo Check version. */
find_ret = knot_zone_contents_find_dname_hash(zone, qname, &node,
&closest_encloser);
-// node = knot_node_current(node);
-// closest_encloser = knot_node_current(closest_encloser);
#else
- /*! \todo Check version. */
find_ret = knot_zone_contents_find_dname(zone, qname, &node,
&closest_encloser, &previous);
node = knot_node_current(node);
@@ -1791,33 +2103,33 @@ search:
return NS_ERR_SERVFAIL;
}
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name;
if (node) {
name = knot_dname_to_str(node->owner);
- dbg_ns("zone_find_dname() returned node %s \n", name);
+ dbg_ns_verb("zone_find_dname() returned node %s \n", name);
free(name);
} else {
- dbg_ns("zone_find_dname() returned no node,\n");
+ dbg_ns_verb("zone_find_dname() returned no node,\n");
}
if (closest_encloser != NULL) {
name = knot_dname_to_str(closest_encloser->owner);
- dbg_ns(" closest encloser %s.\n", name);
+ dbg_ns_verb(" closest encloser %s.\n", name);
free(name);
} else {
- dbg_ns(" closest encloser (nil).\n");
+ dbg_ns_verb(" closest encloser (nil).\n");
}
if (previous != NULL) {
name = knot_dname_to_str(previous->owner);
- dbg_ns(" and previous node: %s.\n", name);
+ dbg_ns_verb(" and previous node: %s.\n", name);
free(name);
} else {
- dbg_ns(" and previous node: (nil).\n");
+ dbg_ns_verb(" and previous node: (nil).\n");
}
);
if (find_ret == KNOT_EBADZONE) {
- // possible only if we followed cname
+ // possible only if we followed CNAME or DNAME
assert(cname != 0);
knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
auth_soa = 1;
@@ -1826,10 +2138,10 @@ dbg_ns_exec(
}
have_node:
- dbg_ns("Closest encloser is deleg. point? %s\n",
+ dbg_ns_verb("Closest encloser is deleg. point? %s\n",
(knot_node_is_deleg_point(closest_encloser)) ? "yes" : "no");
- dbg_ns("Closest encloser is non authoritative? %s\n",
+ dbg_ns_verb("Closest encloser is non authoritative? %s\n",
(knot_node_is_non_auth(closest_encloser)) ? "yes" : "no");
if (knot_node_is_deleg_point(closest_encloser)
@@ -1843,21 +2155,31 @@ have_node:
knot_rrset_t *dname_rrset = knot_node_get_rrset(
closest_encloser, KNOT_RRTYPE_DNAME);
if (dname_rrset != NULL) {
- ns_process_dname(dname_rrset, qname, resp);
- auth_soa = 1;
+ ret = ns_process_dname(dname_rrset, &qname, resp);
+
knot_response_set_aa(resp);
- goto finalize;
+
+ if (ret != KNOT_EOK) {
+ goto finalize;
+ }
+
+ // do not search for the name in new zone
+ // (out-of-bailiwick), just in the current zone if it
+ // belongs there
+
+ cname = 1;
+ goto search;
}
// else check for a wildcard child
const knot_node_t *wildcard_node =
knot_node_wildcard_child(closest_encloser);
if (wildcard_node == NULL) {
- dbg_ns("No wildcard node. (cname: %d)\n",
- cname);
+ dbg_ns_verb("No wildcard node. (cname: %d)\n",
+ cname);
auth_soa = 1;
if (cname == 0) {
- dbg_ns("Setting NXDOMAIN RCODE.\n");
+ dbg_ns_detail("Setting NXDOMAIN RCODE.\n");
// return NXDOMAIN
knot_response_set_rcode(resp,
KNOT_RCODE_NXDOMAIN);
@@ -1884,24 +2206,31 @@ have_node:
}
if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL
- && qtype != KNOT_RRTYPE_CNAME) {
+ && qtype != KNOT_RRTYPE_CNAME && qtype != KNOT_RRTYPE_RRSIG) {
dbg_ns_exec(
char *name = knot_dname_to_str(node->owner);
- dbg_ns("Node %s has CNAME record, resolving...\n",
- name);
+ dbg_ns_verb("Node %s has CNAME record, resolving...\n", name);
free(name);
);
const knot_dname_t *act_name = qname;
- ns_follow_cname(&node, &act_name, resp,
- knot_response_add_rrset_answer, 1);
-dbg_ns_exec(
+ ret = ns_follow_cname(&node, &act_name, resp,
+ knot_response_add_rrset_answer, 1);
+
+ /*! \todo IS OK??? */
+ knot_response_set_aa(resp);
+
+ if (ret != KNOT_EOK) {
+ // KNOT_ESPACE case is handled there
+ goto finalize;
+ }
+dbg_ns_exec_verb(
char *name = (node != NULL) ? knot_dname_to_str(node->owner)
: "(nil)";
char *name2 = knot_dname_to_str(act_name);
- dbg_ns("Canonical name: %s (%p), node found: %p\n",
- name2, act_name, node);
- dbg_ns("The node's owner: %s (%p)\n", name, (node != NULL)
- ? node->owner : NULL);
+ dbg_ns_verb("Canonical name: %s (%p), node found: %p\n",
+ name2, act_name, node);
+ dbg_ns_verb("The node's owner: %s (%p)\n", name, (node != NULL)
+ ? node->owner : NULL);
if (node != NULL) {
free(name);
}
@@ -1913,25 +2242,33 @@ dbg_ns_exec(
// otherwise search for the new name
if (node == NULL) {
goto search;
- } else if (node->owner != act_name) {
- // the stored node is closest encloser
- find_ret = KNOT_ZONE_NAME_NOT_FOUND;
- closest_encloser = node;
- node = NULL;
- goto have_node;
- } // else do nothing, just continue
+ } else if (knot_node_owner(node) != act_name) {
+ if(knot_dname_is_wildcard(knot_node_owner(node))) {
+ // we must set the closest encloser to the
+ // parent of the node, to be right
+ closest_encloser = knot_node_parent(node);
+ assert(closest_encloser != NULL);
+ } else {
+ // the stored node is closest encloser
+ find_ret = KNOT_ZONE_NAME_NOT_FOUND;
+ closest_encloser = node;
+ node = NULL;
+ goto have_node;
+ }
+ }
}
ret = ns_answer_from_node(node, closest_encloser, previous, zone, qname,
- qtype, resp);
+ qtype, resp, check_any);
if (ret == NS_ERR_SERVFAIL) {
// in this case we should drop the response and send an error
// for now, just send the error code with a non-complete answer
-// knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
-// goto finalize;
return ret;
} else if (ret != KNOT_EOK) {
/*! \todo Handle RCODE return values!!! */
+ // In case ret == KNOT_ESPACE, this is later converted to EOK
+ // so it does not cause error response
+ knot_response_set_aa(resp);
goto finalize;
}
knot_response_set_aa(resp);
@@ -1940,16 +2277,21 @@ dbg_ns_exec(
// this is the only case when the servers answers from
// particular node, i.e. the only case when it may return SOA
// or NS records in Answer section
- if (DNSSEC_ENABLED
+ if (knot_packet_tc(resp) == 0 && DNSSEC_ENABLED
&& knot_query_dnssec_requested(knot_packet_query(resp))
&& node == knot_zone_contents_apex(zone)
&& (qtype == KNOT_RRTYPE_SOA || qtype == KNOT_RRTYPE_NS)) {
- ns_add_dnskey(node, resp);
+ ret = ns_add_dnskey(node, resp);
}
finalize:
- if (ret == KNOT_EOK && auth_soa) {
- ns_put_authority_soa(zone, resp);
+ if (ret == KNOT_EOK && knot_packet_tc(resp) == 0 && auth_soa) {
+ ret = ns_put_authority_soa(zone, resp);
+ }
+
+ if (ret == KNOT_ESPACE) {
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ ret = KNOT_EOK;
}
// add all missing NSECs/NSEC3s for wildcard nodes
@@ -1976,20 +2318,9 @@ finalize:
* \retval KNOT_EOK
* \retval NS_ERR_SERVFAIL
*/
-static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp)
-{
-// const knot_dname_t *qname = knot_packet_qname(resp);
-// assert(qname != NULL);
-
-// uint16_t qtype = knot_packet_qtype(resp);
-//dbg_ns_exec(
-// char *name_str = knot_dname_to_str(qname);
-// dbg_ns("Trying to find zone for QNAME %s\n", name_str);
-// free(name_str);
-//);
-// // find zone in which to search for the name
-// const knot_zone_t *zone =
-// ns_get_zone_for_qname(db, qname, qtype);
+static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp,
+ int check_any)
+{
const knot_zone_contents_t *contents = knot_zone_contents(zone);
// if no zone found, return REFUSED
@@ -2012,9 +2343,7 @@ dbg_ns_exec(
// take the zone contents and use only them for answering
- return ns_answer_from_zone(contents, resp);
-
- //knot_dname_free(&qname);
+ return ns_answer_from_zone(contents, resp, check_any);
}
/*----------------------------------------------------------------------------*/
@@ -2026,10 +2355,9 @@ int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
size_t rsize = 0;
int ret = 0;
- if ((ret = knot_packet_to_wire(resp, &rwire, &rsize))
- != KNOT_EOK) {
+ if ((ret = knot_packet_to_wire(resp, &rwire, &rsize)) != KNOT_EOK) {
dbg_ns("Error converting response packet "
- "to wire format (error %d).\n", ret);
+ "to wire format (error %d).\n", ret);
return NS_ERR_SERVFAIL;
}
@@ -2041,14 +2369,13 @@ int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
if (rwire != wire) {
dbg_ns("Wire format reallocated, copying to place for "
- "wire.\n");
+ "wire.\n");
memcpy(wire, rwire, rsize);
} else {
dbg_ns("Using the same space or wire format.\n");
}
*wire_size = rsize;
- //free(rwire);
return KNOT_EOK;
}
@@ -2073,17 +2400,17 @@ static int ns_error_response_to_wire(knot_packet_t *resp, uint8_t *wire,
* wire format is assembled, but COUNTs in header are not set.
* This is ideal, we just truncate the packet after the question.
*/
- dbg_ns("Creating error response.\n");
+ dbg_ns_verb("Creating error response.\n");
size_t rsize = knot_packet_question_size(knot_packet_query(resp));
- dbg_ns("Error response (~ query) size: %zu\n", rsize);
+ dbg_ns_detail("Error response (~ query) size: %zu\n", rsize);
// take 'qsize' from the current wireformat of the response
// it is already assembled - Header and Question section are copied
const uint8_t *rwire = knot_packet_wireformat(resp);
if (rsize > *wire_size) {
dbg_ns("Reponse size (%zu) larger than allowed wire size"
- " (%zu).\n", rsize, *wire_size);
+ " (%zu).\n", rsize, *wire_size);
return NS_ERR_SERVFAIL;
}
@@ -2116,8 +2443,8 @@ typedef struct ns_axfr_params {
int knot_ns_tsig_required(int packet_nr)
{
- dbg_ns_detail("ns_tsig_required(%d): %d\n", packet_nr,
- (packet_nr % KNOT_NS_TSIG_FREQ == 0));
+ dbg_ns_verb("ns_tsig_required(%d): %d\n", packet_nr,
+ (packet_nr % KNOT_NS_TSIG_FREQ == 0));
return (packet_nr % KNOT_NS_TSIG_FREQ == 0);
}
@@ -2132,7 +2459,7 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
assert(xfr->send != NULL);
// Transform the packet into wire format
- dbg_ns("Converting response to wire format..\n");
+ dbg_ns_verb("Converting response to wire format..\n");
size_t real_size = xfr->wire_size;
if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) {
return NS_ERR_SERVFAIL;
@@ -2154,15 +2481,14 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
xfr->tsig_data_size += real_size;
}
- /*! \note [TSIG] Generate TSIG if required (during XFR/IN). */
if (xfr->tsig_key && add_tsig) {
if (xfr->packet_nr == 0) {
/* Add key, digest and digest length. */
dbg_ns_detail("Calling tsig_sign(): %p, %zu, %zu, "
- "%p, %zu, %p, %zu, %p\n",
- xfr->wire, real_size, xfr->wire_size,
- xfr->digest, xfr->digest_size, xfr->digest,
- digest_real_size, xfr->tsig_key);
+ "%p, %zu, %p, %zu, %p\n",
+ xfr->wire, real_size, xfr->wire_size,
+ xfr->digest, xfr->digest_size, xfr->digest,
+ digest_real_size, xfr->tsig_key);
res = knot_tsig_sign(xfr->wire, &real_size,
xfr->wire_size, xfr->digest,
xfr->digest_size, xfr->digest,
@@ -2182,8 +2508,7 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
xfr->tsig_data_size);
}
- dbg_ns_detail("Sign function returned: %s\n",
- knot_strerror(res));
+ dbg_ns_verb("Sign function returned: %s\n", knot_strerror(res));
dbg_ns_detail("Real size of digest: %zu\n", digest_real_size);
if (res != KNOT_EOK) {
@@ -2198,8 +2523,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
xfr->tsig_data_size = 0;
} else if (xfr->tsig_rcode != 0) {
- dbg_ns_detail("Adding TSIG without signing, TSIG RCODE: %d.\n",
- xfr->tsig_rcode);
+ dbg_ns_verb("Adding TSIG without signing, TSIG RCODE: %d.\n",
+ xfr->tsig_rcode);
assert(xfr->tsig_rcode != KNOT_TSIG_RCODE_BADTIME);
// add TSIG without signing
assert(xfr->query != NULL);
@@ -2225,12 +2550,11 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
return res;
} else if (res != real_size) {
dbg_ns("AXFR did not send right amount of bytes."
- " Transfer size: %zu, sent: %d\n",
- real_size, res);
+ " Transfer size: %zu, sent: %d\n", real_size, res);
}
// Clean the response structure
- dbg_ns("Clearing response structure..\n");
+ dbg_ns_verb("Clearing response structure..\n");
knot_response_clear(xfr->response, 0);
// increment the packet number
@@ -2243,8 +2567,10 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
knot_packet_set_tsig_size(xfr->response, 0);
}
- dbg_ns("Response structure after clearing:\n");
+dbg_ns_exec_verb(
+ dbg_ns_verb("Response structure after clearing:\n");
knot_packet_dump(xfr->response);
+);
return KNOT_EOK;
}
@@ -2260,15 +2586,15 @@ static void ns_axfr_from_node(knot_node_t *node, void *data)
if (params->ret != KNOT_EOK) {
// just skip (will be called on next node with the same params
- dbg_ns("Params contain error: %s, skipping node...\n",
+ dbg_ns_detail("Params contain error: %s, skipping node...\n",
knot_strerror(params->ret));
return;
}
- dbg_ns("Params OK, answering AXFR from node %p.\n", node);
-dbg_ns_exec(
+ dbg_ns_detail("Params OK, answering AXFR from node %p.\n", node);
+dbg_ns_exec_verb(
char *name = knot_dname_to_str(knot_node_owner(node));
- dbg_ns("Node ownerr: %s\n", name);
+ dbg_ns_verb("Node owner: %s\n", name);
free(name);
);
@@ -2289,8 +2615,8 @@ dbg_ns_exec(
assert(rrsets[i] != NULL);
rrset = rrsets[i];
rrset:
- dbg_ns(" Type: %s\n",
- knot_rrtype_to_string(knot_rrset_type(rrset)));
+ dbg_ns_verb(" Type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
// do not add SOA
if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
@@ -2477,9 +2803,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, knot_rrset_t *rrset)
/*! \todo Probably send back AXFR instead. */
knot_response_set_rcode(xfr->response,
KNOT_RCODE_SERVFAIL);
- /*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
- //socket_close(xfr->session); /*! \todo Remove for UDP.*/
return res;
}
@@ -2546,12 +2870,10 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
0, 0, 0);
if (res != KNOT_EOK) {
dbg_ns("IXFR query cannot be answered: %s.\n",
- knot_strerror(res));
+ knot_strerror(res));
knot_response_set_rcode(xfr->response,
KNOT_RCODE_SERVFAIL);
- /*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
-// socket_close(xfr->session); /*! \todo Remove for UDP.*/
rcu_read_unlock();
return res;
}
@@ -2571,10 +2893,7 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
}
if (res == KNOT_EOK) {
- /*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
- //socket_close(xfr->session); /*! \todo Remove for UDP.*/
-// return 1;
}
rcu_read_unlock();
@@ -2596,7 +2915,6 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
// malformed packet
dbg_ns("IXFR query does not contain authority record.\n");
knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
- /*! \todo Probably rename the function. */
if (ns_xfr_send_and_clear(xfr, 1) == KNOT_ECONN) {
return KNOT_ECONN;
}
@@ -2614,11 +2932,9 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
// malformed packet
dbg_ns("IXFR query is malformed.\n");
knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
- /*! \todo Probably rename the function. */
if (ns_xfr_send_and_clear(xfr, 1) == KNOT_ECONN) {
return KNOT_ECONN;
}
- //socket_close(xfr->session); /*! \todo Remove for UDP. */
return KNOT_EMALF;
}
@@ -2627,8 +2943,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
/*----------------------------------------------------------------------------*/
-static int knot_ns_prepare_response(knot_nameserver_t *nameserver,
- knot_packet_t *query, knot_packet_t **resp,
+static int knot_ns_prepare_response(knot_packet_t *query, knot_packet_t **resp,
size_t max_size)
{
assert(max_size >= 500);
@@ -2641,8 +2956,6 @@ static int knot_ns_prepare_response(knot_nameserver_t *nameserver,
}
int ret = knot_packet_set_max_size(*resp, max_size);
- //(*resp)->wireformat = response_wire;;
- //(*resp)->max_size = max_size;
if (ret != KNOT_EOK) {
dbg_ns("Failed to init response structure.\n");
@@ -2827,14 +3140,12 @@ void knot_ns_set_nsid(knot_nameserver_t *nameserver, const char *nsid, size_t le
int ret = knot_ns_replace_nsid(nameserver->opt_rr, nsid, len);
-// int ret = knot_edns_add_option(nameserver->opt_rr, EDNS_OPTION_NSID,
-// len, (const uint8_t *)nsid);
if (ret != KNOT_EOK) {
dbg_ns("NS: set_nsid: could not add EDNS option.\n");
return;
}
- dbg_ns("NS: set_nsid: added successfully.\n");
+ dbg_ns_verb("NS: set_nsid: added successfully.\n");
}
/*----------------------------------------------------------------------------*/
@@ -2847,28 +3158,21 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
return KNOT_EBADARG;
}
- dbg_ns("ns_parse_packet() called with query size %zu.\n", qsize);
- //dbg_ns_hex((char *)query_wire, qsize);
-
- if (qsize < 2) {
- return KNOT_EMALF;
- }
+ dbg_ns_verb("ns_parse_packet() called with query size %zu.\n", qsize);
// 1) create empty response
- dbg_ns("Parsing packet...\n");
- //parsed = knot_response_new_empty(NULL);
+ dbg_ns_verb("Parsing packet...\n");
int ret = 0;
if ((ret = knot_packet_parse_from_wire(packet, query_wire,
qsize, 1)) != 0) {
dbg_ns("Error while parsing packet, "
- "libknot error '%s'.\n", knot_strerror(ret));
-// knot_response_free(&parsed);
+ "libknot error '%s'.\n", knot_strerror(ret));
return KNOT_RCODE_FORMERR;
}
- dbg_ns("Parsed packet header and Question:\n");
+ dbg_ns_verb("Parsed packet header and Question:\n");
knot_packet_dump(packet);
// 3) determine the query type
@@ -2904,17 +3208,15 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
return KNOT_RCODE_NOTIMPL;
}
-// knot_packet_free(&packet);
-
return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
-void knot_ns_error_response(const knot_nameserver_t *nameserver,
- uint16_t query_id, uint8_t *flags1_query,
- uint8_t rcode, uint8_t *response_wire,
- size_t *rsize)
+static void knot_ns_error_response(const knot_nameserver_t *nameserver,
+ uint16_t query_id, uint8_t *flags1_query,
+ uint8_t rcode, uint8_t *response_wire,
+ size_t *rsize)
{
memcpy(response_wire, nameserver->err_response,
nameserver->err_resp_size);
@@ -2927,7 +3229,7 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver,
knot_wire_set_rd(response_wire);
}
knot_wire_set_opcode(response_wire,
- knot_wire_flags_get_opcode(*flags1_query));
+ knot_wire_flags_get_opcode(*flags1_query));
}
// set the RCODE
@@ -2937,10 +3239,10 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver,
/*----------------------------------------------------------------------------*/
-int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
- const uint8_t *query, size_t size,
- uint8_t rcode, uint8_t *response_wire,
- size_t *rsize)
+int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver,
+ const uint8_t *query, size_t size,
+ uint8_t rcode,
+ uint8_t *response_wire, size_t *rsize)
{
if (size < 2) {
// ignore packet
@@ -2964,6 +3266,63 @@ int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
/*----------------------------------------------------------------------------*/
+int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
+ const knot_packet_t *query,
+ uint8_t rcode, uint8_t *response_wire,
+ size_t *rsize)
+{
+ if (query->parsed < 2) {
+ // ignore packet
+ return KNOT_EFEWDATA;
+ }
+
+ if (query->parsed < KNOT_WIRE_HEADER_SIZE) {
+ return knot_ns_error_response_from_query_wire(nameserver,
+ query->wireformat, query->size, rcode, response_wire,
+ rsize);
+ }
+
+ size_t max_size = *rsize;
+ uint8_t flags1 = knot_wire_get_flags1(knot_packet_wireformat(query));
+
+ // prepare the generic error response
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ &flags1, rcode, response_wire,
+ rsize);
+
+ if (query->parsed > KNOT_WIRE_HEADER_SIZE
+ + KNOT_WIRE_QUESTION_MIN_SIZE) {
+ // in this case the whole question was parsed, append it
+ size_t question_size = 4 + knot_dname_size(
+ knot_packet_qname(query));
+
+ if (max_size > KNOT_WIRE_HEADER_SIZE + question_size) {
+ /*
+ * At this point, the wireformat of query may be in the
+ * same place where the response is assembled. This does
+ * not matter before this point, although the query
+ * wireformat is rewritten. Now we just need to copy
+ * the original Question section. So if the pointers are
+ * the same, we may just leave it and increase the
+ * response wire size. Otherwise we must copy the data.
+ */
+ if (response_wire != knot_packet_wireformat(query)) {
+ memcpy(response_wire + KNOT_WIRE_HEADER_SIZE,
+ knot_packet_wireformat(query)
+ + KNOT_WIRE_HEADER_SIZE, question_size);
+ }
+ *rsize += question_size;
+
+ // adjust QDCOUNT
+ knot_wire_set_qdcount(response_wire, 1);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
void knot_ns_error_response_full(knot_nameserver_t *nameserver,
knot_packet_t *response, uint8_t rcode,
uint8_t *response_wire, size_t *rsize)
@@ -2971,11 +3330,10 @@ void knot_ns_error_response_full(knot_nameserver_t *nameserver,
knot_response_set_rcode(response, rcode);
if (ns_error_response_to_wire(response, response_wire, rsize) != 0) {
- knot_ns_error_response(nameserver, knot_packet_id(
- knot_packet_query(response)),
- &response->header.flags1,
- KNOT_RCODE_SERVFAIL, response_wire,
- rsize);
+ knot_ns_error_response_from_query(nameserver,
+ knot_packet_query(response),
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
}
}
@@ -2985,7 +3343,7 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
knot_packet_t *query, knot_packet_t **resp,
const knot_zone_t **zone, size_t max_size)
{
- dbg_ns("knot_ns_prep_normal_response()\n");
+ dbg_ns_verb("knot_ns_prep_normal_response()\n");
if (nameserver == NULL || query == NULL || resp == NULL
|| zone == NULL) {
@@ -2994,8 +3352,8 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
// first, parse the rest of the packet
assert(knot_packet_is_query(query));
- dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
- knot_packet_parsed(query), knot_packet_size(query));
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
int ret;
ret = knot_packet_parse_rest(query);
@@ -3017,10 +3375,38 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
if (knot_packet_ancount(query) > 0
|| knot_packet_nscount(query) > 0
|| knot_packet_qdcount(query) != 1) {
- dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n");
+ dbg_ns("ANCOUNT or NSCOUNT not 0 in query, "
+ "or QDCOUNT != 1. Reply FORMERR.\n");
return KNOT_EMALF;
}
+ /*
+ * Check what is in the Additional section. Only OPT and TSIG are
+ * allowed. TSIG must be the last record if present.
+ */
+ if (knot_packet_arcount(query) > 0) {
+ int ok = 0;
+ const knot_rrset_t *add1 =
+ knot_packet_additional_rrset(query, 0);
+ if (knot_packet_additional_rrset_count(query) == 1
+ && (knot_rrset_type(add1) == KNOT_RRTYPE_OPT
+ || knot_rrset_type(add1) == KNOT_RRTYPE_TSIG)) {
+ ok = 1;
+ } else if (knot_packet_additional_rrset_count(query) == 2) {
+ const knot_rrset_t *add2 =
+ knot_packet_additional_rrset(query, 1);
+ if (knot_rrset_type(add1) == KNOT_RRTYPE_OPT
+ && knot_rrset_type(add2) == KNOT_RRTYPE_TSIG) {
+ ok = 1;
+ }
+ }
+
+ if (!ok) {
+ dbg_ns("Additional section malformed. Reply FORMERR\n");
+ return KNOT_EMALF;
+ }
+ }
+
size_t resp_max_size = 0;
knot_packet_dump(query);
@@ -3044,22 +3430,21 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
resp_max_size = MAX_UDP_PAYLOAD;
}
- ret = knot_ns_prepare_response(nameserver, query, resp,
- resp_max_size);
+ ret = knot_ns_prepare_response(query, resp, resp_max_size);
if (ret != KNOT_EOK) {
return KNOT_ERROR;
}
- dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
- query->parsed, query->size);
- dbg_ns("Opt RR: version: %d, payload: %d\n",
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+ dbg_ns_detail("Opt RR: version: %d, payload: %d\n",
query->opt_rr.version, query->opt_rr.payload);
// get the answer for the query
knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
- dbg_ns("EDNS supported in query: %d\n",
- knot_query_edns_supported(query));
+ dbg_ns_detail("EDNS supported in query: %d\n",
+ knot_query_edns_supported(query));
// set the OPT RR to the response
if (knot_query_edns_supported(query)) {
@@ -3067,25 +3452,24 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
knot_query_nsid_requested(query));
if (ret != KNOT_EOK) {
dbg_ns("Failed to set OPT RR to the response"
- ": %s\n", knot_strerror(ret));
+ ": %s\n", knot_strerror(ret));
} else {
// copy the DO bit from the query
if (knot_query_dnssec_requested(query)) {
- /*! \todo API for this. */
knot_edns_set_do(&(*resp)->opt_rr);
}
}
}
- dbg_ns("Response max size: %zu\n", (*resp)->max_size);
+ dbg_ns_verb("Response max size: %zu\n", (*resp)->max_size);
const knot_dname_t *qname = knot_packet_qname(*resp);
assert(qname != NULL);
uint16_t qtype = knot_packet_qtype(*resp);
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name_str = knot_dname_to_str(qname);
- dbg_ns("Trying to find zone for QNAME %s\n", name_str);
+ dbg_ns_verb("Trying to find zone for QNAME %s\n", name_str);
free(name_str);
);
// find zone in which to search for the name
@@ -3098,11 +3482,11 @@ dbg_ns_exec(
int knot_ns_answer_normal(knot_nameserver_t *nameserver,
const knot_zone_t *zone, knot_packet_t *resp,
- uint8_t *response_wire, size_t *rsize)
+ uint8_t *response_wire, size_t *rsize, int check_any)
{
- dbg_ns("ns_answer_normal()\n");
+ dbg_ns_verb("ns_answer_normal()\n");
- int ret = ns_answer(zone, resp);
+ int ret = ns_answer(zone, resp, check_any);
if (ret != 0) {
// now only one type of error (SERVFAIL), later maybe more
@@ -3110,7 +3494,7 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver,
KNOT_RCODE_SERVFAIL,
response_wire, rsize);
} else {
- dbg_ns("Created response packet.\n");
+ dbg_ns_verb("Created response packet.\n");
//knot_response_dump(resp);
knot_packet_dump(resp);
@@ -3123,6 +3507,55 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver,
}
}
+ dbg_ns_verb("Returning response with wire size %zu\n", *rsize);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_ixfr_udp(knot_nameserver_t *nameserver,
+ const knot_zone_t *zone, knot_packet_t *resp,
+ uint8_t *response_wire, size_t *rsize)
+{
+ dbg_ns("ns_answer_ixfr_udp()\n");
+
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+
+ // if no zone found, return REFUSED
+ if (zone == NULL) {
+ dbg_ns("No zone found.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_REFUSED);
+ return KNOT_EOK;
+ } else if (contents == NULL) {
+ dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
+ return KNOT_EOK;
+ }
+
+ const knot_node_t *apex = knot_zone_contents_apex(contents);
+ assert(apex != NULL);
+ knot_rrset_t *soa = knot_node_get_rrset(apex, KNOT_RRTYPE_SOA);
+
+ // just put the SOA to the Answer section of the response and send back
+ int ret = knot_response_add_rrset_answer(resp, soa, 1, 0, 0, 0);
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response_full(nameserver, resp,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ }
+
+ dbg_ns("Created response packet.\n");
+ knot_packet_dump(resp);
+
+ // Transform the packet into wire format
+ if (ns_response_to_wire(resp, response_wire, rsize) != 0) {
+ // send back SERVFAIL (as this is our problem)
+ knot_ns_error_response_full(nameserver, resp,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ }
+
dbg_ns("Returning response with wire size %zu\n", *rsize);
return KNOT_EOK;
@@ -3134,18 +3567,21 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
{
dbg_ns("knot_ns_init_xfr()\n");
+ int ret = 0;
+
if (nameserver == NULL || xfr == NULL) {
- return KNOT_EBADARG;
+ dbg_ns("Wrong parameters given to function ns_init_xfr()\n");
+ /* Sending error was totally wrong. If nameserver or xfr were
+ * NULL, the ns_error_response() function would crash.
+ */
+ return ret;
}
- // no need to parse rest of the packet
- /*! \todo Parse rest of packet because of EDNS. */
- int ret = knot_packet_parse_rest(xfr->query);
+ ret = knot_packet_parse_rest(xfr->query);
if (ret != KNOT_EOK) {
dbg_ns("Failed to parse rest of the query: %s\n",
knot_strerror(ret));
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
+ knot_ns_error_response_from_query(nameserver, xfr->query,
(ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
: KNOT_RCODE_SERVFAIL,
xfr->wire, &xfr->wire_size);
@@ -3154,8 +3590,10 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
return ret;
}
- dbg_packet("Parsed XFR query:\n");
+dbg_ns_exec_verb(
+ dbg_ns_verb("Parsed XFR query:\n");
knot_packet_dump(xfr->query);
+);
// initialize response packet structure
knot_packet_t *response = knot_packet_new(
@@ -3163,41 +3601,25 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
if (response == NULL) {
dbg_ns("Failed to create packet structure.\n");
/*! \todo xfr->wire is not NULL, will fail on assert! */
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
- KNOT_RCODE_SERVFAIL, xfr->wire,
- &xfr->wire_size);
+ knot_ns_error_response_from_query(nameserver, xfr->query,
+ KNOT_RCODE_SERVFAIL,
+ xfr->wire, &xfr->wire_size);
ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
xfr->wire_size);
- knot_packet_free(&response);
return ret;
}
- //int ret = knot_packet_set_max_size(response, xfr->wire_size);
response->wireformat = xfr->wire;
response->max_size = xfr->wire_size;
-// if (ret != KNOT_EOK) {
-// dbg_ns("Failed to init response structure.\n");
-// /*! \todo xfr->wire is not NULL, will fail on assert! */
-// knot_ns_error_response(nameserver, xfr->query->header.id,
-// KNOT_RCODE_SERVFAIL, xfr->wire,
-// &xfr->wire_size);
-// int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
-// xfr->wire_size);
-// knot_packet_free(&response);
-// return res;
-// }
-
ret = knot_response_init_from_query(response, xfr->query);
if (ret != KNOT_EOK) {
dbg_ns("Failed to init response structure.\n");
/*! \todo xfr->wire is not NULL, will fail on assert! */
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
- KNOT_RCODE_SERVFAIL, xfr->wire,
- &xfr->wire_size);
+ knot_ns_error_response_from_query(nameserver, xfr->query,
+ KNOT_RCODE_SERVFAIL,
+ xfr->wire, &xfr->wire_size);
int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
xfr->wire_size);
knot_packet_free(&response);
@@ -3213,9 +3635,9 @@ int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_AXFR ||
knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR);
-dbg_ns_exec(
+dbg_ns_exec_verb(
char *name_str = knot_dname_to_str(qname);
- dbg_ns("Trying to find zone with name %s\n", name_str);
+ dbg_ns_verb("Trying to find zone with name %s\n", name_str);
free(name_str);
);
// find zone in which to search for the name
@@ -3258,20 +3680,20 @@ int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
{
if (xfr == NULL || xfr->zone == NULL || serial_from == NULL
|| serial_to == NULL) {
- dbg_ns_detail("Wrong parameters: xfr=%p,"
- " xfr->zone = %p\n", xfr, xfr->zone);
+ dbg_ns("Wrong parameters: xfr=%p,"
+ " xfr->zone = %p\n", xfr, xfr->zone);
return KNOT_EBADARG;
}
const knot_zone_t *zone = xfr->zone;
const knot_zone_contents_t *contents = knot_zone_contents(zone);
if (!contents) {
- dbg_ns_detail("Missing contents\n");
+ dbg_ns("Missing contents\n");
return KNOT_EBADARG;
}
if (knot_zone_contents_apex(contents) == NULL) {
- dbg_ns_detail("No apex.\n");
+ dbg_ns("No apex.\n");
return KNOT_EBADARG;
}
@@ -3279,17 +3701,17 @@ int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
knot_node_rrset(knot_zone_contents_apex(contents),
KNOT_RRTYPE_SOA);
if (zone_soa == NULL) {
- dbg_ns_verb("No SOA.\n");
+ dbg_ns("No SOA.\n");
return KNOT_EBADARG;
}
if (knot_packet_nscount(xfr->query) < 1) {
- dbg_ns_verb("No Authority record.\n");
+ dbg_ns("No Authority record.\n");
return KNOT_EMALF;
}
if (knot_packet_authority_rrset(xfr->query, 0) == NULL) {
- dbg_ns_verb("Authority record missing.\n");
+ dbg_ns("Authority record missing.\n");
return KNOT_ERROR;
}
@@ -3309,13 +3731,13 @@ int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
/*! \todo Handle TSIG errors differently. */
knot_response_set_rcode(xfr->response, rcode);
- /*! \todo Probably rename the function. */
int ret = 0;
- if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK) {
+ if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK
+ || xfr->response == NULL) {
size_t size = 0;
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
- KNOT_RCODE_SERVFAIL, xfr->wire, &size);
+ knot_ns_error_response_from_query(nameserver, xfr->query,
+ KNOT_RCODE_SERVFAIL,
+ xfr->wire, &size);
ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
}
@@ -3337,11 +3759,7 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone);
if (!contents) {
dbg_ns("AXFR failed on stub zone\n");
- /*! \todo replace with knot_ns_xfr_send_error() */
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
- KNOT_RCODE_SERVFAIL, xfr->wire,
- &xfr->wire_size);
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
xfr->wire_size);
rcu_read_unlock();
@@ -3349,16 +3767,16 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
return ret;
}
- /*!
- * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
- * Now just count the expected size of the TSIG RR and save it
- * to the response structure.
+ /*
+ * The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure.
*/
/*! \todo [TSIG] Get the TSIG size from some API function. */
if (xfr->tsig_size > 0) {
- dbg_ns_detail("Setting TSIG size in packet: %zu\n",
- xfr->tsig_size);
+ dbg_ns_verb("Setting TSIG size in packet: %zu\n",
+ xfr->tsig_size);
knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
}
@@ -3372,11 +3790,7 @@ int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
dbg_ns("AXFR failed, sending SERVFAIL.\n");
// now only one type of error (SERVFAIL), later maybe more
/*! \todo xfr->wire is not NULL, will fail on assert! */
- /*! \todo replace with knot_ns_xfr_send_error() */
- knot_ns_error_response(nameserver, xfr->query->header.id,
- &xfr->query->header.flags1,
- KNOT_RCODE_SERVFAIL, xfr->wire,
- &xfr->wire_size);
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
xfr->wire_size);
} else if (ret > 0) {
@@ -3398,14 +3812,12 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
|| xfr->response == NULL) {
return KNOT_EBADARG;
}
-
- //uint8_t *wire = NULL;
- //size_t size = xfr->wire_size;
// parse rest of the packet (we need the Authority record)
int ret = knot_packet_parse_rest(xfr->query);
if (ret != KNOT_EOK) {
- dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n");
+ dbg_ns("Failed to parse rest of the packet: %s. "
+ "Reply FORMERR.\n", knot_strerror(ret));
knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR);
knot_packet_free(&xfr->response);
return ret;
@@ -3419,11 +3831,11 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
return ret;
}
- /*!
- * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
- * Now just count the expected size of the TSIG RR and save it
- * to the response structure. This should be optional, only if
- * the request contained TSIG, i.e. if there is the data in 'xfr'.
+ /*
+ * The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure. This should be optional, only if
+ * the request contained TSIG, i.e. if there is the data in 'xfr'.
*/
/*! \todo [TSIG] Get the TSIG size from some API function. */
@@ -3433,16 +3845,6 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
ret = ns_ixfr(xfr);
-// /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
-// * and when it does not. E.g. if there was problem in sending
-// * packet, it will probably fail when sending the SERVFAIL also.
-// */
-// if (ret < 0) {
-// dbg_ns("IXFR failed, sending SERVFAIL.\n");
-// // now only one type of error (SERVFAIL), later maybe more
-// knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
-// }
-
knot_packet_free(&xfr->response);
return ret;
@@ -3452,18 +3854,16 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
{
- /*!
- * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
- * and the digest of the query sent to the master or the previous
- * digest.
+ /*
+ * Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
*/
dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n",
- xfr->wire_size);
+ xfr->wire_size);
- int ret = xfrin_process_axfr_packet(/*xfr->wire, xfr->wire_size,*/
- /*(xfrin_constructed_zone_t **)(&xfr->data)*/
- xfr);
+ int ret = xfrin_process_axfr_packet(xfr);
if (ret > 0) { // transfer finished
dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n");
@@ -3477,19 +3877,19 @@ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
assert(zone != NULL);
/* Create and fill hash table */
- dbg_ns("ns_process_axfrin: filling hash table.\n");
+ dbg_ns_verb("ns_process_axfrin: filling hash table.\n");
int rc = knot_zone_contents_create_and_fill_hash_table(zone);
if (rc != KNOT_EOK) {
return KNOT_ERROR; // TODO: change error code
}
- dbg_ns("ns_process_axfrin: adjusting zone.\n");
+ dbg_ns_verb("ns_process_axfrin: adjusting zone.\n");
rc = knot_zone_contents_adjust(zone);
if (rc != KNOT_EOK) {
return rc;
}
- dbg_ns("ns_process_axfrin: checking loops.\n");
+ dbg_ns_verb("ns_process_axfrin: checking loops.\n");
rc = knot_zone_contents_check_loops(zone);
if (rc != KNOT_EOK) {
return rc;
@@ -3508,15 +3908,13 @@ int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
//knot_zone_contents_dump(zone, 0);
// check zone integrity
-dbg_xfrin_exec(
+dbg_ns_exec_verb(
int errs = knot_zone_contents_integrity_check(zone);
- dbg_xfrin("Zone integrity check: %d errors.\n", errs);
+ dbg_ns_verb("Zone integrity check: %d errors.\n", errs);
);
}
- /*!
- * \todo In case of error, shouldn't the zone be destroyed here?
- */
+ /*! \todo In case of error, shouldn't the zone be destroyed here? */
return ret;
}
@@ -3538,14 +3936,14 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver,
return KNOT_ENOZONE;
}
- // find the zone in the zone db
- knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db,
- knot_node_owner(knot_zone_contents_apex(zone)));
+ /* Zone must not be looked-up from server, as it may be a different zone if
+ * a reload occurs when transfer is pending. */
+ knot_zone_t *z = xfr->zone;
if (z == NULL) {
char *name = knot_dname_to_str(knot_node_owner(
knot_zone_contents_apex(zone)));
dbg_ns("Failed to replace zone %s, old zone "
- "not found\n", name);
+ "not found\n", name);
free(name);
return KNOT_ENOZONE;
@@ -3555,16 +3953,18 @@ int knot_ns_switch_zone(knot_nameserver_t *nameserver,
int ret = xfrin_switch_zone(z, zone, xfr->type);
-dbg_ns_exec(
- dbg_ns("Zone db contents: (zone count: %zu)\n",
- nameserver->zone_db->zone_count);
+dbg_ns_exec_verb(
+ dbg_ns_verb("Zone db contents: (zone count: %zu)\n",
+ nameserver->zone_db->zone_count);
+ /* Warning: may not show updated zone if updated zone that is already
+ * discarded from zone db (reload with pending transfer). */
const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db);
for (int i = 0; i < knot_zonedb_zone_count
(nameserver->zone_db); i++) {
- dbg_ns("%d. zone: %p", i, zones[i]);
+ dbg_ns_verb("%d. zone: %p\n", i, zones[i]);
char *name = knot_dname_to_str(zones[i]->name);
- dbg_ns(" zone name: %s\n", name);
+ dbg_ns_verb(" zone name: %s\n", name);
free(name);
}
free(zones);
@@ -3574,18 +3974,16 @@ dbg_ns_exec(
}
/*----------------------------------------------------------------------------*/
-/*! \todo In this function, xfr->zone is properly set. If this is so, we do not
- * have to search for the zone after the transfer has finished.
- */
+
int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
knot_ns_xfr_t *xfr)
{
dbg_ns("ns_process_ixfrin: incoming packet\n");
- /*!
- * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
- * and the digest of the query sent to the master or the previous
- * digest.
+ /*
+ * [TSIG] Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
*/
int ret = xfrin_process_ixfr_packet(xfr);
@@ -3608,19 +4006,18 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
}
// find zone associated with the changesets
- knot_zone_t *zone = knot_zonedb_find_zone(
- nameserver->zone_db,
- knot_rrset_owner(chgsets->first_soa));
+ /* Must not search for the zone in zonedb as it may fetch a
+ * different zone than the one the transfer started on. */
+ knot_zone_t *zone = xfr->zone;
if (zone == NULL) {
dbg_ns("No zone found for incoming IXFR!\n");
knot_free_changesets(
(knot_changesets_t **)(&xfr->data));
- return KNOT_ENOZONE; /*! \todo Other error code? */
+ return KNOT_ENOZONE;
}
switch (ret) {
case XFRIN_RES_COMPLETE:
- xfr->zone = zone;
break;
case XFRIN_RES_SOA_ONLY: {
// compare the SERIAL from the changeset with the zone's
@@ -3666,9 +4063,7 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
}
}
- /*!
- * \todo In case of error, shouldn't the zone be destroyed here?
- */
+ /*! \todo In case of error, shouldn't the zone be destroyed here? */
return ret;
}
@@ -3684,26 +4079,24 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
knot_packet_t *response;
assert(*rsize >= MAX_UDP_PAYLOAD);
- int ret = knot_ns_prepare_response(nameserver, query, &response,
- MAX_UDP_PAYLOAD);
+ int ret = knot_ns_prepare_response(query, &response, MAX_UDP_PAYLOAD);
if (ret != KNOT_EOK) {
- knot_ns_error_response(nameserver, knot_packet_id(query),
- &query->header.flags1,
- KNOT_RCODE_SERVFAIL, response_wire,
- rsize);
+ knot_ns_error_response_from_query(nameserver, query,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
return KNOT_EOK;
}
assert(response != NULL);
- dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
- query->parsed, query->size);
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
if (knot_packet_parsed(query) < knot_packet_size(query)) {
ret = knot_packet_parse_rest(query);
if (ret != KNOT_EOK) {
dbg_ns("Failed to parse rest of the query: "
- "%s.\n", knot_strerror(ret));
+ "%s.\n", knot_strerror(ret));
knot_ns_error_response_full(nameserver, response,
(ret == KNOT_EMALF)
? KNOT_RCODE_FORMERR
@@ -3714,17 +4107,16 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
}
}
- dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
- knot_packet_parsed(query), knot_packet_size(query));
+ dbg_ns_verb("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
/*! \todo API for EDNS values. */
- dbg_ns("Opt RR: version: %d, payload: %d\n",
- query->opt_rr.version, query->opt_rr.payload);
+ dbg_ns_verb("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
// 2) Find zone for the query
// we do not check if there is only one entry in the Question section
// because the packet structure does not allow it
- /*! \todo Check number of Question entries while parsing. */
if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
dbg_ns("Question is not of type SOA.\n");
knot_ns_error_response_full(nameserver, response,
@@ -3754,7 +4146,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
return KNOT_EBADZONE;
} else if (ret != KNOT_EOK) {
dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
+ "%s.\n", knot_strerror(ret));
knot_ns_error_response_full(nameserver, response, rcode,
response_wire, rsize);
knot_packet_free(&response);
@@ -3766,7 +4158,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
ret = knot_ddns_process_prereqs(query, &prereqs, &rcode);
if (ret != KNOT_EOK) {
dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
+ "%s.\n", knot_strerror(ret));
knot_ns_error_response_full(nameserver, response, rcode,
response_wire, rsize);
knot_packet_free(&response);
@@ -3783,7 +4175,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
&rcode);
if (ret != KNOT_EOK) {
dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
+ "%s.\n", knot_strerror(ret));
knot_ns_error_response_full(nameserver, response, rcode,
response_wire, rsize);
knot_ddns_prereqs_free(&prereqs);
@@ -3795,7 +4187,7 @@ int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
ret = knot_ddns_process_update(query, changeset, &rcode);
if (ret != KNOT_EOK) {
dbg_ns("Failed to check zone for update: "
- "%s.\n", knot_strerror(ret));
+ "%s.\n", knot_strerror(ret));
knot_ns_error_response_full(nameserver, response, rcode,
response_wire, rsize);
knot_ddns_prereqs_free(&prereqs);
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
index da19f49..3fe1210 100644..100755
--- a/src/libknot/nameserver/name-server.h
+++ b/src/libknot/nameserver/name-server.h
@@ -115,8 +115,6 @@ typedef struct knot_ns_xfr {
*/
uint8_t *tsig_data;
size_t tsig_data_size; /*!< Size of the message(s) in bytes */
-// const knot_rrset_t *tsig; /*!< Response TSIG.
-// \todo [TSIG] Replace with separate data. */
size_t tsig_size; /*!< Size of the TSIG RR wireformat in bytes.*/
knot_key_t *tsig_key; /*!< Associated TSIG key for signing. */
@@ -127,13 +125,6 @@ typedef struct knot_ns_xfr {
uint16_t tsig_rcode;
uint64_t tsig_prev_time_signed;
- /*! \brief Previous digest or request digest.
- *
- * Should be allocated before the transfer (known size).
- */
-// uint8_t *prev_digest;
-// size_t prev_digest_size; /*!< Size of previous digest in bytes. */
-
/*!
* \brief Number of the packet currently assembled.
*
@@ -154,7 +145,7 @@ static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;
enum knot_ns_xfr_flag_t {
XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */
XFR_FLAG_UDP = 1 << 1, /*!< XFR request is on UDP. */
- XFR_FLAG_AXFR_FINISHED = 1 << 2
+ XFR_FLAG_AXFR_FINISHED = 1 << 2 /*!< Transfer is finished. */
};
typedef enum knot_ns_transport {
@@ -218,26 +209,13 @@ void knot_ns_set_nsid(knot_nameserver_t *nameserver, const char *nsid,
int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
knot_packet_t *packet, knot_packet_type_t *type);
-/*!
- * \brief Prepares wire format of an error response using generic error template
- * stored in the nameserver structure.
- *
- * The error response will not contain the Question section from the query, just
- * a header with ID copied from the query and the given RCODE.
- *
- * \param nameserver Nameserver structure containing the error template.
- * \param query_id ID of the query.
- * \param rcode RCODE to set in the response.
- * \param response_wire Place for wire format of the response.
- * \param rsize Size of the error response will be stored here.
- */
-void knot_ns_error_response(const knot_nameserver_t *nameserver,
- uint16_t query_id, uint8_t *flags1_query,
- uint8_t rcode, uint8_t *response_wire,
- size_t *rsize);
+int knot_ns_error_response_from_query_wire(const knot_nameserver_t *nameserver,
+ const uint8_t *query, size_t size,
+ uint8_t rcode, uint8_t *response_wire,
+ size_t *rsize);
int knot_ns_error_response_from_query(const knot_nameserver_t *nameserver,
- const uint8_t *query, size_t size,
+ const knot_packet_t *query,
uint8_t rcode, uint8_t *response_wire,
size_t *rsize);
@@ -264,7 +242,11 @@ int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
*/
int knot_ns_answer_normal(knot_nameserver_t *nameserver,
const knot_zone_t *zone, knot_packet_t *resp,
- uint8_t *response_wire, size_t *rsize);
+ uint8_t *response_wire, size_t *rsize, int check_any);
+
+int knot_ns_answer_ixfr_udp(knot_nameserver_t *nameserver,
+ const knot_zone_t *zone, knot_packet_t *resp,
+ uint8_t *response_wire, size_t *rsize);
int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
@@ -322,11 +304,11 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
* \param nameserver Name server structure to provide the data for answering.
* \param xfr Persistent transfer-specific data.
*
- * \todo Document me.
*/
int knot_ns_process_axfrin(knot_nameserver_t *nameserver,
knot_ns_xfr_t *xfr);
+/*! \todo Document me. */
int knot_ns_switch_zone(knot_nameserver_t *nameserver,
knot_ns_xfr_t *xfr);
diff --git a/src/libknot/nsec3.c b/src/libknot/nsec3.c
index 1414e7e..9cab4be 100644..100755
--- a/src/libknot/nsec3.c
+++ b/src/libknot/nsec3.c
@@ -74,7 +74,7 @@ int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
dbg_nsec3("Flags: %hu\n", params->flags);
dbg_nsec3("Iterations: %hu\n", params->iterations);
dbg_nsec3("Salt length: %hu\n", params->salt_length);
- dbg_nsec3("Salt: ");
+ dbg_nsec3("Salt: \n");
if (params->salt != NULL) {
dbg_nsec3_hex((char *)params->salt,
params->salt_length);
@@ -171,8 +171,8 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params,
EVP_MD_CTX_cleanup(&mdctx);
- dbg_nsec3("NSEC3 hashing: calls: %lu, avg time per call: %f."
- "\n", calls, (double)(total_time) / calls);
+ dbg_nsec3_verb("NSEC3 hashing: calls: %lu, avg time per call: %f."
+ "\n", calls, (double)(total_time) / calls);
free(data_low);
return 0;
@@ -194,17 +194,17 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params,
uint8_t salt_length = params->salt_length;
uint16_t iterations = params->iterations;
- dbg_nsec3("Hashing: \n");
- dbg_nsec3(" Data: %.*s \n", size, data);
- dbg_nsec3_hex((const char *)data, size);
- dbg_nsec3(" (size %d)\n Iterations: %u\n", (int)size, iterations);
- dbg_nsec3(" Salt length: %u\n", salt_length);
- dbg_nsec3(" Salt: ");
+ dbg_nsec3_verb("Hashing: \n");
+ dbg_nsec3_verb(" Data: %.*s \n", size, data);
+ dbg_nsec3_hex_verb((const char *)data, size);
+ dbg_nsec3_verb(" (size %d)\n Iterations: %u\n", (int)size, iterations);
+ dbg_nsec3_verb(" Salt length: %u\n", salt_length);
+ dbg_nsec3_verb(" Salt: \n");
if (salt_length > 0) {
- dbg_nsec3_hex((char *)salt, salt_length);
- dbg_nsec3("\n");
+ dbg_nsec3_hex_verb((char *)salt, salt_length);
+ dbg_nsec3_verb("\n");
} else {
- dbg_nsec3("none\n");
+ dbg_nsec3_verb("none\n");
}
SHA_CTX ctx;
@@ -251,9 +251,9 @@ int knot_nsec3_sha1(const knot_nsec3_params_t *params,
*digest_size = SHA_DIGEST_LENGTH;
- dbg_nsec3("Hash: %.*s\n", *digest_size, *digest);
- dbg_nsec3_hex((const char *)*digest, *digest_size);
- dbg_nsec3("\n");
+ dbg_nsec3_verb("Hash: %.*s\n", *digest_size, *digest);
+ dbg_nsec3_hex_verb((const char *)*digest, *digest_size);
+ dbg_nsec3_verb("\n");
free(data_low);
return KNOT_EOK;
diff --git a/src/libknot/nsec3.h b/src/libknot/nsec3.h
index 0ce6899..0ce6899 100644..100755
--- a/src/libknot/nsec3.h
+++ b/src/libknot/nsec3.h
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 82b65c6..6c7fd02 100644..100755
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -22,6 +22,7 @@
#include "common.h"
#include "util/descriptor.h"
#include "util/wire.h"
+#include "tsig.h"
/*----------------------------------------------------------------------------*/
@@ -51,7 +52,7 @@ typedef enum {
*/
static void knot_packet_init_pointers_response(knot_packet_t *pkt)
{
- dbg_packet("Packet pointer: %p\n", pkt);
+ dbg_packet_detail("Packet pointer: %p\n", pkt);
char *pos = (char *)pkt + PREALLOC_PACKET;
@@ -59,7 +60,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pkt->question.qname = (knot_dname_t *)pos;
pos += PREALLOC_QNAME_DNAME;
- dbg_packet("QNAME: %p\n", pkt->question.qname);
+ dbg_packet_detail("QNAME: %p\n", pkt->question.qname);
pkt->question.qname->name = (uint8_t *)pos;
pos += PREALLOC_QNAME_NAME;
@@ -67,7 +68,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pos += PREALLOC_QNAME_LABELS;
pkt->owner_tmp = (uint8_t *)pos;
- dbg_packet("Tmp owner: %p\n", pkt->owner_tmp);
+ dbg_packet_detail("Tmp owner: %p\n", pkt->owner_tmp);
pos += PREALLOC_RR_OWNER;
// then answer, authority and additional sections
@@ -92,9 +93,9 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pos += DEFAULT_ARCOUNT * sizeof(const knot_rrset_t *);
}
- dbg_packet("Answer section: %p\n", pkt->answer);
- dbg_packet("Authority section: %p\n", pkt->authority);
- dbg_packet("Additional section: %p\n", pkt->additional);
+ dbg_packet_detail("Answer section: %p\n", pkt->answer);
+ dbg_packet_detail("Authority section: %p\n", pkt->authority);
+ dbg_packet_detail("Additional section: %p\n", pkt->additional);
pkt->max_an_rrsets = DEFAULT_ANCOUNT;
pkt->max_ns_rrsets = DEFAULT_NSCOUNT;
@@ -106,8 +107,8 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pkt->compression.offsets = (size_t *)pos;
pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t);
- dbg_packet("Compression dnames: %p\n", pkt->compression.dnames);
- dbg_packet("Compression offsets: %p\n", pkt->compression.offsets);
+ dbg_packet_detail("Compression dnames: %p\n", pkt->compression.dnames);
+ dbg_packet_detail("Compression offsets: %p\n", pkt->compression.offsets);
pkt->compression.max = DEFAULT_DOMAINS_IN_RESPONSE;
pkt->compression.default_count = DEFAULT_DOMAINS_IN_RESPONSE;
@@ -118,8 +119,8 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pkt->wildcard_nodes.snames = (const knot_dname_t **)pos;
pos += DEFAULT_WILDCARD_NODES * sizeof(knot_dname_t *);
- dbg_packet("Wildcard nodes: %p\n", pkt->wildcard_nodes.nodes);
- dbg_packet("Wildcard SNAMEs: %p\n", pkt->wildcard_nodes.snames);
+ dbg_packet_detail("Wildcard nodes: %p\n", pkt->wildcard_nodes.nodes);
+ dbg_packet_detail("Wildcard SNAMEs: %p\n", pkt->wildcard_nodes.snames);
pkt->wildcard_nodes.default_count = DEFAULT_WILDCARD_NODES;
pkt->wildcard_nodes.max = DEFAULT_WILDCARD_NODES;
@@ -127,7 +128,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
pkt->tmp_rrsets = (const knot_rrset_t **)pos;
pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *);
- dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+ dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets);
pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS;
@@ -141,7 +142,7 @@ static void knot_packet_init_pointers_response(knot_packet_t *pkt)
*/
static void knot_packet_init_pointers_query(knot_packet_t *pkt)
{
- dbg_packet("Packet pointer: %p\n", pkt);
+ dbg_packet_detail("Packet pointer: %p\n", pkt);
char *pos = (char *)pkt + PREALLOC_PACKET;
@@ -149,17 +150,15 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt)
pkt->question.qname = (knot_dname_t *)pos;
pos += PREALLOC_QNAME_DNAME;
- dbg_packet("QNAME: %p (%zu after start of packet)\n",
- pkt->question.qname,
- (void *)pkt->question.qname - (void *)pkt);
+ dbg_packet_detail("QNAME: %p (%zu after start of packet)\n",
+ pkt->question.qname,
+ (void *)pkt->question.qname - (void *)pkt);
pkt->question.qname->name = (uint8_t *)pos;
pos += PREALLOC_QNAME_NAME;
pkt->question.qname->labels = (uint8_t *)pos;
pos += PREALLOC_QNAME_LABELS;
-// pkt->owner_tmp = (uint8_t *)((char *)pkt->question.qname->labels
-// + PREALLOC_QNAME_LABELS);
// then answer, authority and additional sections
if (DEFAULT_ANCOUNT_QUERY == 0) {
@@ -183,9 +182,9 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt)
pos += DEFAULT_ARCOUNT_QUERY * sizeof(const knot_rrset_t *);
}
- dbg_packet("Answer section: %p\n", pkt->answer);
- dbg_packet("Authority section: %p\n", pkt->authority);
- dbg_packet("Additional section: %p\n", pkt->additional);
+ dbg_packet_detail("Answer section: %p\n", pkt->answer);
+ dbg_packet_detail("Authority section: %p\n", pkt->authority);
+ dbg_packet_detail("Additional section: %p\n", pkt->additional);
pkt->max_an_rrsets = DEFAULT_ANCOUNT_QUERY;
pkt->max_ns_rrsets = DEFAULT_NSCOUNT_QUERY;
@@ -194,15 +193,11 @@ static void knot_packet_init_pointers_query(knot_packet_t *pkt)
pkt->tmp_rrsets = (const knot_rrset_t **)pos;
pos += DEFAULT_TMP_RRSETS_QUERY * sizeof(const knot_rrset_t *);
- dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+ dbg_packet_detail("Tmp rrsets: %p\n", pkt->tmp_rrsets);
pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY;
-// dbg_packet("End of data: %p (%zu after start of packet)\n",
-// pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY,
-// (void *)(pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY)
-// - (void *)pkt);
- dbg_packet("Allocated total: %u\n", PREALLOC_QUERY);
+ dbg_packet_detail("Allocated total: %u\n", PREALLOC_QUERY);
assert(pos == (char *)pkt + PREALLOC_QUERY);
}
@@ -230,7 +225,7 @@ static int knot_packet_parse_header(const uint8_t *wire, size_t *pos,
assert(header != NULL);
if (size - *pos < KNOT_WIRE_HEADER_SIZE) {
- dbg_response("Not enough data to parse header.\n");
+ dbg_packet("Not enough data to parse header.\n");
return KNOT_EFEWDATA;
}
@@ -238,10 +233,8 @@ static int knot_packet_parse_header(const uint8_t *wire, size_t *pos,
// copy some of the flags: OPCODE and RD
// do this by copying flags1 and setting QR to 1, AA to 0 and TC to 0
header->flags1 = knot_wire_get_flags1(wire);
-// knot_wire_flags_set_qr(&header->flags1);
-// knot_wire_flags_clear_aa(&header->flags1);
-// knot_wire_flags_clear_tc(&header->flags1);
// do not copy flags2 (all set by server)
+
header->qdcount = knot_wire_get_qdcount(wire);
header->ancount = knot_wire_get_ancount(wire);
header->nscount = knot_wire_get_nscount(wire);
@@ -276,12 +269,11 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
assert(question != NULL);
if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) {
- dbg_response("Not enough data to parse question.\n");
+ dbg_packet("Not enough data to parse question.\n");
return KNOT_EFEWDATA; // malformed
}
- dbg_response("Parsing Question starting on position %zu.\n",
- *pos);
+ dbg_packet("Parsing Question starting on position %zu.\n", *pos);
// domain name must end with 0, so just search for 0
int i = *pos;
@@ -290,13 +282,13 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
}
if (size - i - 1 < 4) {
- dbg_response("Not enough data to parse question.\n");
+ dbg_packet("Not enough data to parse question.\n");
return KNOT_EFEWDATA; // no 0 found or not enough data left
}
- dbg_response("Parsing dname starting on position %zu and "
+ dbg_packet_verb("Parsing dname starting on position %zu and "
"%zu bytes long.\n", *pos, i - *pos + 1);
- dbg_response("Alloc: %d\n", alloc);
+ dbg_packet_verb("Alloc: %d\n", alloc);
if (alloc) {
question->qname = knot_dname_new_from_wire(
wire + *pos, i - *pos + 1, NULL);
@@ -314,10 +306,7 @@ static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
*pos = i + 1;
question->qtype = knot_wire_read_u16(wire + i + 1);
- //*pos += 2;
question->qclass = knot_wire_read_u16(wire + i + 3);
- //*pos += 2;
-
*pos += 4;
return KNOT_EOK;
@@ -340,7 +329,7 @@ static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
short *max_count,
short default_max_count, short step)
{
- dbg_packet("Max count: %d, default max count: %d\n",
+ dbg_packet_verb("Max count: %d, default max count: %d\n",
*max_count, default_max_count);
int free_old = (*max_count) != default_max_count;
const knot_rrset_t **old = *rrsets;
@@ -368,11 +357,6 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
size_t *pos, size_t total_size, size_t rdlength,
const knot_rrtype_descriptor_t *desc)
{
-// if (desc->type == 0) {
-// dbg_packet("Unknown RR type.\n");
-// return NULL;
-// }
-
knot_rdata_t *rdata = knot_rdata_new();
if (rdata == NULL) {
return NULL;
@@ -383,7 +367,7 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
if (rc != KNOT_EOK) {
dbg_packet("rdata_from_wire() returned: %s\n",
- knot_strerror(rc));
+ knot_strerror(rc));
knot_rdata_free(&rdata);
return NULL;
}
@@ -396,38 +380,32 @@ static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
size_t size)
{
-// knot_rrset_t *rrset =
-// (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
-// CHECK_ALLOC_LOG(rrset, NULL);
-
dbg_packet("Parsing RR from position: %zu, total size: %zu\n",
- *pos, size);
+ *pos, size);
knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
NULL);
- dbg_packet("Created owner: %p, actual position: %zu\n", owner,
+ dbg_packet_detail("Created owner: %p, actual position: %zu\n", owner,
*pos);
if (owner == NULL) {
return NULL;
}
-dbg_packet_exec(
+dbg_packet_exec_verb(
char *name = knot_dname_to_str(owner);
- dbg_packet("Parsed name: %s\n", name);
+ dbg_packet_verb("Parsed name: %s\n", name);
free(name);
);
- //*remaining -= knot_dname_size(rrset->owner);
-
/*! @todo Get rid of the numerical constant. */
if (size - *pos < 10) {
dbg_packet("Malformed RR: Not enough data to parse RR"
- " header.\n");
+ " header.\n");
knot_dname_release(owner);
return NULL;
}
- dbg_packet("Reading type from position %zu\n", *pos);
+ dbg_packet_detail("Reading type from position %zu\n", *pos);
uint16_t type = knot_wire_read_u16(wire + *pos);
uint16_t rclass = knot_wire_read_u16(wire + *pos + 2);
@@ -445,7 +423,7 @@ dbg_packet_exec(
uint16_t rdlength = knot_wire_read_u16(wire + *pos + 8);
- dbg_packet("Read RR header: type %u, class %u, ttl %u, "
+ dbg_packet_detail("Read RR header: type %u, class %u, ttl %u, "
"rdlength %u\n", rrset->type, rrset->rclass,
rrset->ttl, rdlength);
@@ -453,10 +431,8 @@ dbg_packet_exec(
if (size - *pos < rdlength) {
dbg_packet("Malformed RR: Not enough data to parse RR"
- " RDATA (size: %zu, position: %zu).\n",
- size, *pos);
+ " RDATA (size: %zu, position: %zu).\n", size, *pos);
knot_rrset_deep_free(&rrset, 1, 1, 0);
-// free(rrset);
return NULL;
}
@@ -473,16 +449,13 @@ dbg_packet_exec(
if (rdata == NULL) {
dbg_packet("Malformed RR: Could not parse RDATA.\n");
knot_rrset_deep_free(&rrset, 1, 1, 0);
-// free(rrset);
return NULL;
}
if (knot_rrset_add_rdata(rrset, rdata) != KNOT_EOK) {
- dbg_packet("Malformed RR: Could not add RDATA to RRSet"
- ".\n");
+ dbg_packet("Malformed RR: Could not add RDATA to RRSet.\n");
knot_rdata_free(&rdata);
knot_rrset_deep_free(&rrset, 1, 1, 0);
-// free(rrset);
return NULL;
}
@@ -492,23 +465,22 @@ dbg_packet_exec(
/*----------------------------------------------------------------------------*/
static int knot_packet_add_rrset(knot_rrset_t *rrset,
- const knot_rrset_t ***rrsets,
- short *rrset_count,
- short *max_rrsets,
- short default_rrsets,
- const knot_packet_t *packet,
- knot_packet_duplicate_handling_t dupl)
+ const knot_rrset_t ***rrsets,
+ short *rrset_count,
+ short *max_rrsets,
+ short default_rrsets,
+ const knot_packet_t *packet,
+ knot_packet_duplicate_handling_t dupl)
{
-
assert(rrset != NULL);
assert(rrsets != NULL);
assert(rrset_count != NULL);
assert(max_rrsets != NULL);
-dbg_packet_exec(
+dbg_packet_exec_verb(
char *name = knot_dname_to_str(rrset->owner);
- dbg_packet("packet_add_rrset(), owner: %s, type: %s\n",
- name, knot_rrtype_to_string(rrset->type));
+ dbg_packet_verb("packet_add_rrset(), owner: %s, type: %s\n",
+ name, knot_rrtype_to_string(rrset->type));
free(name);
);
@@ -529,19 +501,20 @@ dbg_packet_exec(
// try to find the RRSet in this array of RRSets
for (int i = 0; i < *rrset_count; ++i) {
-dbg_packet_exec(
+dbg_packet_exec_detail(
char *name = knot_dname_to_str((*rrsets)[i]->owner);
- dbg_packet("Comparing to RRSet: owner: %s, "
- "type: %s\n", name,
- knot_rrtype_to_string(
- (*rrsets)[i]->type));
+ dbg_packet_detail("Comparing to RRSet: owner: %s, "
+ "type: %s\n", name,
+ knot_rrtype_to_string(
+ (*rrsets)[i]->type));
free(name);
);
if (knot_rrset_compare((*rrsets)[i], rrset,
KNOT_RRSET_COMPARE_HEADER)) {
- //const knot_rrset_t *r = (*rrsets)
/*! \todo Test this!!! */
+ // no duplicate checking here, the packet should
+ // look exactly as it came from wire
int rc = knot_rrset_merge(
(void **)((*rrsets) + i), (void **)&rrset);
if (rc != KNOT_EOK) {
@@ -561,11 +534,12 @@ dbg_packet_exec(
/*----------------------------------------------------------------------------*/
static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
- size_t size, uint16_t rr_count,
- const knot_rrset_t ***rrsets,
- short *rrset_count, short *max_rrsets,
- short default_rrsets,
- knot_packet_t *packet)
+ size_t size, uint16_t rr_count,
+ uint16_t *parsed_rrs,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count, short *max_rrsets,
+ short default_rrsets,
+ knot_packet_t *packet)
{
assert(pos != NULL);
assert(wire != NULL);
@@ -574,12 +548,7 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
assert(max_rrsets != NULL);
assert(packet != NULL);
- dbg_packet("Parsing RRSets starting on position: %zu\n",
- *pos);
-
-// if (*rrsets == NULL) {
-// knot_packet_realloc_rrsets(rrsets, max_rrsets, 0, 1);
-// }
+ dbg_packet("Parsing RRSets starting on position: %zu\n", *pos);
/*
* The RRs from one RRSet may be scattered in the current section.
@@ -589,7 +558,8 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
int err = KNOT_EOK;
knot_rrset_t *rrset = NULL;
- for (int i = 0; i < rr_count; ++i) {
+ /* Start parsing from the first RR not parsed. */
+ for (int i = *parsed_rrs; i < rr_count; ++i) {
rrset = knot_packet_parse_rr(wire, pos, size);
if (rrset == NULL) {
dbg_packet("Failed to parse RR!\n");
@@ -597,13 +567,15 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
break;
}
+ ++(*parsed_rrs);
+
err = knot_packet_add_rrset(rrset, rrsets, rrset_count,
- max_rrsets, default_rrsets, packet,
- KNOT_PACKET_DUPL_MERGE);
+ max_rrsets, default_rrsets, packet,
+ KNOT_PACKET_DUPL_MERGE);
if (err < 0) {
break;
} else if (err > 0) { // merged
- dbg_packet("RRSet merged, freeing.\n");
+ dbg_packet_detail("RRSet merged, freeing.\n");
knot_rrset_deep_free(&rrset, 1, 0, 0); // TODO: ok??
continue;
}
@@ -616,6 +588,23 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
knot_rrset_deep_free(&rrset, 1, 1, 1);
break;
}
+
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_TSIG) {
+ // if there is some TSIG already, treat as malformed
+ if (knot_packet_tsig(packet) != NULL) {
+ err = KNOT_EMALF;
+ break;
+ }
+
+ // First check the format of the TSIG RR
+ if (!tsig_rdata_is_ok(rrset)) {
+ err = KNOT_EMALF;
+ break;
+ }
+
+ // store the TSIG into the packet
+ knot_packet_set_tsig(packet, rrset);
+ }
}
return (err < 0) ? err : KNOT_EOK;
@@ -630,9 +619,9 @@ static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
*/
static void knot_packet_free_allocated_space(knot_packet_t *pkt)
{
- dbg_packet("Freeing additional space in packet.\n");
+ dbg_packet_verb("Freeing additional space in packet.\n");
if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) {
- dbg_packet("Freeing QNAME.\n");
+ dbg_packet_detail("Freeing QNAME.\n");
knot_dname_release(pkt->question.qname);
}
@@ -674,37 +663,55 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet,
int err;
- dbg_packet("Parsing Answer RRs...\n");
+ assert(packet->tsig_rr == NULL);
+
+ dbg_packet_verb("Parsing Answer RRs...\n");
if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
- packet->size, packet->header.ancount, &packet->answer,
- &packet->an_rrsets, &packet->max_an_rrsets,
+ packet->size, packet->header.ancount, &packet->parsed_an,
+ &packet->answer, &packet->an_rrsets, &packet->max_an_rrsets,
DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet)) != KNOT_EOK) {
return err;
}
- dbg_packet("Parsing Authority RRs...\n");
+ if (packet->tsig_rr != NULL) {
+ dbg_packet("TSIG in Answer section.\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet_verb("Parsing Authority RRs...\n");
if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
- packet->size, packet->header.nscount, &packet->authority,
- &packet->ns_rrsets, &packet->max_ns_rrsets,
+ packet->size, packet->header.nscount, &packet->parsed_ns,
+ &packet->authority, &packet->ns_rrsets, &packet->max_ns_rrsets,
DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet)) != KNOT_EOK) {
return err;
}
- dbg_packet("Parsing Additional RRs...\n");
+ if (packet->tsig_rr != NULL) {
+ dbg_packet("TSIG in Authority section.\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet_verb("Parsing Additional RRs...\n");
if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
- packet->size, packet->header.arcount, &packet->additional,
- &packet->ar_rrsets, &packet->max_ar_rrsets,
+ packet->size, packet->header.arcount, &packet->parsed_ar,
+ &packet->additional, &packet->ar_rrsets, &packet->max_ar_rrsets,
DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet)) != KNOT_EOK) {
return err;
}
- dbg_packet("Trying to find OPT RR in the packet.\n");
+ // If TSIG is not the last record
+ if (packet->tsig_rr != NULL
+ && packet->ar_rrsets[packet->additional - 1] != packet->tsig_rr) {
+ dbg_packet("TSIG in Additonal section but not last.\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet_verb("Trying to find OPT RR in the packet.\n");
for (int i = 0; i < packet->ar_rrsets; ++i) {
assert(packet->additional[i] != NULL);
- if (knot_rrset_type(packet->additional[i])
- == KNOT_RRTYPE_OPT) {
- dbg_packet("Found OPT RR, filling.\n");
+ if (knot_rrset_type(packet->additional[i]) == KNOT_RRTYPE_OPT) {
+ dbg_packet_detail("Found OPT RR, filling.\n");
err = knot_edns_new_from_rr(&packet->opt_rr,
packet->additional[i]);
if (err != KNOT_EOK) {
@@ -717,9 +724,10 @@ static int knot_packet_parse_rr_sections(knot_packet_t *packet,
packet->parsed = *pos;
if (*pos < packet->size) {
- // some trailing garbage; ignore, but log
- dbg_response("Packet: %zu bytes of trailing garbage "
- "in packet.\n", packet->size - (*pos));
+ // If there is some trailing garbage, treat the packet as
+ // malformed
+ dbg_packet_verb("Packet: %zu bytes of trailing garbage "
+ "in packet.\n", packet->size - (*pos));
return KNOT_EMALF;
}
@@ -784,12 +792,14 @@ int knot_packet_parse_from_wire(knot_packet_t *packet,
packet->size = size;
packet->free_wireformat = 0;
- //uint8_t *pos = wireformat;
+ if (size < 2) {
+ return KNOT_EMALF;
+ }
+
size_t pos = 0;
- //size_t remaining = size;
- dbg_packet("Parsing wire format of packet (size %zu).\nHeader\n",
- size);
+ dbg_packet_verb("Parsing wire format of packet (size %zu).\nHeader\n",
+ size);
if ((err = knot_packet_parse_header(wireformat, &pos, size,
&packet->header)) != KNOT_EOK) {
return err;
@@ -797,15 +807,14 @@ int knot_packet_parse_from_wire(knot_packet_t *packet,
packet->parsed = pos;
- dbg_packet("Question (prealloc type: %d)...\n", packet->prealloc_type);
+ dbg_packet_verb("Question (prealloc type: %d)...\n",
+ packet->prealloc_type);
if (packet->header.qdcount > 1) {
dbg_packet("QDCOUNT larger than 1, FORMERR.\n");
return KNOT_EMALF;
}
- knot_packet_dump(packet);
-
if (packet->header.qdcount == 1) {
if ((err = knot_packet_parse_question(wireformat, &pos, size,
&packet->question, packet->prealloc_type
@@ -816,18 +825,20 @@ int knot_packet_parse_from_wire(knot_packet_t *packet,
packet->parsed = pos;
}
+dbg_packet_exec_detail(
knot_packet_dump(packet);
+);
if (question_only) {
return KNOT_EOK;
}
/*! \todo Replace by call to parse_rest()? */
- err = knot_packet_parse_rr_sections(packet, &pos);
+ err = knot_packet_parse_rest(packet);
-#ifdef KNOT_PACKET_DEBUG
+dbg_packet_exec_detail(
knot_packet_dump(packet);
-#endif /* KNOT_RESPONSE_DEBUG */
+);
return err;
}
@@ -840,16 +851,21 @@ int knot_packet_parse_rest(knot_packet_t *packet)
return KNOT_EBADARG;
}
-// if (packet->parsed >= packet->size) {
-// return KNOT_EOK;
-// }
-
- if (packet->parsed == packet->size) {
+ if (packet->header.ancount == packet->parsed_an
+ && packet->header.nscount == packet->parsed_ns
+ && packet->header.arcount == packet->parsed_ar
+ && packet->parsed == packet->size) {
return KNOT_EOK;
}
+
+ // If there is less data then required, the next function will find out.
+ // If there is more data than required, it also returns EMALF.
size_t pos = packet->parsed;
+ /*! \todo If we already parsed some part of the packet, it is not ok
+ * to begin parsing from the Answer section.
+ */
return knot_packet_parse_rr_sections(packet, &pos);
}
@@ -866,16 +882,16 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
if (packet->parsed >= packet->size) {
assert(packet->an_rrsets <= packet->header.ancount);
- if (packet->an_rrsets != packet->header.ancount) {
+ if (packet->parsed_an != packet->header.ancount) {
dbg_packet("Parsed less RRs than expected.\n");
return KNOT_EMALF;
} else {
- dbg_packet("Whole packet parsed\n");
+ dbg_packet_detail("Whole packet parsed\n");
return KNOT_EOK;
}
}
- if (packet->an_rrsets == packet->header.ancount) {
+ if (packet->parsed_an == packet->header.ancount) {
assert(packet->parsed < packet->size);
//dbg_packet("Trailing garbage, ignoring...\n");
// there may be other data in the packet
@@ -885,19 +901,20 @@ int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
size_t pos = packet->parsed;
- dbg_packet("Parsing next Answer RR (pos: %zu)...\n", pos);
+ dbg_packet_verb("Parsing next Answer RR (pos: %zu)...\n", pos);
*rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
if (*rr == NULL) {
- dbg_packet("Failed to parse RR!\n");
+ dbg_packet_verb("Failed to parse RR!\n");
return KNOT_EMALF;
}
- dbg_packet("Parsed. Pos: %zu.\n", pos);
+ dbg_packet_detail("Parsed. Pos: %zu.\n", pos);
packet->parsed = pos;
// increment the number of answer RRSets, though there are no saved
// in the packet; it is OK, because packet->answer is NULL
++packet->an_rrsets;
+ ++packet->parsed_an;
return KNOT_EOK;
}
@@ -916,37 +933,37 @@ int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
if (packet->parsed >= packet->size) {
assert(packet->ar_rrsets <= packet->header.arcount);
- if (packet->ar_rrsets != packet->header.arcount) {
+ if (packet->parsed_ar != packet->header.arcount) {
dbg_packet("Parsed less RRs than expected.\n");
return KNOT_EMALF;
} else {
- dbg_packet("Whole packet parsed\n");
+ dbg_packet_detail("Whole packet parsed\n");
return KNOT_EOK;
}
}
- if (packet->ar_rrsets == packet->header.arcount) {
+ if (packet->parsed_ar == packet->header.arcount) {
assert(packet->parsed < packet->size);
- dbg_packet("Trailing garbage, ignoring...\n");
- /*! \todo Do not ignore. */
- return KNOT_EOK;
+ dbg_packet_verb("Trailing garbage, treating as malformed...\n");
+ return KNOT_EMALF;
}
size_t pos = packet->parsed;
- dbg_packet("Parsing next Additional RR (pos: %zu)...\n", pos);
+ dbg_packet_verb("Parsing next Additional RR (pos: %zu)...\n", pos);
*rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
if (*rr == NULL) {
- dbg_packet("Failed to parse RR!\n");
+ dbg_packet_verb("Failed to parse RR!\n");
return KNOT_EMALF;
}
- dbg_packet("Parsed. Pos: %zu.\n", pos);
+ dbg_packet_detail("Parsed. Pos: %zu.\n", pos);
packet->parsed = pos;
// increment the number of answer RRSets, though there are no saved
// in the packet; it is OK, because packet->answer is NULL
++packet->ar_rrsets;
+ ++packet->parsed_ar;
return KNOT_EOK;
}
@@ -1186,7 +1203,7 @@ const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet)
void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr)
{
- packet->tsig_rr = (knot_rrset_t *)tsig_rr;
+ packet->tsig_rr = (knot_rrset_t *)tsig_rr;
}
/*----------------------------------------------------------------------------*/
@@ -1268,19 +1285,19 @@ int knot_packet_contains(const knot_packet_t *packet,
return KNOT_EBADARG;
}
- for (int i = 0; i < packet->header.ancount; ++i) {
+ for (int i = 0; i < packet->an_rrsets; ++i) {
if (knot_rrset_compare(packet->answer[i], rrset, cmp)) {
return 1;
}
}
- for (int i = 0; i < packet->header.nscount; ++i) {
+ for (int i = 0; i < packet->ns_rrsets; ++i) {
if (knot_rrset_compare(packet->authority[i], rrset, cmp)) {
return 1;
}
}
- for (int i = 0; i < packet->header.arcount; ++i) {
+ for (int i = 0; i < packet->ar_rrsets; ++i) {
if (knot_rrset_compare(packet->additional[i], rrset, cmp)) {
return 1;
}
@@ -1307,8 +1324,8 @@ int knot_packet_add_tmp_rrset(knot_packet_t *packet,
}
packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset;
- dbg_packet("Current tmp RRSets count: %d, max count: %d\n",
- packet->tmp_rrsets_count, packet->tmp_rrsets_max);
+ dbg_packet_detail("Current tmp RRSets count: %d, max count: %d\n",
+ packet->tmp_rrsets_count, packet->tmp_rrsets_max);
return KNOT_EOK;
}
@@ -1329,7 +1346,7 @@ void knot_packet_free_tmp_rrsets(knot_packet_t *pkt)
dbg_packet_exec(
char *name = knot_dname_to_str(
(((knot_rrset_t **)(pkt->tmp_rrsets))[i])->owner);
- dbg_packet("Freeing tmp RRSet on ptr: %p (ptr to ptr:"
+ dbg_packet_verb("Freeing tmp RRSet on ptr: %p (ptr to ptr:"
" %p, type: %s, owner: %s)\n",
(((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
&(((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
@@ -1394,14 +1411,6 @@ int knot_packet_question_to_wire(knot_packet_t *packet)
pos += 2;
knot_wire_write_u16(pos, packet->question.qclass);
-// int err = 0;
- // TODO: put the qname into the compression table
-// // TODO: get rid of the numeric constants
-// if ((err = knot_response_store_dname_pos(&packet->compression,
-// packet->question.qname,0, 12, 12)) != KNOT_EOK) {
-// return err;
-// }
-
packet->size += knot_dname_size(packet->question.qname) + 4;
return KNOT_EOK;
@@ -1486,8 +1495,6 @@ void knot_packet_free(knot_packet_t **packet)
knot_packet_free_allocated_space(*packet);
// free the space for wireformat
-// assert((*packet)->wireformat != NULL);
-// free((*packet)->wireformat);
if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) {
free((*packet)->wireformat);
}
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 9e37c12..d76209a 100644..100755
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -138,6 +138,9 @@ struct knot_packet {
short free_wireformat;
size_t parsed;
+ uint16_t parsed_an;
+ uint16_t parsed_ns;
+ uint16_t parsed_ar;
size_t size; /*!< Current wire size of the packet. */
size_t max_size; /*!< Maximum allowed size of the packet. */
diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c
index b76059b..bc3a4db 100644..100755
--- a/src/libknot/packet/query.c
+++ b/src/libknot/packet/query.c
@@ -209,7 +209,7 @@ int knot_query_add_rrset_authority(knot_packet_t *query,
// reserve space for OPT RR
/*! \todo Why here??? */
endp -= query->opt_rr.size;
- /*! \note [TSIG] reserve space for TSIG RR */
+ /* Reserve space for TSIG RR */
endp -= query->tsig_size;
uint8_t *pos = startp;
diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h
index a979641..cda72b9 100644..100755
--- a/src/libknot/packet/query.h
+++ b/src/libknot/packet/query.h
@@ -61,12 +61,6 @@ int knot_query_nsid_requested(const knot_packet_t *query);
int knot_query_edns_supported(const knot_packet_t *query);
-//int knot_query_set_qname(knot_packet_t *query, const knot_dname_t *qname);
-
-//int knot_query_set_qtype(knot_packet_t *query, uint16_t qtype);
-
-//int knot_query_set_qclass(knot_packet_t *query, uint16_t qclass);
-
int knot_query_init(knot_packet_t *query);
int knot_query_set_question(knot_packet_t *query,
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
index 9f6277c..bb4d0f2 100644..100755
--- a/src/libknot/packet/response.c
+++ b/src/libknot/packet/response.c
@@ -164,15 +164,14 @@ static int knot_response_store_dname_pos(knot_compressed_dnames_t *table,
{
dbg_response_exec(
char *name = knot_dname_to_str(dname);
- dbg_response("Putting dname %s into compression table."
- " Labels not matched: %d, position: %zu,"
- ", pointer: %p, unmatched off: %zu\n", name,
- not_matched, pos, dname, unmatched_offset);
+ dbg_response_detail("Putting dname %s into compression table."
+ " Labels not matched: %d, position: %zu,"
+ ", pointer: %p, unmatched off: %zu\n", name,
+ not_matched, pos, dname, unmatched_offset);
free(name);
);
if (pos > KNOT_RESPONSE_MAX_PTR) {
- dbg_response("Pointer larger than it can be, not"
- " saving\n");
+ dbg_response("Pointer larger than it can be, not saving\n");
return KNOT_EDNAMEPTR;
}
@@ -181,11 +180,6 @@ dbg_response_exec(
return KNOT_ENOMEM;
}
- // store the position of the name
-// table->dnames[table->count] = dname;
-// table->offsets[table->count] = pos;
-// ++table->count;
-
/*
* Store positions of ancestors if more than 1 label was not matched.
*
@@ -210,11 +204,11 @@ dbg_response_exec(
parent_pos = unmatched_offset;
}
-dbg_response_exec(
+dbg_response_exec_detail(
char *name = knot_dname_to_str(to_save);
- dbg_response("Putting dname %s into compression table."
- " Position: %zu, pointer: %p\n",
- name, parent_pos, to_save);
+ dbg_response_detail("Putting dname %s into compression table."
+ " Position: %zu, pointer: %p\n",
+ name, parent_pos, to_save);
free(name);
);
@@ -224,7 +218,6 @@ dbg_response_exec(
return KNOT_ENOMEM;
}
-// dbg_response("Saving..\n");
knot_response_compr_save(table, to_save, parent_pos);
/*! \todo Remove '!compr_cs'. */
@@ -234,16 +227,23 @@ dbg_response_exec(
// If case-sensitive search is in place, we should not save the
// node's parent's positions.
- to_save = !compr_cs && (knot_dname_node(to_save) != NULL
- && knot_node_parent(knot_dname_node(to_save))
- != NULL) ? knot_node_owner(knot_node_parent(
+ // Added check to rule out wildcard-covered dnames
+ // (in such case the offset is not right)
+
+ /*! \todo The whole compression requires a serious refactoring.
+ * Or better - a rewrite!
+ */
+ to_save = (!compr_cs && knot_dname_node(to_save) != NULL
+ && knot_node_owner(knot_dname_node(to_save))
+ != to_save
+ && knot_node_parent(knot_dname_node(to_save))
+ != NULL)
+ ? knot_node_owner(knot_node_parent(
knot_dname_node(to_save)))
- : NULL;
+ : NULL;
dbg_response("i: %d\n", i);
parent_pos += knot_dname_label_size(dname, i) + 1;
-// parent_pos += (i > 0)
-// ? knot_dname_label_size(dname, i - 1) + 1 : 0;
++i;
}
@@ -267,22 +267,12 @@ static size_t knot_response_find_dname_pos(
const knot_dname_t *dname, int compr_cs)
{
for (int i = 0; i < table->count; ++i) {
-// dbg_response("Comparing dnames %p and %p\n",
-// dname, table->dnames[i]);
-//dbg_response_exec(
-// char *name = knot_dname_to_str(dname);
-// dbg_response("(%s and ", name);
-// name = knot_dname_to_str(table->dnames[i]);
-// dbg_response("%s)\n", name);
-// free(name);
-//);
- //if (table->dnames[i] == dname) {
int ret = (compr_cs)
? knot_dname_compare_cs(table->dnames[i], dname)
: knot_dname_compare(table->dnames[i], dname);
if (ret == 0) {
- dbg_response("Found offset: %zu\n",
- table->offsets[i]);
+ dbg_response_detail("Found offset: %zu\n",
+ table->offsets[i]);
return table->offsets[i];
}
}
@@ -319,7 +309,7 @@ static int knot_response_put_dname_ptr(const knot_dname_t *dname,
memcpy(wire, knot_dname_name(dname), size);
knot_wire_put_pointer(wire + size, offset);
- dbg_response("Size of the dname with ptr: %d\n", size + 2);
+ dbg_response_detail("Size of the dname with ptr: %d\n", size + 2);
return size + 2;
}
@@ -343,15 +333,9 @@ static int knot_response_compress_dname(const knot_dname_t *dname,
knot_compr_t *compr, uint8_t *dname_wire, size_t max, int compr_cs)
{
int size = 0;
- /*!
- * \todo Compress!!
- *
- * if pos == 0, do not store the position!
- */
// try to find the name or one of its ancestors in the compr. table
#ifdef COMPRESSION_PEDANTIC
- //knot_dname_t *to_find = knot_dname_copy(dname);
knot_dname_t *to_find = (knot_dname_t *)dname;
int copied = 0;
#else
@@ -361,11 +345,11 @@ static int knot_response_compress_dname(const knot_dname_t *dname,
int not_matched = 0;
while (to_find != NULL && knot_dname_label_count(to_find) != 0) {
-dbg_response_exec(
+dbg_response_exec_detail(
char *name = knot_dname_to_str(to_find);
- dbg_response("Searching for name %s in the compression"
- " table, not matched labels: %d\n", name,
- not_matched);
+ dbg_response_detail("Searching for name %s in the compression"
+ " table, not matched labels: %d\n", name,
+ not_matched);
free(name);
);
offset = knot_response_find_dname_pos(compr->table, to_find,
@@ -396,17 +380,17 @@ dbg_response_exec(
|| knot_node_owner(knot_dname_node(to_find)) != to_find
|| knot_node_parent(knot_dname_node(to_find))
== NULL) {
- dbg_response("compr_cs: %d\n", compr_cs);
- dbg_response("knot_dname_node(to_find, 1) == %p"
+ dbg_response_detail("compr_cs: %d\n", compr_cs);
+ dbg_response_detail("knot_dname_node(to_find, 1) == %p"
"\n", knot_dname_node(to_find));
if (knot_dname_node(to_find) != NULL) {
- dbg_response("knot_node_owner(knot_dname_node("
- "to_find, 1)) = %p, to_find = %p\n",
- knot_node_owner(knot_dname_node(to_find)),
- to_find);
- dbg_response("knot_node_parent(knot_dname_node("
- "to_find, 1), 1) = %p\n",
+ dbg_response_detail("knot_node_owner(knot_dname_node("
+ "to_find, 1)) = %p, to_find = %p\n",
+ knot_node_owner(knot_dname_node(to_find)),
+ to_find);
+ dbg_response_detail("knot_node_parent(knot_dname_node("
+ "to_find, 1), 1) = %p\n",
knot_node_parent(knot_dname_node(to_find)));
}
break;
@@ -417,7 +401,7 @@ dbg_response_exec(
knot_node_parent(knot_dname_node(to_find))));
to_find = knot_node_owner(
knot_node_parent(knot_dname_node(to_find)));
- dbg_response("New to_find: %p\n", to_find);
+ dbg_response_detail("New to_find: %p\n", to_find);
}
#endif
}
@@ -428,10 +412,10 @@ dbg_response_exec(
}
#endif
- dbg_response("Max size available for domain name: %zu\n", max);
+ dbg_response_detail("Max size available for domain name: %zu\n", max);
if (offset > 0) { // found such dname somewhere in the packet
- dbg_response("Found name in the compression table.\n");
+ dbg_response_detail("Found name in the compression table.\n");
assert(offset >= KNOT_WIRE_HEADER_SIZE);
size = knot_response_put_dname_ptr(dname, not_matched, offset,
dname_wire, max);
@@ -439,7 +423,7 @@ dbg_response_exec(
return KNOT_ESPACE;
}
} else {
- dbg_response("Not found, putting whole name.\n");
+ dbg_response_detail("Not found, putting whole name.\n");
// now just copy the dname without compressing
if (dname->size > max) {
return KNOT_ESPACE;
@@ -458,8 +442,7 @@ dbg_response_exec(
if (knot_response_store_dname_pos(compr->table, dname, not_matched,
compr->wire_pos, offset, compr_cs)
!= 0) {
- dbg_response("Compression info could not be stored."
- "\n");
+ dbg_response_detail("Compression info could not be stored.\n");
}
return size;
@@ -488,7 +471,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
{
int size = 0;
- dbg_response("Max size: %zu, owner pos: %zu, owner size: %d\n",
+ dbg_response_detail("Max size: %zu, owner pos: %zu, owner size: %d\n",
max_size, compr->owner.pos, compr->owner.size);
if (size + ((compr->owner.pos == 0
@@ -498,7 +481,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
return KNOT_ESPACE;
}
- dbg_response("Owner position: %zu\n", compr->owner.pos);
+ dbg_response_detail("Owner position: %zu\n", compr->owner.pos);
// put owner if needed (already compressed)
if (compr->owner.pos == 0 || compr->owner.pos > KNOT_RESPONSE_MAX_PTR) {
@@ -507,28 +490,28 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
*rrset_wire += compr->owner.size;
size += compr->owner.size;
} else {
- dbg_response("Putting pointer: %zu\n",
- compr->owner.pos);
+ dbg_response_detail("Putting pointer: %zu\n",
+ compr->owner.pos);
knot_wire_put_pointer(*rrset_wire, compr->owner.pos);
*rrset_wire += 2;
size += 2;
}
- dbg_response("Max size: %zu, size: %d\n", max_size, size);
+ dbg_response_detail("Max size: %zu, size: %d\n", max_size, size);
- dbg_response("Wire format:\n");
+ dbg_response_detail("Wire format:\n");
// put rest of RR 'header'
knot_wire_write_u16(*rrset_wire, rrset->type);
- dbg_response(" Type: %u\n", rrset->type);
+ dbg_response_detail(" Type: %u\n", rrset->type);
*rrset_wire += 2;
knot_wire_write_u16(*rrset_wire, rrset->rclass);
- dbg_response(" Class: %u\n", rrset->rclass);
+ dbg_response_detail(" Class: %u\n", rrset->rclass);
*rrset_wire += 2;
knot_wire_write_u32(*rrset_wire, rrset->ttl);
- dbg_response(" TTL: %u\n", rrset->ttl);
+ dbg_response_detail(" TTL: %u\n", rrset->ttl);
*rrset_wire += 4;
// save space for RDLENGTH
@@ -538,7 +521,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
size += 10;
compr->wire_pos += size;
- dbg_response("Max size: %zu, size: %d\n", max_size, size);
+ dbg_response_detail("Max size: %zu, size: %d\n", max_size, size);
knot_rrtype_descriptor_t *desc =
knot_rrtype_descriptor_by_type(rrset->type);
@@ -561,8 +544,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
return KNOT_ESPACE;
}
- dbg_response("Compressed dname size: %d\n",
- ret);
+ dbg_response_detail("Compressed dname size: %d\n", ret);
*rrset_wire += ret;
rdlength += ret;
compr->wire_pos += ret;
@@ -579,8 +561,8 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
// save whole domain name
memcpy(*rrset_wire, dname->name, dname->size);
- dbg_response("Uncompressed dname size: %d\n",
- dname->size);
+ dbg_response_detail("Uncompressed dname size: %d\n",
+ dname->size);
*rrset_wire += dname->size;
rdlength += dname->size;
compr->wire_pos += dname->size;
@@ -596,8 +578,8 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
// copy just the rdata item data (without size)
memcpy(*rrset_wire, raw_data + 1, raw_data[0]);
- dbg_response("Raw data size: %d\n",
- raw_data[0]);
+ dbg_response_detail("Raw data size: %d\n",
+ raw_data[0]);
*rrset_wire += raw_data[0];
rdlength += raw_data[0];
compr->wire_pos += raw_data[0];
@@ -606,7 +588,7 @@ static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
}
}
- dbg_response("Max size: %zu, size: %d\n", max_size, size);
+ dbg_response_detail("Max size: %zu, size: %d\n", max_size, size);
assert(size + rdlength <= max_size);
size += rdlength;
@@ -639,12 +621,12 @@ static int knot_response_rrset_to_wire(const knot_rrset_t *rrset,
knot_compressed_dnames_t *compr,
int compr_cs)
{
-dbg_response_exec(
+dbg_response_exec_verb(
char *name = knot_dname_to_str(rrset->owner);
- dbg_response("Converting RRSet with owner %s, type %s\n",
- name, knot_rrtype_to_string(rrset->type));
+ dbg_response_verb("Converting RRSet with owner %s, type %s\n",
+ name, knot_rrtype_to_string(rrset->type));
free(name);
- dbg_response(" Size before: %zu\n", *size);
+ dbg_response_verb(" Size before: %zu\n", *size);
);
// if no RDATA in RRSet, return
@@ -652,10 +634,6 @@ dbg_response_exec(
return KNOT_EOK;
}
- //uint8_t *rrset_wire = (uint8_t *)malloc(PREALLOC_RRSET_WIRE);
- //short rrset_size = 0;
-
- //uint8_t *owner_wire = (uint8_t *)malloc(rrset->owner->size);
/*
* We may pass the current position to the compression function
* because if the owner will be put somewhere, it will be on the
@@ -665,7 +643,6 @@ dbg_response_exec(
*/
knot_compr_t compr_info;
- //compr_info.new_entries = 0;
compr_info.table = compr;
compr_info.wire_pos = wire_pos;
compr_info.owner.pos = 0;
@@ -674,8 +651,8 @@ dbg_response_exec(
knot_response_compress_dname(rrset->owner, &compr_info,
owner_tmp, max_size, compr_cs);
- dbg_response(" Owner size: %d, position: %zu\n",
- compr_info.owner.size, compr_info.owner.pos);
+ dbg_response_detail(" Owner size: %d, position: %zu\n",
+ compr_info.owner.size, compr_info.owner.pos);
if (compr_info.owner.size < 0) {
return KNOT_ESPACE;
}
@@ -698,20 +675,17 @@ dbg_response_exec(
return KNOT_ESPACE;
}
- dbg_response("RR of size %d added.\n", ret);
+ dbg_response_verb("RR of size %d added.\n", ret);
rrset_size += ret;
++rrs;
} while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
- //memcpy(*pos, rrset_wire, rrset_size);
- //*size += rrset_size;
- //*pos += rrset_size;
// the whole RRSet did fit in
assert (rrset_size <= max_size);
*size += rrset_size;
- dbg_response(" Size after: %zu\n", *size);
+ dbg_response_verb(" Size after: %zu\n", *size);
return rrs;
}
@@ -750,8 +724,8 @@ static int knot_response_try_add_rrset(const knot_rrset_t **rrsets,
dbg_response_exec(
char *name = knot_dname_to_str(rrset->owner);
- dbg_response("\nAdding RRSet with owner %s and type %s: \n",
- name, knot_rrtype_to_string(rrset->type));
+ dbg_response_verb("\nAdding RRSet with owner %s and type %s: \n",
+ name, knot_rrtype_to_string(rrset->type));
free(name);
);
@@ -764,11 +738,11 @@ dbg_response_exec(
if (rrs >= 0) {
rrsets[(*rrset_count)++] = rrset;
resp->size += size;
- dbg_response("RRset added, size: %zu, RRs: %d, total "
- "size of response: %zu\n\n", size, rrs,
- resp->size);
+ dbg_response_verb("RRset added, size: %zu, RRs: %d, total "
+ "size of response: %zu\n\n", size, rrs,
+ resp->size);
} else if (tc) {
- dbg_response("Setting TC bit.\n");
+ dbg_response_verb("Setting TC bit.\n");
knot_wire_flags_set_tc(&resp->header.flags1);
knot_wire_set_tc(resp->wireformat);
}
@@ -825,8 +799,8 @@ static int knot_response_realloc_wc_nodes(const knot_node_t ***nodes,
short *max_count,
short default_max_count, short step)
{
- dbg_packet("Max count: %d, default max count: %d\n",
- *max_count, default_max_count);
+ dbg_packet_detail("Max count: %d, default max count: %d\n",
+ *max_count, default_max_count);
int free_old = (*max_count) != default_max_count;
const knot_node_t **old_nodes = *nodes;
@@ -896,13 +870,10 @@ int knot_response_init_from_query(knot_packet_t *response,
// copy the header from the query
memcpy(&response->header, &query->header, sizeof(knot_header_t));
-// memmove(&response->header, &query->header, sizeof(knot_header_t));
// copy the Question section (but do not copy the QNAME)
memcpy(&response->question, &query->question,
sizeof(knot_question_t));
-// memmove(&response->question, &query->question,
-// sizeof(knot_question_t));
int err = 0;
// put the qname into the compression table
@@ -917,18 +888,16 @@ int knot_response_init_from_query(knot_packet_t *response,
size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname);
assert(response->max_size >= to_copy);
-// printf("Resp init from query: Copying from: %p to: %p size: %d\n",
-// response->wireformat, query->wireformat,
-// to_copy);
-// printf("Resp init from query: Question name size: %d Query name size: %d\n",
-// knot_dname_size(response->question.qname),
-// knot_dname_size(query->question.qname));
memcpy(response->wireformat, query->wireformat, to_copy);
response->size = to_copy;
// set the qr bit to 1
knot_wire_flags_set_qr(&response->header.flags1);
knot_wire_set_qr(response->wireformat);
+
+ // clear TC flag
+ knot_wire_flags_clear_tc(&response->header.flags1);
+ knot_wire_clear_tc(response->wireformat);
// clear AD flag
knot_wire_flags_clear_ad(&response->header.flags2);
@@ -940,8 +909,11 @@ int knot_response_init_from_query(knot_packet_t *response,
// set counts to 0
response->header.ancount = 0;
+ knot_wire_set_ancount(response->wireformat, 0);
response->header.nscount = 0;
+ knot_wire_set_nscount(response->wireformat, 0);
response->header.arcount = 0;
+ knot_wire_set_arcount(response->wireformat, 0);
response->query = query;
@@ -1039,16 +1011,14 @@ int knot_response_add_opt(knot_packet_t *resp,
if (override_max_size && resp->max_size > 0
&& resp->max_size < opt_rr->payload) {
-// return KNOT_EPAYLOAD;
return KNOT_EOK;
}
// set max size (less is OK)
if (override_max_size) {
dbg_response("Overriding max size to: %u\n",
- resp->opt_rr.payload);
+ resp->opt_rr.payload);
return knot_packet_set_max_size(resp, resp->opt_rr.payload);
- //resp->max_size = resp->opt_rr.payload;
}
return KNOT_EOK;
@@ -1065,7 +1035,7 @@ int knot_response_add_rrset_answer(knot_packet_t *response,
return KNOT_EBADARG;
}
- dbg_response("add_rrset_answer()\n");
+ dbg_response_verb("add_rrset_answer()\n");
assert(response->header.arcount == 0);
assert(response->header.nscount == 0);
@@ -1081,9 +1051,9 @@ int knot_response_add_rrset_answer(knot_packet_t *response,
return KNOT_EOK;
}
- dbg_response("Trying to add RRSet to Answer section.\n");
- dbg_response("RRset: %p\n", rrset);
- dbg_response("Owner: %p\n", rrset->owner);
+ dbg_response_verb("Trying to add RRSet to Answer section.\n");
+ dbg_response_detail("RRset: %p\n", rrset);
+ dbg_response_detail("Owner: %p\n", rrset->owner);
int rrs = knot_response_try_add_rrset(response->answer,
&response->an_rrsets, response,
@@ -1132,7 +1102,7 @@ int knot_response_add_rrset_authority(knot_packet_t *response,
return KNOT_EOK;
}
- dbg_response("Trying to add RRSet to Authority section.\n");
+ dbg_response_verb("Trying to add RRSet to Authority section.\n");
int rrs = knot_response_try_add_rrset(response->authority,
&response->ns_rrsets, response,
@@ -1188,7 +1158,7 @@ int knot_response_add_rrset_additional(knot_packet_t *response,
return KNOT_EOK;
}
- dbg_response("Trying to add RRSet to Additional section.\n");
+ dbg_response_verb("Trying to add RRSet to Additional section.\n");
int rrs = knot_response_try_add_rrset(response->additional,
&response->ar_rrsets, response,
@@ -1283,7 +1253,7 @@ int knot_response_add_wildcard_node(knot_packet_t *response,
response->wildcard_nodes.snames[response->wildcard_nodes.count] = sname;
++response->wildcard_nodes.count;
- dbg_response("Current wildcard nodes count: %d, max count: %d\n",
+ dbg_response_verb("Current wildcard nodes count: %d, max count: %d\n",
response->wildcard_nodes.count,
response->wildcard_nodes.max);
diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h
index d3e66f3..d3e66f3 100644..100755
--- a/src/libknot/packet/response.h
+++ b/src/libknot/packet/response.h
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
index 8e9e8c1..1cdd339 100644..100755
--- a/src/libknot/rdata.c
+++ b/src/libknot/rdata.c
@@ -210,8 +210,8 @@ knot_rdata_t *knot_rdata_new()
/*----------------------------------------------------------------------------*/
int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
- size_t *pos, size_t total_size, size_t rdlength,
- const knot_rrtype_descriptor_t *desc)
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
{
int i = 0;
uint8_t item_type;
@@ -249,7 +249,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
return KNOT_ERROR;
}
items[i].dname = dname;
- //*pos += dname->size;
parsed += pos2 - *pos;
*pos = pos2;
dname = 0;
@@ -309,7 +308,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
break;
case 3:
pos2 = *pos;
- //fprintf(stderr, "reading dname from pos: %zu\n", pos2);
dname = knot_dname_parse_from_wire(
wire, &pos2, total_size, NULL);
if (dname == NULL) {
@@ -331,12 +329,9 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
memcpy((uint8_t *)(items[i].raw_data + 1),
knot_dname_name(dname),
knot_dname_size(dname));
-
-// items[i].dname = dname;
- //*pos += dname->size;
+
parsed += pos2 - *pos;
-
- //fprintf(stderr, "read %zu bytes.\n", parsed);
+
*pos = pos2;
knot_dname_free(&dname);
@@ -374,9 +369,7 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
memcpy(items[i].raw_data + 1, wire + *pos, item_size);
*pos += item_size;
parsed += item_size;
- } else if (item_type == KNOT_RDATA_WF_BINARY/*
- || item_type == KNOT_RDATA_WF_IPSECGATEWAY*/) {
-// fprintf(stderr, "item_size was 0, creating empty rdata item.\n");
+ } else if (item_type == KNOT_RDATA_WF_BINARY) {
// in this case we are at the end of the RDATA
// and should create an empty RDATA item
items[i].raw_data = (uint16_t *)malloc(2);
@@ -389,8 +382,6 @@ int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
} else if (item_type != KNOT_RDATA_WF_COMPRESSED_DNAME
&& item_type != KNOT_RDATA_WF_UNCOMPRESSED_DNAME
&& item_type != KNOT_RDATA_WF_LITERAL_DNAME) {
-// fprintf(stderr, "RDATA item not set (i: %d), type: %u"
-// " RDATA item type: %d\n", i, desc->type ,item_type);
assert(0);
}
@@ -519,6 +510,25 @@ int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, uint pos,
/*----------------------------------------------------------------------------*/
+int knot_rdata_count(const knot_rdata_t *rdata)
+{
+ if (rdata == NULL) {
+ return 0;
+ }
+
+ int count = 1;
+ const knot_rdata_t *r = rdata;
+
+ while (r->next != NULL && r->next != rdata) {
+ r = r->next;
+ ++count;
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+
void knot_rdata_free(knot_rdata_t **rdata)
{
if (rdata == NULL || *rdata == NULL) {
@@ -541,134 +551,16 @@ void knot_rdata_deep_free(knot_rdata_t **rdata, uint type,
return;
}
- knot_rdata_free_items((*rdata)->items, (*rdata)->count, type,
- free_all_dnames);
+ if ((*rdata)->items != NULL) {
+ knot_rdata_free_items((*rdata)->items, (*rdata)->count, type,
+ free_all_dnames);
+ }
free(*rdata);
*rdata = NULL;
}
/*----------------------------------------------------------------------------*/
-/* CLEANUP */
-//uint knot_rdata_wire_size(const knot_rdata_t *rdata,
-// const uint8_t *format)
-//{
-// uint size = 0;
-
-// for (int i = 0; i < rdata->count; ++i) {
-// switch (format[i]) {
-// case KNOT_RDATA_WF_COMPRESSED_DNAME:
-// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
-// case KNOT_RDATA_WF_LITERAL_DNAME:
-// size += knot_dname_size(rdata->items[i].dname);
-// break;
-// case KNOT_RDATA_WF_BYTE:
-// size += 1;
-// break;
-// case KNOT_RDATA_WF_SHORT:
-// size += 2;
-// break;
-// case KNOT_RDATA_WF_LONG:
-// size += 4;
-// break;
-// case KNOT_RDATA_WF_A:
-// size += 4;
-// break;
-// case KNOT_RDATA_WF_AAAA:
-// size += 16;
-// break;
-// case KNOT_RDATA_WF_BINARY:
-// case KNOT_RDATA_WF_APL: // saved as binary
-// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
-// size += rdata->items[i].raw_data[0];
-// break;
-// case KNOT_RDATA_WF_TEXT:
-// case KNOT_RDATA_WF_BINARYWITHLENGTH:
-// size += rdata->items[i].raw_data[0] + 1;
-// break;
-// default:
-// assert(0);
-// }
-// }
-// return size;
-//}
-
-/*----------------------------------------------------------------------------*/
-
-//int knot_rdata_to_wire(const knot_rdata_t *rdata, const uint8_t *format,
-// uint8_t *buffer, uint buf_size)
-//{
-// uint copied = 0;
-// uint8_t tmp[KNOT_MAX_RDATA_WIRE_SIZE];
-// uint8_t *to = tmp;
-
-// for (int i = 0; i < rdata->count; ++i) {
-// assert(copied < KNOT_MAX_RDATA_WIRE_SIZE);
-
-// const uint8_t *from = (uint8_t *)rdata->items[i].raw_data;
-// uint size = 0;
-
-// switch (format[i]) {
-// case KNOT_RDATA_WF_COMPRESSED_DNAME:
-// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
-// case KNOT_RDATA_WF_LITERAL_DNAME:
-// size = knot_dname_size(rdata->items[i].dname);
-// from = knot_dname_name(rdata->items[i].dname);
-
-// break;
-// case KNOT_RDATA_WF_BYTE:
-// size = 1;
-// break;
-// case KNOT_RDATA_WF_SHORT:
-// size = 2;
-// break;
-// case KNOT_RDATA_WF_LONG:
-// size = 4;
-// break;
-// case KNOT_RDATA_WF_A:
-// size = 4;
-// break;
-// case KNOT_RDATA_WF_AAAA:
-// size = 16;
-// break;
-// case KNOT_RDATA_WF_TEXT:
-// case KNOT_RDATA_WF_BINARYWITHLENGTH:
-// // size stored in the first two bytes, but in little
-// // endian and we need only the lower byte from it
-// *to = *from; // lower byte is the first in little endian
-// to += 1;
-// case KNOT_RDATA_WF_BINARY:
-// case KNOT_RDATA_WF_APL: // saved as binary
-// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
-// // size stored in the first two bytes, those bytes
-// // must not be copied
-// size = rdata->items[i].raw_data[0];
-// from += 2; // skip the first two bytes
-// break;
-// default:
-// assert(0);
-// }
-
-// assert(size != 0);
-// assert(copied + size < KNOT_MAX_RDATA_WIRE_SIZE);
-
-// memcpy(to, from, size);
-// to += size;
-// copied += size;
-// }
-
-// if (copied > buf_size) {
-// dbg_rdata("Not enough place allocated for function "
-// "knot_rdata_to_wire(). Allocated %u, need %u\n",
-// buf_size, copied);
-// return -1;
-// }
-
-// memcpy(buffer, tmp, copied);
-// return 0;
-//}
-
-/*----------------------------------------------------------------------------*/
knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata,
uint16_t type, int copy_dnames)
@@ -728,33 +620,19 @@ int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2,
int cmp = 0;
for (int i = 0; i < count; ++i) {
- /* CLEANUP */
-// const uint8_t *data1, *data2;
-// int size1, size2;
-
if (format[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
format[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
format[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
cmp = knot_dname_compare(r1->items[i].dname,
r2->items[i].dname);
-// data1 = knot_dname_name(r1->items[i].dname);
-// data2 = knot_dname_name(r2->items[i].dname);
-// size1 = knot_dname_size(r2->items[i].dname);
-// size2 = knot_dname_size(r2->items[i].dname);
} else {
cmp = knot_rdata_compare_binary(
(uint8_t *)(r1->items[i].raw_data + 1),
(uint8_t *)(r2->items[i].raw_data + 1),
r1->items[i].raw_data[0],
r1->items[i].raw_data[0]);
-// data1 = (uint8_t *)(r1->items[i].raw_data + 1);
-// data2 = (uint8_t *)(r2->items[i].raw_data + 1);
-// size1 = r1->items[i].raw_data[0];
-// size2 = r1->items[i].raw_data[0];
}
-// cmp =
-
if (cmp != 0) {
return cmp;
}
@@ -894,3 +772,48 @@ uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata)
return knot_wire_read_u16((uint8_t *)(rdata->items[0].raw_data + 1));
}
+
+/*---------------------------------------------------------------------------*/
+
+uint8_t knot_rdata_nsec3_algorithm(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return 0;
+ }
+
+ return *((uint8_t *)(rdata->items[0].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 3) {
+ // this is actually valid value...what to return??
+ return 0;
+ }
+
+ return knot_wire_read_u16((uint8_t *)(rdata->items[2].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 4) {
+ return 0;
+ }
+
+ return *((uint8_t *)(rdata->items[3].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 4) {
+ return NULL;
+ }
+
+ return ((uint8_t *)(rdata->items[3].raw_data + 1)) + 1;
+}
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
index bb45f50..acd678f 100644..100755
--- a/src/libknot/rdata.h
+++ b/src/libknot/rdata.h
@@ -220,6 +220,8 @@ int knot_rdata_item_set_dname(knot_rdata_t *rdata, unsigned int pos,
int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, unsigned int pos,
uint16_t *raw_data);
+int knot_rdata_count(const knot_rdata_t *rdata);
+
/*!
* \brief Copies the given RDATA.
*
@@ -335,6 +337,11 @@ uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata);
uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata);
+uint8_t knot_rdata_nsec3_algorithm(const knot_rdata_t *rdata);
+uint16_t knot_rdata_nsec3_iterations(const knot_rdata_t *rdata);
+uint8_t knot_rdata_nsec3_salt_length(const knot_rdata_t *rdata);
+const uint8_t *knot_rdata_nsec3_salt(const knot_rdata_t *rdata);
+
#endif /* _KNOT_RDATA_H */
/*! @} */
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index d665c63..ef7fce8 100644..100755
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -32,7 +32,7 @@
/*----------------------------------------------------------------------------*/
static void knot_rrset_disconnect_rdata(knot_rrset_t *rrset,
- knot_rdata_t *prev, knot_rdata_t *rdata)
+ knot_rdata_t *prev, knot_rdata_t *rdata)
{
if (prev == NULL) {
// find the previous RDATA in the series, as its pointer must
@@ -113,6 +113,53 @@ int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata)
/*----------------------------------------------------------------------------*/
+int knot_rrset_add_rdata_order(knot_rrset_t *rrset, knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ dbg_rrset("rrset: add_rdata_order: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (rrset->rdata == NULL) {
+ /* Easy peasy, just insert the first item. */
+ rrset->rdata = rdata;
+ rrset->rdata->next = rrset->rdata;
+ } else {
+ knot_rdata_t *walk = NULL;
+ char found = 0;
+ knot_rdata_t *insert_after = rrset->rdata;
+ while (((walk = knot_rrset_rdata_get_next(rrset,
+ walk)) != NULL) &&
+ (!found)) {
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+ assert(desc);
+ int cmp = knot_rdata_compare(rdata, walk,
+ desc->wireformat);
+ if (cmp < 1) {
+ /* We've found place for this item. */
+ } else if (cmp == 0) {
+ /* This item will not be inserted. */
+ found = 1;
+ insert_after = NULL;
+ }
+ assert(cmp > 1);
+ /* Continue the search. */
+ insert_after = walk;
+ }
+ if (insert_after != NULL) {
+ rdata->next = insert_after->next;
+ insert_after->next = rdata;
+ } else {
+ ;
+ /* Consider returning something different than EOK. */
+ }
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
knot_rdata_t *knot_rrset_remove_rdata(knot_rrset_t *rrset,
const knot_rdata_t *rdata)
{
@@ -167,19 +214,30 @@ int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
int rc;
if (rrset->rrsigs != NULL) {
if (dupl == KNOT_RRSET_DUPL_MERGE) {
- rc = knot_rrset_merge((void **)&rrset->rrsigs,
- (void **)&rrsigs);
+ rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs,
+ (void **)&rrsigs);
if (rc != KNOT_EOK) {
return rc;
} else {
return 1;
}
} else if (dupl == KNOT_RRSET_DUPL_SKIP) {
+// rc = knot_rrset_merge_no_dupl((void **)&rrset->rrsigs,
+// (void **)&rrsigs);
+// if (rc != KNOT_EOK) {
+// return rc;
+// } else {
+// return 1;
+// }
return 2;
} else if (dupl == KNOT_RRSET_DUPL_REPLACE) {
rrset->rrsigs = rrsigs;
}
} else {
+ if (rrset->ttl != rrsigs->ttl) {
+ rrsigs->ttl = rrset->ttl;
+ }
+
rrset->rrsigs = rrsigs;
}
@@ -291,6 +349,10 @@ knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
{
+ if (rrset == NULL) {
+ return 0;
+ }
+
int count = 0;
const knot_rdata_t *rdata = rrset->rdata;
@@ -341,7 +403,7 @@ int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2)
}
// compare RDATA sets (order is not significant)
- const knot_rdata_t *rdata1= knot_rrset_rdata(r1);
+ const knot_rdata_t *rdata1 = knot_rrset_rdata(r1);
const knot_rdata_t *rdata2;
// find all RDATA from r1 in r2
@@ -434,7 +496,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
*pos += 2;
size += 10;
-// compr->wire_pos += size;
dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size);
@@ -465,7 +526,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
knot_dname_size(dname));
*pos += knot_dname_size(dname);
rdlength += knot_dname_size(dname);
-// compr->wire_pos += dname->size;
break;
}
default: {
@@ -481,7 +541,6 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
dbg_rrset_detail("Raw data size: %d\n", raw_data[0]);
*pos += raw_data[0];
rdlength += raw_data[0];
-// compr->wire_pos += raw_data[0];
break;
}
}
@@ -523,11 +582,11 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
if (ret < 0) {
// some RR didn't fit in, so no RRs should be used
// TODO: remove last entries from compression table
- dbg_rrset_detail("Some RR didn't fit in.\n");
+ dbg_rrset_verb("Some RR didn't fit in.\n");
return KNOT_ESPACE;
}
- dbg_rrset_detail("RR of size %d added.\n", ret);
+ dbg_rrset_verb("RR of size %d added.\n", ret);
rrset_size += ret;
++rrs;
} while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
@@ -556,7 +615,7 @@ int knot_rrset_compare(const knot_rrset_t *r1,
int res = ((r1->rclass == r2->rclass)
&& (r1->type == r2->type)
- && (r1->ttl == r2->ttl)
+// && (r1->ttl == r2->ttl)
&& knot_dname_compare(r1->owner, r2->owner) == 0);
if (cmp == KNOT_RRSET_COMPARE_WHOLE && res) {
@@ -571,7 +630,8 @@ int knot_rrset_compare(const knot_rrset_t *r1,
/*----------------------------------------------------------------------------*/
-int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
+ int copy_rdata_dnames)
{
if (from == NULL || to == NULL) {
return KNOT_EBADARG;
@@ -582,14 +642,14 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
*to = (knot_rrset_t *)calloc(1, sizeof(knot_rrset_t));
CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
- //(*to)->owner = knot_dname_deep_copy(from->owner);
(*to)->owner = from->owner;
knot_dname_retain((*to)->owner);
(*to)->rclass = from->rclass;
(*to)->ttl = from->ttl;
(*to)->type = from->type;
if (from->rrsigs != NULL) {
- ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs);
+ ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs,
+ copy_rdata_dnames);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(to, 1, 0, 0);
return ret;
@@ -601,8 +661,19 @@ int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
/*! \note Order of RDATA will be reversed. */
while (rdata != NULL) {
- ret = knot_rrset_add_rdata(*to, knot_rdata_deep_copy(rdata,
- knot_rrset_type(from), 1));
+ knot_rdata_t *rdata_copy = knot_rdata_deep_copy(rdata,
+ knot_rrset_type(from),
+ copy_rdata_dnames);
+dbg_rrset_exec_detail(
+ char *name = knot_dname_to_str(knot_rrset_owner(from));
+ dbg_rrset_detail("Copying RDATA from RRSet with owner: %s, type"
+ ": %s. Old RDATA ptr: %p, new RDATA ptr: %p\n",
+ name,
+ knot_rrtype_to_string(knot_rrset_type(from)),
+ rdata, rdata_copy);
+ free(name);
+);
+ ret = knot_rrset_add_rdata(*to, rdata_copy);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(to, 1, 1, 1);
return ret;
@@ -632,6 +703,7 @@ int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to)
void knot_rrset_rotate(knot_rrset_t *rrset)
{
+ /*! \todo Maybe implement properly one day. */
//rrset->rdata = rrset->rdata->next;
}
@@ -659,12 +731,6 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
return;
}
-// char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
-// char *type = knot_rrtype_to_string(knot_rrset_type(*rrset));
-// fprintf(stderr, "Deleting RRSet (%p) %s, type %s, rdata: %p\n",
-// *rrset, name, type, (*rrset)->rdata);
-// free(name);
-
if (free_rdata) {
knot_rdata_t *tmp_rdata;
knot_rdata_t *next_rdata;
@@ -679,7 +745,6 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
tmp_rdata = next_rdata;
}
-// printf("test: %p\n", tmp_rdata->next->next);
assert(tmp_rdata == NULL
|| tmp_rdata->next == (*rrset)->rdata);
@@ -693,10 +758,7 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
free_rdata_dnames);
}
- /*! \todo Release owner every time? */
- //if (free_owner) {
- knot_dname_release((*rrset)->owner);
- //}
+ knot_dname_release((*rrset)->owner);
free(*rrset);
*rrset = NULL;
@@ -711,8 +773,7 @@ int knot_rrset_merge(void **r1, void **r2)
if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0)
|| rrset1->rclass != rrset2->rclass
- || rrset1->type != rrset2->type
- || rrset1->ttl != rrset2->ttl) {
+ || rrset1->type != rrset2->type) {
return KNOT_EBADARG;
}
@@ -744,6 +805,155 @@ int knot_rrset_merge(void **r1, void **r2)
}
tmp_rdata->next = rrset1->rdata;
+ rrset2->rdata = rrset1->rdata;
+
+ return KNOT_EOK;
+}
+
+int knot_rrset_merge_no_dupl(void **r1, void **r2)
+{
+ if (r1 == NULL || r2 == NULL) {
+ dbg_rrset("rrset: merge_no_dupl: NULL arguments.");
+ return KNOT_EBADARG;
+ }
+
+ knot_rrset_t *rrset1 = (knot_rrset_t *)(*r1);
+ knot_rrset_t *rrset2 = (knot_rrset_t *)(*r2);
+ if (rrset1 == NULL || rrset2 == NULL) {
+ dbg_rrset("rrset: merge_no_dupl: NULL arguments.");
+ return KNOT_EBADARG;
+ }
+
+dbg_rrset_exec_detail(
+ char *name = knot_dname_to_str(rrset1->owner);
+ dbg_rrset_detail("rrset: merge_no_dupl: Merging %s.\n", name);
+ free(name);
+);
+
+ if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0)
+ || rrset1->rclass != rrset2->rclass
+ || rrset1->type != rrset2->type) {
+ dbg_rrset("rrset: merge_no_dupl: Trying to merge "
+ "different RRs.\n");
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *walk2 = rrset2->rdata;
+
+ // no RDATA in RRSet 1
+ if (rrset1->rdata == NULL && rrset2->rdata != NULL) {
+ /*
+ * This function has to assure that there are no duplicates in
+ * second RRSet's list. This can be done by putting a first
+ * item from the second list as a first item of the first list
+ * and then simply continuing with inserting items from second
+ * list to the first one.
+ *
+ * However, we must store pointer to second item in the second
+ * list, as the 'next' pointer of the first item will be altered
+ */
+
+ // Store pointer to the second item in RRSet2 RDATA so that
+ // we later start from this item.
+ walk2 = knot_rrset_rdata_get_next(rrset2, walk2);
+ assert(walk2 == rrset2->rdata->next || walk2 == NULL);
+
+ // Connect the first item from second list to the first list.
+ rrset1->rdata = rrset2->rdata;
+ // Close the cyclic list (by pointing to itself).
+ rrset1->rdata->next = rrset1->rdata;
+ } else if (rrset2->rdata == NULL) {
+ return KNOT_EOK;
+ }
+
+ /*
+ * Check that rrset1 does not contain any rdata from rrset2, if so
+ * such RDATA shall not be inserted.
+ */
+
+ /* Get last RDATA from first rrset, we'll need it for insertion. */
+ knot_rdata_t *insert_after = rrset1->rdata;
+ while (insert_after->next != rrset1->rdata) {
+ dbg_rrset_detail("rrset: merge_dupl: first rrset rdata: %p.\n",
+ insert_after);
+ insert_after = insert_after->next;
+ }
+ assert(insert_after->next == rrset1->rdata);
+
+ while (walk2 != NULL) {
+ knot_rdata_t *walk1 = rrset1->rdata;
+ char dupl = 0;
+ while ((walk1 != NULL) &&
+ !dupl) {
+ const knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset1->type);
+ assert(desc);
+ /* If walk1 and walk2 are equal, do not insert. */
+ dupl = !knot_rdata_compare(walk1, walk2,
+ desc->wireformat);
+ walk1 = knot_rrset_rdata_get_next(rrset1, walk1);
+ dbg_rrset_detail("rrset: merge_dupl: next item: %p.\n",
+ walk1);
+ }
+ if (!dupl) {
+ dbg_rrset_detail("rrset: merge_dupl: Inserting "
+ "unique item (%p).\n",
+ walk2);
+ knot_rdata_t *tmp = walk2;
+ /*
+ * We need to move this, insertion
+ * will corrupt pointers.
+ */
+ walk2 = knot_rrset_rdata_get_next(rrset2, walk2);
+ /* Insert this item at the end of first list. */
+ tmp->next = insert_after->next;
+ insert_after->next = tmp;
+ insert_after = tmp;
+ /*!< \todo This message has to be removed after bugfix. */
+ dbg_rrset_detail("rrset: merge_no_dupl: Insert after=%p"
+ ", tmp=%p, tmp->next=%p, "
+ " rrset1->rdata=%p"
+ "\n",
+ insert_after, tmp, tmp->next,
+ rrset1->rdata);
+ assert(tmp->next == rrset1->rdata);
+ } else {
+ dbg_rrset_detail("rrset: merge_dupl: Skipping and "
+ "freeing duplicated item "
+ "of type: %s (%p).\n",
+ knot_rrtype_to_string(rrset1->type),
+ walk2);
+ /*
+ * Not freeing this item will result in a leak.
+ * Since this operation destroys the second
+ * list, we have to free the item here.
+ */
+ knot_rdata_t *tmp = walk2;
+ dbg_rrset_detail("rrset: merge_dupl: freeing: %p.\n",
+ tmp);
+ walk2 = knot_rrset_rdata_get_next(rrset2, walk2);
+ knot_rdata_deep_free(&tmp, rrset1->type, 1);
+ assert(tmp == NULL);
+ /* Maybe caller should be warned about this. */
+ }
+ }
+
+ assert(walk2 == NULL);
+dbg_rrset_exec_detail(
+ dbg_rrset_detail("rrset: merge_dupl: RDATA after merge:\n ");
+ knot_rdata_t *walk1 = rrset1->rdata;
+ while (walk1 != NULL) {
+ dbg_rrset_detail("%p ->\n", walk1);
+ walk1 = knot_rrset_rdata_get_next(rrset1, walk1);
+ }
+ dbg_rrset_detail("rrset: merge_dupl: RDATA after merge: r1:%p r2: %p\n",
+ rrset1->rdata, rrset2->rdata);
+);
+ /*
+ * Since there is a possibility of corrupted list for second RRSet, it
+ * is safer to set its list to NULL, so that it cannot be used.
+ */
+ rrset2->rdata = NULL;
return KNOT_EOK;
}
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
index b5c11dc..36d8da5 100644..100755
--- a/src/libknot/rrset.h
+++ b/src/libknot/rrset.h
@@ -105,6 +105,21 @@ knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
*/
int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata);
+/*!
+ * \brief Adds the given RDATA to the RRSet but will not insert duplicated data.
+ *
+ * \warning Should be only used to insert one RDATA. (NO lists)
+ *
+ * \param rrset RRSet to add the RDATA to.
+ * \param rdata RDATA to add to the RRSet.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ *
+ * \todo Provide some function for comparing RDATAs.
+ */
+int knot_rrset_add_rdata_order(knot_rrset_t *rrset, knot_rdata_t *rdata);
+
knot_rdata_t * knot_rrset_remove_rdata(knot_rrset_t *rrset,
const knot_rdata_t *rdata);
@@ -249,7 +264,8 @@ int knot_rrset_compare(const knot_rrset_t *r1,
knot_rrset_compare_type_t cmp);
/*! \todo Add unit test. */
-int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to);
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to,
+ int copy_rdata_dnames);
/*! \todo Add unit test. */
int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to);
@@ -296,9 +312,9 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
* \brief Merges two RRSets.
*
* Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after
- * the list of RDATAs in \a r1. \a r2 is unaffected by this, though you must not
- * destroy the RDATAs in \a r2 as they are now also in \a r1. (You may use
- * function knot_rrset_free() though, as it does not touch RDATAs).
+ * the list of RDATAs in \a r1. You must not
+ * destroy the RDATAs in \a r2 as they are now identical to RDATAs in \a r1.
+ * (You may use function knot_rrset_free() though, as it does not touch RDATAs).
*
* \note Member \a rrsigs is preserved from the first RRSet.
*
@@ -311,6 +327,26 @@ void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
*/
int knot_rrset_merge(void **r1, void **r2);
+
+/*!
+ * \brief Merges two RRSets, but will discard and free any duplicates in \a r2.
+ *
+ * Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after
+ * the list of RDATAs in \a r1. You must not
+ * destroy the RDATAs in \a r2 as they are now identical to RDATAs in \a r1.
+ * (You may use function knot_rrset_free() though, as it does not touch RDATAs).
+ *
+ * \note Member \a rrsigs is preserved from the first RRSet.
+ *
+ * \param r1 Pointer to RRSet to be merged into.
+ * \param r2 Poitner to RRSet to be merged.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG if the RRSets could not be merged, because their
+ * Owner, Type, Class or TTL does not match.
+ */
+int knot_rrset_merge_no_dupl(void **r1, void **r2);
+
#endif /* _KNOT_RRSET_H_ */
/*! @} */
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
index 148fc87..173538c 100644..100755
--- a/src/libknot/tsig-op.c
+++ b/src/libknot/tsig-op.c
@@ -21,20 +21,27 @@
#include <time.h>
#include "common.h"
+#include "common/base64.h"
#include "tsig.h"
#include "tsig-op.h"
#include "util/wire.h"
-#include "libknot/util/conv.h"
#include "util/error.h"
#include "util/debug.h"
#include "consts.h"
const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
-
+const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300; // default Fudge value
+enum b64_const {
+ B64BUFSIZE = 65535
+};
static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr)
{
+ if (tsig_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
if (!alg_name) {
return KNOT_EMALF;
@@ -53,6 +60,10 @@ static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr)
static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
const knot_key_t *tsig_key)
{
+ if (tsig_rr == NULL || tsig_key == NULL) {
+ return KNOT_EBADARG;
+ }
+
const knot_dname_t *tsig_name = knot_rrset_owner(tsig_rr);
if (!tsig_name) {
return KNOT_EMALF;
@@ -94,28 +105,25 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
return KNOT_TSIG_EBADSIG;
}
- /* Create digest, using length of the algorithm. */
-// *digest = malloc(sizeof(uint8_t) * tsig_alg_digest_length(tsig_alg));
-// if (!digest) {
-// ERR_ALLOC_FAILED;
-// return KNOT_ENOMEM;
-// }
-
/* Decode key from Base64. */
char decoded_key[B64BUFSIZE];
-
- int decoded_key_size = b64_pton(key->secret, (uint8_t *)decoded_key,
- B64BUFSIZE);
+
+ size_t decoded_key_size = B64BUFSIZE;
+ int ret = base64_decode(key->secret, strlen(key->secret),
+ decoded_key,
+ &decoded_key_size);
+ if (ret != 1) {
+ dbg_tsig("TSIG: New decode function failed! (%d)\n", ret);
+ return KNOT_ERROR;
+ }
+
if (decoded_key_size < 0) {
dbg_tsig("TSIG: Could not decode Base64\n");
return KNOT_ERROR;
}
-
+
dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size);
dbg_tsig_detail("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
-
-// dbg_tsig_detail("TSIG: using this wire for digest calculation\n");
-// dbg_tsig_hex_detail(wire, wire_len);
dbg_tsig_detail("Wire for signing is %zu bytes long.\n", wire_len);
/* Compute digest. */
@@ -152,6 +160,7 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr,
uint64_t prev_time_signed)
{
if (!tsig_rr) {
+ dbg_tsig("TSIG: check_time_signed: NULL argument.\n");
return KNOT_EBADARG;
}
@@ -184,21 +193,14 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr,
return KNOT_EOK;
}
-static int knot_tsig_write_tsig_timers(uint8_t *wire,
- const knot_rrset_t *tsig_rr)
-{
- // put time signed
- knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
-
- // put fudge
- knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
-
- return KNOT_EOK;
-}
-
static int knot_tsig_write_tsig_variables(uint8_t *wire,
const knot_rrset_t *tsig_rr)
{
+ if (wire == NULL || tsig_rr == NULL) {
+ dbg_tsig("TSIG: write tsig variables: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+
/* Copy TSIG variables - starting with key name. */
const knot_dname_t *tsig_owner = knot_rrset_owner(tsig_rr);
if (!tsig_owner) {
@@ -210,24 +212,23 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
memcpy(wire + offset, knot_dname_name(tsig_owner),
sizeof(uint8_t) * knot_dname_size(tsig_owner));
- dbg_tsig("TSIG: write variables: written owner (tsig alg): \n");
- /*knot_rrset_class(tsig_rr));*/
- dbg_tsig_hex_detail(wire + offset, knot_dname_size(tsig_owner));
+ dbg_tsig_verb("TSIG: write variables: written owner (tsig alg): \n");
+ dbg_tsig_hex_verb(wire + offset, knot_dname_size(tsig_owner));
offset += knot_dname_size(tsig_owner);
/*!< \todo which order? */
/* Copy class. */
knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr));
- dbg_tsig("TSIG: write variables: written CLASS: %u - ",
- knot_rrset_class(tsig_rr));
+ dbg_tsig_verb("TSIG: write variables: written CLASS: %u - ",
+ knot_rrset_class(tsig_rr));
dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t));
offset += sizeof(uint16_t);
/* Copy TTL - always 0. */
knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr));
- dbg_tsig("TSIG: write variables: written TTL: %u - ",
- knot_rrset_ttl(tsig_rr));
+ dbg_tsig_verb("TSIG: write variables: written TTL: %u - ",
+ knot_rrset_ttl(tsig_rr));
dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t));
offset += sizeof(uint32_t);
@@ -237,28 +238,25 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
dbg_tsig("TSIG: write variables: no algorithm name.\n");
return KNOT_EBADARG;
}
-// alg_name = knot_dname_new_from_str("HMAC-MD5.SIG-ALG.REG.INT.",
- //strlen("HMAC-MD5.SIG-ALG.REG.INT."),
- //NULL);
memcpy(wire + offset, knot_dname_name(alg_name),
sizeof(uint8_t) * knot_dname_size(alg_name));
offset += knot_dname_size(alg_name);
- dbg_tsig_detail("TSIG: write variables: written alg name: %s\n",
- knot_dname_to_str(alg_name));
+ dbg_tsig_verb("TSIG: write variables: written alg name: %s\n",
+ knot_dname_to_str(alg_name));
/* Following data are written in network order. */
/* Time signed. */
knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr));
offset += 6;
- dbg_tsig_detail("TSIG: write variables: time signed: %llu - ",
- tsig_rdata_time_signed(tsig_rr));
+ dbg_tsig_verb("TSIG: write variables: time signed: %llu - ",
+ tsig_rdata_time_signed(tsig_rr));
dbg_tsig_hex_detail(wire + offset - 6, 6);
/* Fudge. */
knot_wire_write_u16(wire + offset, tsig_rdata_fudge(tsig_rr));
offset += sizeof(uint16_t);
- dbg_tsig_detail("TSIG: write variables: fudge: %hu\n",
- tsig_rdata_fudge(tsig_rr));
+ dbg_tsig_verb("TSIG: write variables: fudge: %hu\n",
+ tsig_rdata_fudge(tsig_rr));
/* TSIG error. */
knot_wire_write_u16(wire + offset, tsig_rdata_error(tsig_rr));
offset += sizeof(uint16_t);
@@ -279,7 +277,7 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
offset += sizeof(uint16_t);
/* Skip the length. */
- dbg_tsig_detail("Copying other data.\n");
+ dbg_tsig_verb("Copying other data.\n");
memcpy(wire + offset, other_data, other_data_length);
return KNOT_EOK;
@@ -288,7 +286,14 @@ static int knot_tsig_write_tsig_variables(uint8_t *wire,
static int knot_tsig_wire_write_timers(uint8_t *wire,
const knot_rrset_t *tsig_rr)
{
+ if (wire == NULL || tsig_rr == NULL) {
+ dbg_tsig("TSIG: write timers: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+
+ //write time signed
knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+ //write fudge
knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
return KNOT_EOK;
@@ -308,21 +313,14 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
/* Create tmp TSIG. */
int ret = KNOT_EOK;
-// knot_rrset_t *tmp_tsig =
-// knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
-// if (!tmp_tsig) {
-// return KNOT_ENOMEM;
-// }
-
-// tsig_rdata_store_current_time(tmp_tsig);
/*
* Create tmp wire, it should contain message
* plus request mac plus tsig varibles.
*/
- dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
- msg_len, request_mac_len,
- tsig_rdata_tsig_variables_length(tmp_tsig));
+ dbg_tsig_verb("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, request_mac_len,
+ tsig_rdata_tsig_variables_length(tmp_tsig));
size_t wire_len = sizeof(uint8_t) *
(msg_len + request_mac_len + ((request_mac_len > 0)
? 2 : 0) +
@@ -339,23 +337,21 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
/* Copy the request MAC - should work even if NULL. */
if (request_mac_len > 0) {
- dbg_tsig_detail("Copying request MAC size\n");
+ dbg_tsig_verb("Copying request MAC size\n");
knot_wire_write_u16(pos, request_mac_len);
pos += 2;
}
- dbg_tsig("Copying request mac.\n");
+ dbg_tsig_verb("Copying request mac.\n");
memcpy(pos, request_mac, sizeof(uint8_t) * request_mac_len);
dbg_tsig_detail("TSIG: create wire: request mac: ");
dbg_tsig_hex_detail(pos, request_mac_len);
pos += request_mac_len;
/* Copy the original message. */
- dbg_tsig("Copying original message.\n");
+ dbg_tsig_verb("Copying original message.\n");
memcpy(pos, msg, msg_len);
- dbg_tsig_detail("TSIG: create wire: original message: \n");
- //dbg_tsig_hex_detail(pos, msg_len);
pos += msg_len;
/* Copy TSIG variables. */
- dbg_tsig("Writing TSIG variables.\n");
+ dbg_tsig_verb("Writing TSIG variables.\n");
ret = knot_tsig_write_tsig_variables(pos, tmp_tsig);
if (ret != KNOT_EOK) {
dbg_tsig("TSIG: create wire: failed to write TSIG "
@@ -375,20 +371,8 @@ static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
return ret;
}
-// assert(digest_tmp_len > 0);
free(wire);
-// if (digest_tmp_len > *digest_len) {
-// *digest_len = 0;
-// return KNOT_ESPACE;
-// }
-
-// knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
-
- // everything went ok, save the digest to the output parameter
-// memcpy(digest, digest_tmp, digest_tmp_len);
-// *digest_len = digest_tmp_len;
-
return KNOT_EOK;
}
@@ -411,9 +395,9 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
* Create tmp wire, it should contain message
* plus request mac plus tsig varibles.
*/
- dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
- msg_len, prev_mac_len,
- tsig_rdata_tsig_timers_length());
+ dbg_tsig_verb("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, prev_mac_len,
+ tsig_rdata_tsig_timers_length());
size_t wire_len = sizeof(uint8_t) *
(msg_len + prev_mac_len +
tsig_rdata_tsig_timers_length() + 2);
@@ -426,27 +410,24 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
memset(wire, 0, wire_len);
/* Copy the request MAC - should work even if NULL. */
- dbg_tsig("Copying request mac size.\n");
+ dbg_tsig_verb("Copying request mac size.\n");
knot_wire_write_u16(wire, prev_mac_len);
- dbg_tsig("Copying request mac.\n");
+ dbg_tsig_verb("Copying request mac.\n");
memcpy(wire + 2, prev_mac, sizeof(uint8_t) * prev_mac_len);
dbg_tsig_detail("TSIG: create wire: request mac: ");
dbg_tsig_hex_detail(wire + 2, prev_mac_len);
/* Copy the original message. */
- dbg_tsig("Copying original message.\n");
+ dbg_tsig_verb("Copying original message.\n");
memcpy(wire + prev_mac_len + 2, msg, msg_len);
- dbg_tsig_detail("TSIG: create wire: original message: \n");
- //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len);
/* Copy TSIG variables. */
- dbg_tsig("Writing TSIG timers.\n");
- ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len + 2,
+ dbg_tsig_verb("Writing TSIG timers.\n");
+ ret = knot_tsig_wire_write_timers(wire + prev_mac_len + msg_len + 2,
tmp_tsig);
-// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len,
-// tmp_tsig);
if (ret != KNOT_EOK) {
dbg_tsig("TSIG: create wire: failed to write TSIG "
"timers: %s\n", knot_strerror(ret));
+ free(wire);
return ret;
}
@@ -457,6 +438,7 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
knot_strerror(ret));
*digest_len = 0;
+ free(wire);
return ret;
}
@@ -478,7 +460,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name);
if (!key_name_copy) {
- dbg_tsig_detail("TSIG: key_name_copy = NULL\n");
+ dbg_tsig("TSIG: key_name_copy = NULL\n");
return KNOT_ENOMEM;
}
@@ -488,14 +470,14 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
/* Should be retained by rrsig or freed, release. */
knot_dname_release(key_name_copy);
if (!tmp_tsig) {
- dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ dbg_tsig("TSIG: tmp_tsig = NULL\n");
return KNOT_ENOMEM;
}
/* Create rdata for TSIG RR. */
knot_rdata_t *rdata = knot_rdata_new();
if (!rdata) {
- dbg_tsig_detail("TSIG: rdata = NULL\n");
+ dbg_tsig("TSIG: rdata = NULL\n");
knot_rrset_free(&tmp_tsig);
return KNOT_ENOMEM;
}
@@ -510,7 +492,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
knot_rdata_item_t *items =
malloc(sizeof(knot_rdata_item_t) * desc->length);
if (!items) {
- dbg_tsig_detail("TSIG: items = NULL\n");
+ dbg_tsig("TSIG: items = NULL\n");
ERR_ALLOC_FAILED;
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
@@ -521,7 +503,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
int ret = knot_rdata_set_items(rdata, items, desc->length);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ dbg_tsig("TSIG: rdata_set_items returned %s\n",
+ knot_strerror(ret));
free(items);
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
@@ -554,7 +537,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
tsig_rdata_set_other_data(tmp_tsig, 0, 0);
}
- tsig_rdata_set_fudge(tmp_tsig, 300); /*! \todo Bleeding eyes :-) */
+ tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
/* Set original ID */
tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
@@ -583,13 +566,11 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
int rr_count = 0;
tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
- //knot_rrset_dump(tmp_tsig, 1);
-
/* Write RRSet to wire */
ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
&tsig_wire_len, &rr_count);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
*digest_len = 0;
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
@@ -633,7 +614,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
/* Create rdata for TSIG RR. */
knot_rdata_t *rdata = knot_rdata_new();
if (!rdata) {
- dbg_tsig_detail("TSIG: rdata = NULL\n");
+ dbg_tsig("TSIG: rdata = NULL\n");
knot_rrset_free(&tmp_tsig);
return KNOT_ENOMEM;
}
@@ -642,7 +623,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
ret = knot_rrset_add_rdata(tmp_tsig, rdata);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: could not add rdata\n");
+ dbg_tsig("TSIG: could not add rdata\n");
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
return ret;
@@ -656,7 +637,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
knot_rdata_item_t *items =
malloc(sizeof(knot_rdata_item_t) * desc->length);
if (!items) {
- dbg_tsig_detail("TSIG: items = NULL\n");
+ dbg_tsig("TSIG: items = NULL\n");
ERR_ALLOC_FAILED;
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
@@ -667,7 +648,8 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
ret = knot_rdata_set_items(rdata, items, desc->length);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ dbg_tsig("TSIG: rdata_set_items returned %s\n",
+ knot_strerror(ret));
knot_rrset_free(&tmp_tsig);
knot_rdata_free(&rdata);
free(items);
@@ -676,7 +658,7 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
free(items);
tsig_rdata_store_current_time(tmp_tsig);
- tsig_rdata_set_fudge(tmp_tsig, 300);
+ tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
/* Create wire to be signed. */
size_t wire_len = prev_digest_len + to_sign_len
@@ -739,8 +721,8 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
/* Set other data. */
tsig_rdata_set_other_data(tmp_tsig, 0, NULL);
- dbg_tsig_detail("Message max length: %zu, message length: %zu\n",
- msg_max_len, *msg_len);
+ dbg_tsig_verb("Message max length: %zu, message length: %zu\n",
+ msg_max_len, *msg_len);
size_t tsig_wire_size = msg_max_len - *msg_len;
int rr_count = 0;
@@ -788,7 +770,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
return ret;
}
- dbg_tsig("TSIG: time checked.\n");
+ dbg_tsig_verb("TSIG: time checked.\n");
/* Check that libknot knows the algorithm. */
ret = knot_tsig_check_algorithm(tsig_rr);
@@ -796,7 +778,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
return ret;
}
- dbg_tsig("TSIG: algorithm checked.\n");
+ dbg_tsig_verb("TSIG: algorithm checked.\n");
/* Check that key is valid, ie. the same as given in args. */
ret = knot_tsig_check_key(tsig_rr, tsig_key);
@@ -804,22 +786,15 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
return ret;
}
- dbg_tsig("TSIG: key validity checked.\n");
+ dbg_tsig_verb("TSIG: key validity checked.\n");
/* Time OK algorithm OK, key name OK - do digest. */
/* Calculate the size of TSIG RR. */
size_t tsig_len = tsig_wire_actsize(tsig_rr);
- dbg_tsig_detail("TSIG: check digest: wire before strip: \n");
- //dbg_tsig_hex_detail(wire, size);
-
/* Strip the TSIG. */
size -= tsig_len;
- dbg_tsig_detail("TSIG: check digest: wire after strip (stripped %zu):\n",
- tsig_len);
- //dbg_tsig_hex_detail(wire, size);
-
uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size);
if (!wire_to_sign) {
ERR_ALLOC_FAILED;
@@ -858,16 +833,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
return ret;
}
-// uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
-// size_t digest_tmp_len = 0;
-// ret = knot_tsig_compute_digest(wire, size, digest_tmp,
-// &digest_tmp_len, tsig_key);
-// if (ret != KNOT_EOK) {
-// dbg_tsig("TSIG: digest could not be calculated\n");
-// return ret;
-// }
-
- dbg_tsig("TSIG: digest calculated\n");
+ dbg_tsig_verb("TSIG: digest calculated\n");
/* Compare MAC from TSIG RR RDATA with just computed digest. */
@@ -880,17 +846,16 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr);
if (mac_length != tsig_alg_digest_length(alg)) {
- dbg_tsig("TSIG: calculated digest length and given length do not match!\n");
+ dbg_tsig("TSIG: calculated digest length and given length do "
+ "not match!\n");
return KNOT_TSIG_EBADSIG;
}
-// assert(tsig_alg_digest_length(alg) == mac_length);
-
- dbg_tsig("TSIG: calc digest : ");
- dbg_tsig_hex(digest_tmp, digest_tmp_len);
+ dbg_tsig_verb("TSIG: calc digest : ");
+ dbg_tsig_hex_verb(digest_tmp, digest_tmp_len);
- dbg_tsig("TSIG: given digest: ");
- dbg_tsig_hex(tsig_mac, mac_length);
+ dbg_tsig_verb("TSIG: given digest: ");
+ dbg_tsig_hex_verb(tsig_mac, mac_length);
if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp,
mac_length) != 0) {
@@ -904,7 +869,7 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
const uint8_t *wire, size_t size,
const knot_key_t *tsig_key)
{
- dbg_tsig_verb("tsig_server_check()\n");
+ dbg_tsig("tsig_server_check()\n");
return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key,
0, 0);
}
@@ -915,7 +880,7 @@ int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
const knot_key_t *tsig_key,
uint64_t prev_time_signed)
{
- dbg_tsig_verb("tsig_client_check()\n");
+ dbg_tsig("tsig_client_check()\n");
return knot_tsig_check_digest(tsig_rr, wire, size, request_mac,
request_mac_len, tsig_key,
prev_time_signed, 0);
@@ -928,9 +893,7 @@ int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
const knot_key_t *tsig_key,
uint64_t prev_time_signed)
{
-// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest,
-// prev_digest_len, tsig_key);
- dbg_tsig_verb("tsig_client_check_next()\n");
+ dbg_tsig("tsig_client_check_next()\n");
return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest,
prev_digest_len, tsig_key,
prev_time_signed, 1);
@@ -950,15 +913,14 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
knot_dname_t *key_name =
knot_dname_deep_copy(knot_rrset_owner(tsig_rr));
if (key_name == NULL) {
- dbg_tsig_detail("TSIG: failed to copy owner\n");
+ dbg_tsig("TSIG: failed to copy owner\n");
return KNOT_ERROR;
}
knot_rrset_t *tmp_tsig =
- knot_rrset_new(key_name,
- KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ knot_rrset_new(key_name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
if (!tmp_tsig) {
- dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ dbg_tsig("TSIG: tmp_tsig = NULL\n");
knot_dname_free(&key_name);
return KNOT_ENOMEM;
}
@@ -966,7 +928,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
/* Create rdata for TSIG RR. */
knot_rdata_t *rdata = knot_rdata_new();
if (!rdata) {
- dbg_tsig_detail("TSIG: rdata = NULL\n");
+ dbg_tsig("TSIG: rdata = NULL\n");
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
return KNOT_ENOMEM;
}
@@ -981,7 +943,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
knot_rdata_item_t *items =
malloc(sizeof(knot_rdata_item_t) * desc->length);
if (items == NULL) {
- dbg_tsig_detail("TSIG: items = NULL\n");
+ dbg_tsig("TSIG: items = NULL\n");
ERR_ALLOC_FAILED;
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
return KNOT_ENOMEM;
@@ -992,8 +954,8 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
int ret = knot_rdata_set_items(rdata, items, desc->length);
free(items);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: rdata_set_items returned %s\n",
- knot_strerror(ret));
+ dbg_tsig("TSIG: rdata_set_items returned %s\n",
+ knot_strerror(ret));
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
return ret;
}
@@ -1001,14 +963,18 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
knot_dname_t *alg_name =
knot_dname_deep_copy(tsig_rdata_alg_name(tsig_rr));
if (alg_name == NULL) {
- dbg_tsig_detail("TSIG: failed to copy alg name\n");
+ dbg_tsig("TSIG: failed to copy alg name\n");
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
return KNOT_ERROR;
}
tsig_rdata_set_alg_name(tmp_tsig, alg_name);
tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(tsig_rr));
- tsig_rdata_set_fudge(tmp_tsig, tsig_rdata_fudge(tsig_rr));
+
+ /* Comparing to BIND it was found out that the Fudge should always be
+ * set to the server's value.
+ */
+ tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
tsig_rdata_set_mac(tmp_tsig, 0, NULL);
knot_dname_release(alg_name); /* Already copied in tsig_rdata_set_alg_name() */
@@ -1029,7 +995,7 @@ int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
&tsig_wire_len, &rr_count);
if (ret != KNOT_EOK) {
- dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ dbg_tsig("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
return ret;
}
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
index 07a84a8..07a84a8 100644..100755
--- a/src/libknot/tsig-op.h
+++ b/src/libknot/tsig-op.h
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
index e8df92e..86b7f9d 100644..100755
--- a/src/libknot/tsig.c
+++ b/src/libknot/tsig.c
@@ -319,21 +319,21 @@ tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
/* Get the algorithm name. */
const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig);
if (!alg_name) {
- dbg_tsig_detail("TSIG: rdata: cannot get algorithm name.\n");
+ dbg_tsig("TSIG: rdata: cannot get algorithm name.\n");
return KNOT_TSIG_ALG_NULL;
}
/* Convert alg name to string. */
char *name = knot_dname_to_str(alg_name);
if (!name) {
- dbg_tsig_detail("TSIG: rdata: cannot convert alg name.\n");
+ dbg_tsig("TSIG: rdata: cannot convert alg name.\n");
return KNOT_TSIG_ALG_NULL;
}
knot_lookup_table_t *item = knot_lookup_by_name(tsig_alg_table, name);
free(name);
if (!item) {
- dbg_tsig_detail("TSIG: rdata: unknown algorithm.\n");
+ dbg_tsig("TSIG: rdata: unknown algorithm.\n");
return KNOT_TSIG_ALG_NULL;
}
@@ -342,7 +342,7 @@ tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -366,7 +366,7 @@ uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig)
uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -390,7 +390,7 @@ uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig)
const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -424,7 +424,7 @@ size_t tsig_rdata_mac_length(const knot_rrset_t *tsig)
uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -448,7 +448,7 @@ uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig)
uint16_t tsig_rdata_error(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -472,7 +472,7 @@ uint16_t tsig_rdata_error(const knot_rrset_t *tsig)
const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -491,7 +491,7 @@ const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig)
uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig)
{
- /*!< \note How about assert. Or maybe change API??? */
+ /*! \note How about assert. Or maybe change API??? */
if (!tsig) {
return 0;
}
@@ -558,6 +558,9 @@ uint16_t tsig_alg_digest_length(tsig_algorithm_t alg)
size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
{
+ if (tsig == NULL) {
+ return 0;
+ }
/* Key name, Algorithm name and Other data have variable lengths. */
const knot_dname_t *key_name = knot_rrset_owner(tsig);
if (!key_name) {
@@ -569,13 +572,6 @@ size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
return 0;
}
-// dbg_tsig_detail("key_name: %.*s (size: %u) alg_name: %.*s (size: %u)\n", knot_dname_size(key_name),
-// key_name->name, alg_name->size, alg_name->name,
-// key_name->size, alg_name->size);
-
-// dbg_tsig_hex_detail(key_name->name, key_name->size);
-// dbg_tsig_hex_detail(alg_name->name, alg_name->size);
-
uint16_t other_data_length = tsig_rdata_other_data_length(tsig);
return knot_dname_size(key_name) + knot_dname_size(alg_name) +
@@ -594,7 +590,7 @@ int tsig_rdata_store_current_time(knot_rrset_t *tsig)
return KNOT_EBADARG;
}
time_t curr_time = time(NULL);
- /*!< \todo bleeding eyes. */
+ /*! \todo bleeding eyes. */
tsig_rdata_set_time_signed(tsig, (uint64_t)curr_time);
return KNOT_EOK;
}
@@ -612,6 +608,10 @@ const char* tsig_alg_to_str(tsig_algorithm_t alg)
size_t tsig_wire_maxsize(const knot_key_t* key)
{
+ if (key == NULL) {
+ return 0;
+ }
+
size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1;
return knot_dname_size(key->name) +
@@ -632,6 +632,10 @@ size_t tsig_wire_maxsize(const knot_key_t* key)
size_t tsig_wire_actsize(const knot_rrset_t *tsig)
{
+ if (tsig == NULL) {
+ return 0;
+ }
+
return knot_dname_size(knot_rrset_owner(tsig)) +
sizeof(uint16_t) + /* TYPE */
sizeof(uint16_t) + /* CLASS */
@@ -648,3 +652,12 @@ size_t tsig_wire_actsize(const knot_rrset_t *tsig)
tsig_rdata_other_data_length(tsig);
}
+int tsig_rdata_is_ok(const knot_rrset_t *tsig)
+{
+ return (tsig
+ && knot_rrset_rdata(tsig) != NULL
+ && knot_rdata_item_count(knot_rrset_rdata(tsig)) >= 7
+ && tsig_rdata_alg_name(tsig) != NULL
+ && tsig_rdata_time_signed(tsig) != 0);
+}
+
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
index 73fa832..249184d 100644..100755
--- a/src/libknot/tsig.h
+++ b/src/libknot/tsig.h
@@ -140,6 +140,8 @@ uint16_t tsig_alg_digest_length(tsig_algorithm_t alg);
size_t tsig_wire_maxsize(const knot_key_t *key);
size_t tsig_wire_actsize(const knot_rrset_t *tsig);
+int tsig_rdata_is_ok(const knot_rrset_t *tsig);
+
#endif /* _KNOT_TSIG_H_ */
/*! @} */
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
index 1c3d3b9..502a858 100644..100755
--- a/src/libknot/updates/changesets.c
+++ b/src/libknot/updates/changesets.c
@@ -115,23 +115,20 @@ int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
// try to find the RRSet in the list of RRSets, but search backwards
// as it is probable that the last RRSet is the one to which the RR
// belongs
- int i = *count - 1;
-
- while (i >= 0 && !knot_changeset_rrsets_match((*rrsets)[i], rr)) {
- --i;
- }
- if (i >= 0) {
- // found RRSet to merge the new one into
- if (knot_rrset_merge((void **)&(*rrsets)[i],
- (void **)&rr) != KNOT_EOK) {
+ // Just check the last RRSet. If the RR belongs to it, merge it,
+ // otherwise just add the RR to the end of the list
+
+ if (*count > 0
+ && knot_changeset_rrsets_match((*rrsets)[*count - 1], rr)) {
+ // Create changesets exactly as they came, with possibly
+ // duplicate records
+ if (knot_rrset_merge((void **)&(*rrsets)[*count - 1],
+ (void **)&rr) != KNOT_EOK) {
return KNOT_ERROR;
}
- // remove the RR
- /*! \todo does this make sense? */
- knot_rrset_free(&rr); // used to be deep free with all 1's
-
+ knot_rrset_free(&rr);
return KNOT_EOK;
} else {
return knot_changeset_add_rrset(rrsets, count, allocated, rr);
@@ -211,7 +208,7 @@ int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
int knot_changesets_check_size(knot_changesets_t *changesets)
{
/* Check if allocated is sufficient. */
- if (changesets->count <= changesets->allocated) {
+ if (changesets->count < changesets->allocated) {
return KNOT_EOK;
}
@@ -243,7 +240,6 @@ int knot_changesets_check_size(knot_changesets_t *changesets)
void knot_free_changeset(knot_changeset_t **changeset)
{
- /* XXX XXX investigate wrong frees. */
assert((*changeset)->add_allocated >= (*changeset)->add_count);
assert((*changeset)->remove_allocated >= (*changeset)->remove_count);
assert((*changeset)->allocated >= (*changeset)->size);
@@ -292,7 +288,3 @@ void knot_free_changesets(knot_changesets_t **changesets)
free(*changesets);
*changesets = NULL;
}
-
-/*---------------------------------------------------------------------------*/
-
-
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index 0570fc9..642b155 100644..100755
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -5,7 +5,7 @@
*
* \brief Structure for representing IXFR/DDNS changeset and its API.
*
- * \addtogroup libknot
+ * \addtogroup xfr
* @{
*/
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
@@ -90,9 +90,9 @@ typedef struct {
int new_rdata_count;
int new_rdata_allocated;
-// /*!
-// * Deleted (without contents) after successful update.
-// */
+ /*!
+ * Deleted (without contents) after successful update.
+ */
knot_node_t **old_nodes;
int old_nodes_count;
int old_nodes_allocated;
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
index 025cd6b..905c44b 100644..100755
--- a/src/libknot/updates/ddns.c
+++ b/src/libknot/updates/ddns.c
@@ -97,7 +97,7 @@ static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset,
}
knot_rrset_t *new_rrset = NULL;
- ret = knot_rrset_deep_copy(rrset, &new_rrset);
+ ret = knot_rrset_deep_copy(rrset, &new_rrset, 0);
if (ret != KNOT_EOK) {
return ret;
}
@@ -202,7 +202,7 @@ static int knot_ddns_add_update(knot_changeset_t *changeset,
* copy.
*/
knot_rrset_t *rrset_copy;
- ret = knot_rrset_deep_copy(rrset, &rrset_copy);
+ ret = knot_rrset_deep_copy(rrset, &rrset_copy, 0);
if (ret != KNOT_EOK) {
return ret;
}
diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h
index dceebed..35dfcb7 100644..100755
--- a/src/libknot/updates/ddns.h
+++ b/src/libknot/updates/ddns.h
@@ -5,7 +5,7 @@
*
* \brief Dynamic updates processing.
*
- * \addtogroup query_processing
+ * \addtogroup ddns
* @{
*/
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index ea8178d..c870ece 100644..100755
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -102,7 +102,7 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
if (wire_size > *size) {
dbg_xfrin("Not enough space provided for the wire "
- "format of the query.\n");
+ "format of the query.\n");
knot_packet_free(&pkt);
return KNOT_ESPACE;
}
@@ -112,7 +112,7 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
char *name = knot_dname_to_str(xfr->tsig_key->name);
dbg_xfrin_detail("Signing XFR query with key (name %s): \n",
name);
- free(name);
+ free(name);
dbg_xfrin_hex_detail(xfr->tsig_key->secret,
xfr->tsig_key->secret_size);
@@ -152,9 +152,9 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
size_t *size)
{
- /*! \todo [TSIG] Should TSIG apply for SOA query too? */
return xfrin_create_query(owner, KNOT_RRTYPE_SOA,
- KNOT_CLASS_IN, xfr, size, 0, 0);
+ KNOT_CLASS_IN, xfr, size, 0,
+ xfr->tsig_key != NULL);
}
/*----------------------------------------------------------------------------*/
@@ -164,8 +164,8 @@ int xfrin_transfer_needed(const knot_zone_contents_t *zone,
{
// first, parse the rest of the packet
assert(!knot_packet_is_query(soa_response));
- dbg_xfrin("Response - parsed: %zu, total wire size: %zu\n",
- soa_response->parsed, soa_response->size);
+ dbg_xfrin_verb("Response - parsed: %zu, total wire size: %zu\n",
+ soa_response->parsed, soa_response->size);
int ret;
if (soa_response->parsed < soa_response->size) {
@@ -199,7 +199,7 @@ dbg_xfrin_exec(
dbg_xfrin("Malformed data in SOA of zone %s\n", name);
free(name);
);
- return KNOT_EMALF; // maybe some other error
+ return KNOT_EMALF; // maybe some other error
}
/*
@@ -273,8 +273,8 @@ static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t **rrsigs,
&& knot_rdata_rrsig_type_covered(knot_rrset_rdata(
last->rrsig))
== knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) {
- ret = knot_rrset_merge((void **)&last->rrsig,
- (void **)&rr);
+ ret = knot_rrset_merge_no_dupl((void **)&last->rrsig,
+ (void **)&rr);
if (ret != KNOT_EOK) {
return ret;
} else {
@@ -341,7 +341,7 @@ void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
}
/*----------------------------------------------------------------------------*/
-/*! \note [TSIG] */
+
static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
int tsig_req)
{
@@ -400,8 +400,8 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
}
if (ret != KNOT_EOK) {
- /*! \note [TSIG] No need to check TSIG error
- * here, propagate and check elsewhere.*/
+ /* No need to check TSIG error
+ * here, propagate and check elsewhere.*/
knot_rrset_deep_free(&tsig, 1, 1, 1);
return ret;
}
@@ -425,9 +425,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
tsig_rdata_time_signed(tsig);
- }/* else { // TSIG not required and not there
-
- }*/
+ }
} else if (tsig != NULL) {
// TSIG where it should not be
knot_rrset_deep_free(&tsig, 1, 1, 1);
@@ -441,9 +439,7 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
/*----------------------------------------------------------------------------*/
-int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
- xfrin_constructed_zone_t **constr*/
- knot_ns_xfr_t *xfr)
+int xfrin_process_axfr_packet(knot_ns_xfr_t *xfr)
{
const uint8_t *pkt = xfr->wire;
size_t size = xfr->wire_size;
@@ -451,11 +447,10 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
(xfrin_constructed_zone_t **)(&xfr->data);
if (pkt == NULL || constr == NULL) {
- dbg_xfrin("Wrong parameters supported.\n");
return KNOT_EBADARG;
}
- dbg_xfrin("Processing AXFR packet of size %zu.\n", size);
+ dbg_xfrin_verb("Processing AXFR packet of size %zu.\n", size);
// check if the response is OK
if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) {
@@ -473,8 +468,7 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
int ret = knot_packet_parse_from_wire(packet, pkt, size, 1);
if (ret != KNOT_EOK) {
- dbg_xfrin("Could not parse packet: %s.\n",
- knot_strerror(ret));
+ dbg_xfrin("Could not parse packet: %s.\n", knot_strerror(ret));
knot_packet_free(&packet);
/*! \todo Cleanup. */
return KNOT_EMALF;
@@ -510,18 +504,16 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
if (*constr == NULL) {
// this should be the first packet
- /*! \note [TSIG] Packet number for checking TSIG validation. */
+ /*! Packet number for checking TSIG validation. */
xfr->packet_nr = 0;
-// /*! \note [TSIG] Storing total size of data for TSIG digest. */
-// xfr->tsig_data_size = 0;
// create new zone
/*! \todo Ensure that the packet is the first one. */
if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
dbg_xfrin("No zone created, but the first RR in "
- "Answer is not a SOA RR.\n");
+ "Answer is not a SOA RR.\n");
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
knot_rrset_deep_free(&rr, 1, 1, 1);
/*! \todo Cleanup. */
return KNOT_EMALF;
@@ -543,7 +535,7 @@ dbg_xfrin_exec(
);
/*! \todo Cleanup. */
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_EMALF;
}
@@ -564,19 +556,20 @@ dbg_xfrin_exec(
if (*constr == NULL) {
dbg_xfrin("Failed to create new constr. zone.\n");
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_ENOMEM;
}
memset(*constr, 0, sizeof(xfrin_constructed_zone_t));
- (*constr)->contents = knot_zone_contents_new(node, 0, 1, NULL);
-// assert(0);
+ dbg_xfrin_verb("Creating new zone contents.\n");
+ (*constr)->contents = knot_zone_contents_new(node, 0, 1,
+ xfr->zone);
if ((*constr)->contents== NULL) {
dbg_xfrin("Failed to create new zone.\n");
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
knot_rrset_deep_free(&rr, 1, 1, 1);
/*! \todo Cleanup. */
return KNOT_ENOMEM;
@@ -588,21 +581,19 @@ dbg_xfrin_exec(
assert(zone->apex == node);
assert(zone->apex->owner == rr->owner);
// add the RRSet to the node
- //ret = knot_node_add_rrset(node, rr, 0);
ret = knot_zone_contents_add_rrset(zone, rr, &node,
KNOT_RRSET_DUPL_MERGE, 1);
if (ret < 0) {
dbg_xfrin("Failed to add RRSet to zone node: %s.\n",
knot_strerror(ret));
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
knot_rrset_deep_free(&rr, 1, 1, 1);
/*! \todo Cleanup. */
return KNOT_ERROR;
} else if (ret > 0) {
dbg_xfrin("Merged SOA RRSet.\n");
// merged, free the RRSet
- //knot_rrset_deep_free(&rr, 1, 0, 0);
knot_rrset_free(&rr);
}
@@ -613,20 +604,6 @@ dbg_xfrin_exec(
++xfr->packet_nr;
}
- /*! \note [TSIG] add the packet wire size to the data to be verified by
- * TSIG
- */
-// if (xfr->tsig_key) {
-// dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu,"
-// " adding: %zu).\n", xfr->tsig_data_size,
-// xfr->wire_size);
-// assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
-// >= xfr->wire_size);
-// memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire,
-// xfr->wire_size);
-// xfr->tsig_data_size += xfr->wire_size;
-// }
-
assert(zone != NULL);
while (ret == KNOT_EOK && rr != NULL) {
@@ -637,9 +614,9 @@ dbg_xfrin_exec(
if (node != NULL
&& knot_dname_compare(rr->owner, node->owner) != 0) {
-dbg_xfrin_exec(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(node->owner);
- dbg_xfrin("Node owner: %s\n", name);
+ dbg_xfrin_detail("Node owner: %s\n", name);
free(name);
);
if (!in_zone) {
@@ -659,16 +636,15 @@ dbg_xfrin_exec(
assert(knot_zone_contents_apex((zone)) != NULL);
assert(knot_node_rrset(knot_zone_contents_apex((zone)),
KNOT_RRTYPE_SOA) != NULL);
- dbg_xfrin("Found last SOA, transfer finished.\n");
+ dbg_xfrin_verb("Found last SOA, transfer finished.\n");
- dbg_xfrin("Verifying TSIG...\n");
+ dbg_xfrin_verb("Verifying TSIG...\n");
/*! \note [TSIG] Now check if there is not a TSIG record
* at the end of the packet.
*/
ret = xfrin_check_tsig(packet, xfr, 1);
- dbg_xfrin_detail("xfrin_check_tsig() returned %d\n",
- ret);
+ dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret);
knot_packet_free(&packet);
knot_rrset_deep_free(&rr, 1, 1, 1);
@@ -682,8 +658,7 @@ dbg_xfrin_exec(
ret = xfrin_process_orphan_rrsigs(zone,
(*constr)->rrsigs);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to process orphan "
- "RRSIGs\n");
+ dbg_xfrin("Failed to process orphan RRSIGs\n");
/*! \todo Cleanup?? */
return ret;
}
@@ -700,8 +675,8 @@ dbg_xfrin_exec(
ret = knot_zone_contents_add_rrsigs(zone, rr,
&tmp_rrset, &node, KNOT_RRSET_DUPL_MERGE, 1);
if (ret == KNOT_ENONODE || ret == KNOT_ENORRSET) {
- dbg_xfrin("No node or RRSet for RRSIGs\n");
- dbg_xfrin("Saving for later insertion.\n");
+ dbg_xfrin_verb("No node or RRSet for RRSIGs\n");
+ dbg_xfrin_verb("Saving for later insertion.\n");
if (ret == KNOT_ENORRSET) {
in_zone = 1;
@@ -711,13 +686,13 @@ dbg_xfrin_exec(
rr);
if (ret > 0) {
- dbg_xfrin("Merged RRSIGs.\n");
+ dbg_xfrin_detail("Merged RRSIGs.\n");
knot_rrset_free(&rr);
} else if (ret != KNOT_EOK) {
dbg_xfrin("Failed to save orphan"
- " RRSIGs.\n");
+ " RRSIGs.\n");
knot_packet_free(&packet);
- knot_node_free(&node, 0); // ???
+ knot_node_free(&node); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return ret;
}
@@ -725,15 +700,15 @@ dbg_xfrin_exec(
dbg_xfrin("Failed to add RRSIGs (%s).\n",
knot_strerror(ret));
knot_packet_free(&packet);
- knot_node_free(&node, 0); // ???
+ knot_node_free(&node); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_ERROR; /*! \todo Other error code. */
} else if (ret == 1) {
assert(node != NULL);
dbg_xfrin_exec_verb(
char *name = knot_dname_to_str(node->owner);
- dbg_xfrin("Found node for the record in "
- "zone: %s. Merged.\n", name);
+ dbg_xfrin_detail("Found node for the record in "
+ "zone: %s. Merged.\n", name);
free(name);
);
in_zone = 1;
@@ -741,13 +716,12 @@ dbg_xfrin_exec_verb(
} else if (ret == 2) {
// should not happen
assert(0);
-// knot_rrset_deep_free(&rr, 1, 1, 1);
} else {
assert(node != NULL);
dbg_xfrin_exec_verb(
char *name = knot_dname_to_str(node->owner);
- dbg_xfrin("Found node for the record in "
- "zone: %s.\n", name);
+ dbg_xfrin_detail("Found node for the record in "
+ "zone: %s.\n", name);
free(name);
);
in_zone = 1;
@@ -760,12 +734,12 @@ dbg_xfrin_exec_verb(
continue;
}
- /*! \note [TSIG] TSIG where it should not be - in Answer section.*/
+ /* TSIG where it should not be - in Answer section.*/
if (knot_rrset_type(rr) == KNOT_RRTYPE_TSIG) {
// not allowed here
- dbg_xfrin(" in Answer section.\n");
+ dbg_xfrin("TSIG in Answer section.\n");
knot_packet_free(&packet);
- knot_node_free(&node, 0); // ???
+ knot_node_free(&node); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_EMALF;
}
@@ -786,7 +760,7 @@ dbg_xfrin_exec_verb(
if (node == NULL && (node = get_node(zone,
knot_rrset_owner(rr))) != NULL) {
// the node for this RR was found in the zone
- dbg_xfrin("Found node for the record in zone.\n");
+ dbg_xfrin_detail("Found node for the record in zone\n");
in_zone = 1;
}
@@ -800,31 +774,30 @@ dbg_xfrin_exec_verb(
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_ENOMEM;
}
- dbg_xfrin("Created new node for the record.\n");
+ dbg_xfrin_detail("Created new node for the record.\n");
// insert the RRSet to the node
- ret = knot_node_add_rrset(node, rr, 1);
+ ret = knot_node_add_rrset_no_dupl(node, rr);
if (ret < 0) {
- dbg_xfrin("Failed to add RRSet to node (%s"
- ")\n", knot_strerror(ret));
+ dbg_xfrin("Failed to add RRSet to node (%s)\n",
+ knot_strerror(ret));
knot_packet_free(&packet);
- knot_node_free(&node, 0); // ???
+ knot_node_free(&node); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_ERROR;
} else if (ret > 0) {
// should not happen, this is new node
assert(0);
-// knot_rrset_deep_free(&rr, 1, 0, 0);
}
// insert the node into the zone
ret = add_node(zone, node, 1, 0, 1);
assert(node != NULL);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add node to zone (%s)"
- ".\n", knot_strerror(ret));
+ dbg_xfrin("Failed to add node to zone (%s).\n",
+ knot_strerror(ret));
knot_packet_free(&packet);
- knot_node_free(&node, 0); // ???
+ knot_node_free(&node); // ???
knot_rrset_deep_free(&rr, 1, 1, 1);
return KNOT_ERROR;
}
@@ -837,12 +810,11 @@ dbg_xfrin_exec_verb(
KNOT_RRSET_DUPL_MERGE, 1);
if (ret < 0) {
knot_packet_free(&packet);
- dbg_xfrin("Failed to add RRSet to zone:"
- "%s.\n", knot_strerror(ret));
+ dbg_xfrin("Failed to add RRSet to zone :%s.\n",
+ knot_strerror(ret));
return KNOT_ERROR;
} else if (ret > 0) {
// merged, free the RRSet
-// knot_rrset_deep_free(&rr, 1, 0, 0);
knot_rrset_free(&rr);
}
@@ -858,12 +830,11 @@ dbg_xfrin_exec_verb(
if (ret < 0) {
// some error in parsing
- dbg_xfrin("Could not parse next RR: %s.\n",
- knot_strerror(ret));
+ dbg_xfrin("Could not parse next RR: %s.\n", knot_strerror(ret));
knot_packet_free(&packet);
if (!in_zone) {
- knot_node_free(&node, 0);
+ knot_node_free(&node);
}
knot_rrset_deep_free(&rr, 1, 1, 1);
@@ -878,25 +849,23 @@ dbg_xfrin_exec_verb(
assert(node != NULL);
ret = knot_zone_contents_add_node(zone, node, 1, 0, 1);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add last node into zone (%s)"
- ".\n", knot_strerror(ret));
+ dbg_xfrin("Failed to add last node into zone (%s).\n",
+ knot_strerror(ret));
knot_packet_free(&packet);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
return KNOT_ERROR; /*! \todo Other error */
}
}
- /*! \note [TSIG] Now check if there is not a TSIG record at the end of
- * the packet.
- */
+ /* Now check if there is not a TSIG record at the end of the packet. */
ret = xfrin_check_tsig(packet, xfr,
knot_ns_tsig_required(xfr->packet_nr));
++xfr->packet_nr;
knot_packet_free(&packet);
- dbg_xfrin("Processed one AXFR packet successfully.\n");
+ dbg_xfrin_verb("Processed one AXFR packet successfully.\n");
- /*! \note [TSIG] TSIG errors are propagated and reported in a standard
+ /* TSIG errors are propagated and reported in a standard
* manner, as we're in response processing, no further error response
* should be sent.
*/
@@ -920,8 +889,7 @@ static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt,
int ret = knot_packet_parse_from_wire(*packet, pkt, size, 1);
if (ret != KNOT_EOK) {
- dbg_xfrin("Could not parse packet: %s.\n",
- knot_strerror(ret));
+ dbg_xfrin("Could not parse packet: %s.\n", knot_strerror(ret));
knot_packet_free(packet);
return KNOT_EMALF;
}
@@ -986,7 +954,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
}
if (*chs == NULL) {
- dbg_xfrin("Changesets empty, creating new.\n");
+ dbg_xfrin_verb("Changesets empty, creating new.\n");
ret = knot_changeset_allocate(chs);
if (ret != KNOT_EOK) {
@@ -1007,7 +975,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
(*chs)->first_soa = rr;
state = -1;
- dbg_xfrin("First SOA of IXFR saved, state set to -1.\n");
+ dbg_xfrin_verb("First SOA of IXFR saved, state set to -1.\n");
// parse the next one
ret = knot_packet_parse_next_rr_answer(packet, &rr);
@@ -1050,7 +1018,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
ret = KNOT_EBADARG;
goto cleanup;
}
- dbg_xfrin("Changesets present.\n");
+ dbg_xfrin_detail("Changesets present.\n");
}
/*
@@ -1090,7 +1058,7 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
* calls to this function.
*/
if (state != -1) {
- dbg_xfrin("State is not -1, deciding...\n");
+ dbg_xfrin_detail("State is not -1, deciding...\n");
// there should be at least one started changeset right now
if ((*chs)->count <= 0) {
knot_rrset_deep_free(&rr, 1, 1, 1);
@@ -1108,15 +1076,15 @@ int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
}
}
- dbg_xfrin("State before the loop: %d\n", state);
+ dbg_xfrin_detail("State before the loop: %d\n", state);
/*! \todo This may be implemented with much less IFs! */
while (ret == KNOT_EOK && rr != NULL) {
dbg_xfrin_exec_verb(
- dbg_xfrin_verb("Next loop, state: %d\n", state);
+ dbg_xfrin_detail("Next loop, state: %d\n", state);
char *name = knot_dname_to_str(knot_rrset_owner(rr));
- dbg_xfrin_verb("Actual RR: %s, type %s.\n", name,
+ dbg_xfrin_detail("Actual RR: %s, type %s.\n", name,
knot_rrtype_to_string(knot_rrset_type(rr)));
free(name);
);
@@ -1128,7 +1096,7 @@ dbg_xfrin_exec_verb(
// is quite weird in fact
if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
dbg_xfrin("First RR is not a SOA RR!\n");
- dbg_xfrin("RR type: %s\n",
+ dbg_xfrin_verb("RR type: %s\n",
knot_rrtype_to_string(knot_rrset_type(rr)));
ret = KNOT_EMALF;
knot_rrset_deep_free(&rr, 1, 1, 1);
@@ -1222,15 +1190,12 @@ dbg_xfrin_exec_verb(
}
}
break;
- // dead code
-// default:
-// assert(0);
}
// parse the next RR
- dbg_xfrin_verb("Parsing next RR..\n");
+ dbg_xfrin_detail("Parsing next RR..\n");
ret = knot_packet_parse_next_rr_answer(packet, &rr);
- dbg_xfrin_verb("Returned %d, %p.\n", ret, rr);
+ dbg_xfrin_detail("Returned %d, %p.\n", ret, rr);
}
/*! \note Check TSIG, we're at the end of packet. It may not be
@@ -1238,7 +1203,7 @@ dbg_xfrin_exec_verb(
*/
ret = xfrin_check_tsig(packet, xfr,
knot_ns_tsig_required(xfr->packet_nr));
- dbg_xfrin_detail("xfrin_check_tsig() returned %d\n", ret);
+ dbg_xfrin_verb("xfrin_check_tsig() returned %d\n", ret);
++xfr->packet_nr;
/*! \note [TSIG] Cleanup and propagate error if TSIG validation fails.*/
@@ -1255,7 +1220,7 @@ cleanup:
/* We should go here only if some error occured. */
assert(ret < 0);
- dbg_xfrin("Cleanup after processing IXFR/IN packet.\n");
+ dbg_xfrin_detail("Cleanup after processing IXFR/IN packet.\n");
knot_free_changesets(chs);
knot_packet_free(&packet);
xfr->data = 0;
@@ -1337,6 +1302,9 @@ static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types,
}
int new_count = (*allocated == 0) ? 2 : *allocated * 2;
+ while (new_count < count + to_add) {
+ new_count *= 2;
+ }
/* Allocate new memory block. */
knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *));
@@ -1372,36 +1340,21 @@ static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types,
static void xfrin_changes_add_rdata(knot_rdata_t **rdatas, uint *types,
int *count, knot_rdata_t *rdata, uint type)
{
- dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", rdata);
-
if (rdata == NULL) {
return;
}
-//dbg_xfrin_exec_detail(
-// // try to find the first RDATA in the given list
-// for (int i = 0; i < *count; ++i) {
-// knot_rdata_t *r = rdatas[i];
-// if (r == NULL) {
-// continue;
-// }
-// while (r != NULL && r->next != rdatas[i]) {
-// if (r == rdata) {
-// dbg_xfrin_detail("Found same RDATA: %p\n", rdata);
-// knot_rdata_dump(rdata, type, 0);
-// }
-// r = r->next;
-// }
-// if (r == rdata) {
-// dbg_xfrin_detail("Found same RDATA: %p\n", rdata);
-// knot_rdata_dump(rdata, type, 0);
-// }
-// }
-//);
+ // Add all RDATAs from the chain!!
- rdatas[*count] = rdata;
- types[*count] = type;
- ++*count;
+ knot_rdata_t *r = rdata;
+ do {
+ dbg_xfrin_detail("Adding RDATA to RDATA list: %p\n", r);
+ rdatas[*count] = r;
+ types[*count] = type;
+ ++*count;
+
+ r = r->next;
+ } while (r != NULL && r != rdata);
}
/*----------------------------------------------------------------------------*/
@@ -1412,7 +1365,6 @@ static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
if ((*contents)->table != NULL) {
ck_destroy_table(&(*contents)->table, NULL, 0);
-// ck_table_free(&(*contents)->table);
}
// free the zone tree with nodes
@@ -1459,10 +1411,9 @@ static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
knot_changes_t *changes)
{
- dbg_xfrin_verb("Copying old RRSet: %p\n", old);
+ dbg_xfrin_detail("Copying old RRSet: %p\n", old);
// create new RRSet by copying the old one
-// int ret = knot_rrset_shallow_copy(old, copy);
- int ret = knot_rrset_deep_copy(old, copy);
+ int ret = knot_rrset_deep_copy(old, copy, 0);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to create RRSet copy.\n");
return KNOT_ENOMEM;
@@ -1479,11 +1430,14 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
return ret;
}
+ int count = knot_rrset_rdata_rr_count(*copy);
+ count += knot_rrset_rdata_rr_count((*copy)->rrsigs);
+
// add the copied RDATA to the list of new RDATA
ret = xfrin_changes_check_rdata(&changes->new_rdata,
&changes->new_rdata_types,
changes->new_rdata_count,
- &changes->new_rdata_allocated, 2);
+ &changes->new_rdata_allocated, count);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to add new RRSet to list.\n");
knot_rrset_deep_free(copy, 1, 1, 1);
@@ -1492,7 +1446,8 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
changes->new_rrsets[changes->new_rrsets_count++] = *copy;
- dbg_xfrin_verb("Adding RDATA from the RRSet copy to new RDATA list.\n");
+ dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list."
+ "\n");
xfrin_changes_add_rdata(changes->new_rdata, changes->new_rdata_types,
&changes->new_rdata_count,
knot_rrset_get_rdata(*copy),
@@ -1502,8 +1457,9 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
assert(old->rrsigs != NULL);
changes->new_rrsets[changes->new_rrsets_count++] =
(*copy)->rrsigs;
- dbg_xfrin_verb("Adding RDATA from RRSIG of the RRSet copy to "
- "new RDATA list.\n");
+
+ dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to "
+ "new RDATA list.\n");
xfrin_changes_add_rdata(changes->new_rdata,
changes->new_rdata_types,
&changes->new_rdata_count,
@@ -1520,11 +1476,14 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
return ret;
}
+ count = knot_rrset_rdata_rr_count(old);
+ count += knot_rrset_rdata_rr_count(old->rrsigs);
+
// and old RDATA to the list of old RDATA
ret = xfrin_changes_check_rdata(&changes->old_rdata,
&changes->old_rdata_types,
changes->old_rdata_count,
- &changes->old_rdata_allocated, 2);
+ &changes->old_rdata_allocated, count);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to add old RRSet to list.\n");
return ret;
@@ -1532,7 +1491,7 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
changes->old_rrsets[changes->old_rrsets_count++] = old;
- dbg_xfrin_verb("Adding RDATA from old RRSet to old RDATA list.\n");
+ dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n");
xfrin_changes_add_rdata(changes->old_rdata, changes->old_rdata_types,
&changes->old_rdata_count, old->rdata,
knot_rrset_type(old));
@@ -1541,8 +1500,8 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
assert(old->rrsigs != NULL);
changes->old_rrsets[changes->old_rrsets_count++] = old->rrsigs;
- dbg_xfrin_verb("Adding RDATA from RRSIG of the old RRSet to "
- "old RDATA list.\n");
+ dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to "
+ "old RDATA list.\n");
xfrin_changes_add_rdata(changes->old_rdata,
changes->old_rdata_types,
&changes->old_rdata_count,
@@ -1558,15 +1517,15 @@ static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
knot_rrset_t **rrset, knot_changes_t *changes)
{
-dbg_xfrin_exec_verb(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_node_owner(node));
- dbg_xfrin_verb("Removing RRSet of type %s from node %s (%p)\n",
+ dbg_xfrin_detail("Removing RRSet of type %s from node %s (%p)\n",
knot_rrtype_to_string(type), name, node);
free(name);
);
knot_rrset_t *old = knot_node_remove_rrset(node, type);
- dbg_xfrin_verb("Removed RRSet: %p\n", old);
+ dbg_xfrin_detail("Removed RRSet: %p\n", old);
dbg_xfrin_detail("Other RRSet of the same type in the node: %p\n",
knot_node_rrset(node, type));
@@ -1580,7 +1539,7 @@ dbg_xfrin_exec_verb(
return ret;
}
- dbg_xfrin_verb("Copied old rrset %p to new %p.\n", old, *rrset);
+ dbg_xfrin_detail("Copied old rrset %p to new %p.\n", old, *rrset);
// replace the RRSet in the node copy by the new one
ret = knot_node_add_rrset(node, *rrset, 0);
@@ -1597,7 +1556,8 @@ dbg_xfrin_exec_verb(
static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
const knot_rrset_t *remove,
knot_node_t *node,
- knot_rrset_t **rrset)
+ knot_rrset_t **rrset,
+ knot_rrset_t **rrsigs_old)
{
assert(changes != NULL);
assert(remove != NULL);
@@ -1613,37 +1573,35 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
int copied = 0;
- if (!*rrset
- || knot_dname_compare(knot_rrset_owner(*rrset),
- knot_node_owner(node)) != 0
- || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
- knot_rrset_rdata(remove))) {
+ if (*rrset
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) == 0
+ && knot_rrset_type(*rrset) == knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove))) {
+ // this RRSet should be the already copied RRSet so we may
+ // update it right away
+ /*! \todo Does this case even occur? */
+ dbg_xfrin_verb("Using RRSet from previous iteration.\n");
+ } else {
// find RRSet based on the Type Covered
knot_rr_type_t type = knot_rdata_rrsig_type_covered(
knot_rrset_rdata(remove));
// copy the rrset
- dbg_xfrin_verb("Copying RRSet that carries the RRSIGs.\n");
+ dbg_xfrin_detail("Copying RRSet that carries the RRSIGs.\n");
ret = xfrin_copy_rrset(node, type, rrset, changes);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to copy rrset from changeset.\n");
return ret;
}
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
copied = 1;
- } else {
- // we should have the right RRSIG RRSet in *rrset
- assert(knot_rrset_type(*rrset)
- == knot_rdata_rrsig_type_covered(
- knot_rrset_rdata(remove)));
- // this RRSet should be the already copied RRSet so we may
- // update it right away
- /*! \todo Does this case even occur? */
- dbg_xfrin_verb("Using RRSet from previous iteration.\n");
}
-
+
// get the old rrsigs
knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset);
- dbg_xfrin_verb("Old RRSIGs from RRSet: %p\n", old);
+ dbg_xfrin_detail("Old RRSIGs from RRSet: %p\n", old);
if (old == NULL) {
return 1;
}
@@ -1651,14 +1609,22 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
// copy the RRSIGs
knot_rrset_t *rrsigs = NULL;
if (!copied) {
- ret = xfrin_copy_old_rrset(old, &rrsigs, changes);
- if (ret != KNOT_EOK) {
- return ret;
+ // check if the stored RRSIGs are not the right ones
+ if (*rrsigs_old && *rrsigs_old == (*rrset)->rrsigs) {
+ dbg_xfrin_verb("Using RRSIG from previous iteration\n");
+ rrsigs = *rrsigs_old;
+ } else {
+ ret = xfrin_copy_old_rrset(old, &rrsigs, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ dbg_xfrin_detail("Copied RRSIGs: %p\n", rrsigs);
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(rrsigs, 0);
}
- dbg_xfrin_verb("Copied RRSIGs: %p\n", rrsigs);
} else {
rrsigs = old;
- dbg_xfrin_verb("Using old RRSIGs: %p\n", rrsigs);
+ dbg_xfrin_detail("Using old RRSIGs: %p\n", rrsigs);
}
// set the RRSIGs to the new RRSet copy
@@ -1667,21 +1633,25 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
return KNOT_ERROR;
}
+ *rrsigs_old = rrsigs;
+
// now in '*rrset' we have a copy of the RRSet which holds the RRSIGs
// and in 'rrsigs' we have the copy of the RRSIGs
knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove);
if (rdata == NULL) {
dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
- knot_strerror(ret));
+ knot_strerror(ret));
return 1;
}
+
+ int count = knot_rdata_count(rdata);
// connect the RDATA to the list of old RDATA
ret = xfrin_changes_check_rdata(&changes->old_rdata,
&changes->old_rdata_types,
changes->old_rdata_count,
- &changes->old_rdata_allocated, 1);
+ &changes->old_rdata_allocated, count);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1704,7 +1674,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
1);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to add empty RRSet to the "
- "list of old RRSets.");
+ "list of old RRSets.");
// delete the RRSet right away
knot_rrset_free(&rrsigs);
return ret;
@@ -1729,7 +1699,7 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
1);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to add empty RRSet to "
- "the list of old RRSets.");
+ "the list of old RRSets.");
// delete the RRSet right away
knot_rrset_free(rrset);
return ret;
@@ -1762,10 +1732,13 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes,
// now we have the copy of the node, so lets get the right RRSet
// check if we do not already have it
- if (!*rrset
- || knot_dname_compare(knot_rrset_owner(*rrset),
- knot_node_owner(node)) != 0
- || knot_rrset_type(*rrset) != knot_rrset_type(remove)) {
+ if (*rrset
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) == 0
+ && knot_rrset_type(*rrset) == knot_rrset_type(remove)) {
+ /*! \todo Does some other case even occur? */
+ dbg_xfrin_verb("Using RRSet from previous loop.\n");
+ } else {
/*!
* \todo This may happen also with already
* copied RRSet. In that case it would be
@@ -1774,30 +1747,29 @@ static int xfrin_apply_remove_normal(knot_changes_t *changes,
*/
ret = xfrin_copy_rrset(node,
knot_rrset_type(remove), rrset, changes);
- dbg_xfrin_detail("Copied RRSet:\n");
- knot_rrset_dump(*rrset, 0);
if (ret != KNOT_EOK) {
return ret;
}
- } /*! \todo Does some other case even occur? */
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
+ }
if (*rrset == NULL) {
dbg_xfrin_verb("RRSet not found for RR to be removed.\n");
return 1;
}
-dbg_xfrin_exec_verb(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
- dbg_xfrin("Updating RRSet with owner %s, type %s\n", name,
- knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ dbg_xfrin_detail("Updating RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
free(name);
);
- // remove the specified RRs from the RRSet (de facto difference of
- // sets)
+ // remove the specified RRs from the RRSet (de facto difference of sets)
knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove);
if (rdata == NULL) {
- dbg_xfrin("Failed to remove RDATA from RRSet\n");
+ dbg_xfrin_verb("Failed to remove RDATA from RRSet\n");
// In this case, the RDATA was not found in the RRSet
return 1;
}
@@ -1814,11 +1786,12 @@ dbg_xfrin_exec_detail(
}
);
+ int count = knot_rdata_count(rdata);
// connect the RDATA to the list of old RDATA
ret = xfrin_changes_check_rdata(&changes->old_rdata,
&changes->old_rdata_types,
changes->old_rdata_count,
- &changes->old_rdata_allocated, 1);
+ &changes->old_rdata_allocated, count);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1859,7 +1832,7 @@ dbg_xfrin_exec_detail(
}
/*----------------------------------------------------------------------------*/
-
+/*! \todo Needs review - RRs may not be merged into RRSets. */
static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes,
knot_node_t *node, uint16_t type)
{
@@ -1918,9 +1891,6 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
// insert the node into zone structures and create parents if
// necessary
-// dbg_xfrin("Adding new node to zone. From owner: %s type %s\n",
-// knot_dname_to_str(node->owner),
-// knot_rrtype_to_string(rrset->type));
if (is_nsec3) {
ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0,
1);
@@ -1959,35 +1929,53 @@ static int xfrin_apply_add_normal(knot_changes_t *changes,
int ret;
- dbg_xfrin_verb("applying rrset:\n");
+dbg_xfrin_exec_detail(
+ dbg_xfrin_detail("applying rrset:\n");
knot_rrset_dump(add, 0);
+);
+ int copied = 0;
/*! \note Reusing RRSet from previous function caused it not to be
* removed from the node.
* Maybe modification of the code would allow reusing the RRSet
* as in apply_add_rrsigs() - the RRSet should not be copied
* in such case.
*/
-// if (!*rrset
-// || knot_dname_compare(knot_rrset_owner(*rrset),
-// knot_node_owner(node)) != 0
-// || knot_rrset_type(*rrset)
-// != knot_rrset_type(add)) {
-// dbg_xfrin("Removing rrset!\n");
-// *rrset = knot_node_remove_rrset(node, knot_rrset_type(add));
-// }
-
- *rrset = knot_node_remove_rrset(node, knot_rrset_type(add));
-
- dbg_xfrin_verb("Removed RRSet: \n");
+ if (*rrset
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) == 0
+ && knot_rrset_type(*rrset) == knot_rrset_type(add)) {
+ dbg_xfrin_verb("Using RRSet from previous iteration.\n");
+ } else {
+ dbg_xfrin_detail("Removing rrset!\n");
+ *rrset = knot_node_remove_rrset(node, knot_rrset_type(add));
+
+ knot_rrset_t *old = *rrset;
+
+ if (*rrset != NULL) {
+ ret = xfrin_copy_old_rrset(old, rrset, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_xfrin_detail("Copied RRSet: %p\n", *rrset);
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
+ copied = 1;
+ }
+ }
+
+dbg_xfrin_exec_detail(
+ dbg_xfrin_detail("Removed RRSet: \n");
knot_rrset_dump(*rrset, 1);
+);
if (*rrset == NULL) {
-dbg_xfrin_exec_verb(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(add->owner);
- dbg_xfrin_verb("RRSet to be added not found in zone.\n");
- dbg_xfrin_verb("owner: %s type: %s\n", name,
- knot_rrtype_to_string(add->type));
+ dbg_xfrin_detail("RRSet to be added not found in zone.\n");
+ dbg_xfrin_detail("owner: %s type: %s\n", name,
+ knot_rrtype_to_string(add->type));
free(name);
);
// add the RRSet from the changeset to the node
@@ -2013,25 +2001,12 @@ dbg_xfrin_exec_verb(
return 1; // return 1 to indicate the add RRSet was used
}
- knot_rrset_t *old = *rrset;
-
-dbg_xfrin_exec_verb(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
- dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
- knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ dbg_xfrin_detail("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
free(name);
);
-// knot_rrset_dump(*rrset, 1);
- ret = xfrin_copy_old_rrset(old, rrset, changes);
- if (ret != KNOT_EOK) {
- return ret;
- }
-
- dbg_xfrin_verb("Copied RRSet: %p\n", *rrset);
-
-// dbg_xfrin("After copy: Found RRSet with owner %s, type %s\n",
-// knot_dname_to_str((*rrset)->owner),
-// knot_rrtype_to_string(knot_rrset_type(*rrset)));
// merge the changeset RRSet to the copy
/* What if the update fails?
@@ -2044,25 +2019,38 @@ dbg_xfrin_exec_verb(
*
* TODO: add the 'add' rrset to list of old RRSets?
*/
- dbg_xfrin_verb("Merging RRSets with owners: %s, %s types: %s, %s\n",
- (*rrset)->owner->name, add->owner->name,
- knot_rrtype_to_string((*rrset)->type),
- knot_rrtype_to_string(add->type));
+ dbg_xfrin_detail("Merging RRSets with owners: %s, %s types: %s, %s\n",
+ (*rrset)->owner->name, add->owner->name,
+ knot_rrtype_to_string((*rrset)->type),
+ knot_rrtype_to_string(add->type));
dbg_xfrin_detail("RDATA in RRSet1: %p, RDATA in RRSet2: %p\n",
(*rrset)->rdata, add->rdata);
- ret = knot_rrset_merge((void **)rrset, (void **)&add);
+ /* In case the RRSet is empty (and only remained there because of the
+ * RRSIGs) it may happen that the TTL may be different than that of
+ * the new RRs. Update the TTL according to the first RR.
+ */
+
+ if (knot_rrset_rdata(*rrset) == NULL
+ && knot_rrset_ttl(*rrset) != knot_rrset_ttl(add)) {
+ knot_rrset_set_ttl(*rrset, knot_rrset_ttl(add));
+ }
+
+ ret = knot_rrset_merge_no_dupl((void **)rrset, (void **)&add);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to merge changeset RRSet to copy.\n");
- return KNOT_ERROR;
+ dbg_xfrin("Failed to merge changeset RRSet.\n");
+ return ret;
}
- dbg_xfrin_verb("Merge returned: %d\n", ret);
+ dbg_xfrin_detail("Merge returned: %d\n", ret);
knot_rrset_dump(*rrset, 1);
- ret = knot_node_add_rrset(node, *rrset, 0);
- if (ret < 0) {
- dbg_xfrin("Failed to add merged RRSet to the node.\n");
- return ret;
+ if (copied) {
+ ret = knot_node_add_rrset(node, *rrset, 0);
+
+ if (ret < 0) {
+ dbg_xfrin("Failed to add merged RRSet to the node.\n");
+ return ret;
+ }
}
// return 2 so that the add RRSet is removed from
@@ -2078,6 +2066,7 @@ static int xfrin_apply_add_rrsig(knot_changes_t *changes,
knot_rrset_t *add,
knot_node_t *node,
knot_rrset_t **rrset,
+ knot_rrset_t **rrsigs_old,
knot_zone_contents_t *contents)
{
assert(changes != NULL);
@@ -2095,8 +2084,8 @@ static int xfrin_apply_add_rrsig(knot_changes_t *changes,
dbg_xfrin_exec_verb(
char *name = knot_dname_to_str(knot_rrset_owner(add));
const char *typestr = knot_rrtype_to_string(type);
- dbg_xfrin("Adding RRSIG: Owner %s, type covered %s.\n",
- name, typestr);
+ dbg_xfrin_verb("Adding RRSIG: Owner %s, type covered %s.\n",
+ name, typestr);
free(name);
);
@@ -2105,11 +2094,12 @@ dbg_xfrin_exec_verb(
/*! \note Here the check is OK, because if we aready have the RRSet,
* it's a copied one, so it is OK to modify it right away.
*/
- if (!*rrset
- || knot_dname_compare(knot_rrset_owner(*rrset),
- knot_node_owner(node)) != 0
- || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
- knot_rrset_rdata(add))) {
+ if (*rrset
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) == 0
+ && knot_rrset_type(*rrset) == type) {
+ dbg_xfrin_verb("Using RRSet from previous iteration.\n");
+ } else {
// copy the rrset
ret = xfrin_copy_rrset(node, type, rrset, changes);
if (ret < 0) {
@@ -2118,15 +2108,12 @@ dbg_xfrin_exec_verb(
*rrset = NULL;
}
copied = 1;
- } else {
- // we should have the right RRSIG RRSet in *rrset
- assert(knot_rrset_type(*rrset) == type);
- // this RRSet should be the already copied RRSet so we may
- // update it right away
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
}
if (*rrset == NULL) {
- dbg_xfrin_verb("RRSet to be added not found in zone.\n");
+ dbg_xfrin_detail("RRSet to be added not found in zone.\n");
// create a new RRSet to add the RRSIGs into
*rrset = knot_rrset_new(knot_node_get_owner(node), type,
@@ -2136,7 +2123,7 @@ dbg_xfrin_exec_verb(
dbg_xfrin("Failed to create new RRSet for RRSIGs.\n");
return KNOT_ENOMEM;
}
- dbg_xfrin_verb("Created new RRSet for RRSIG: %p.\n", *rrset);
+ dbg_xfrin_detail("Created new RRSet for RRSIG: %p.\n", *rrset);
// add the RRset to the list of new RRsets
ret = xfrin_changes_check_rrsets(
@@ -2144,8 +2131,7 @@ dbg_xfrin_exec_verb(
&changes->new_rrsets_count,
&changes->new_rrsets_allocated, 1);
if (ret != KNOT_EOK) {
- dbg_xfrin("Failed to add old RRSet to "
- "list.\n");
+ dbg_xfrin("Failed to add old RRSet to list.\n");
knot_rrset_free(rrset);
return ret;
}
@@ -2163,20 +2149,19 @@ dbg_xfrin_exec_verb(
changes->new_rrsets[changes->new_rrsets_count++] = *rrset;
}
-dbg_xfrin_exec_verb(
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
- dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
- knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ dbg_xfrin_detail("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
free(name);
);
if (knot_rrset_rrsigs(*rrset) == NULL) {
- dbg_xfrin_verb("Adding new RRSIGs to RRSet.\n");
+ dbg_xfrin_detail("Adding new RRSIGs to RRSet.\n");
ret = knot_zone_contents_add_rrsigs(contents, add, rrset, &node,
KNOT_RRSET_DUPL_SKIP, 1);
-// ret = knot_rrset_set_rrsigs(*rrset, add);
if (ret < 0) {
dbg_xfrin("Failed to add RRSIGs to the RRSet.\n");
return KNOT_ERROR;
@@ -2191,20 +2176,30 @@ dbg_xfrin_exec_verb(
knot_rrset_t *rrsig;
if (!copied) {
- ret = xfrin_copy_old_rrset(old, &rrsig, changes);
- if (ret != KNOT_EOK) {
- return ret;
+ // check if the stored RRSIGs are not the right ones
+ if (*rrsigs_old && *rrsigs_old == old) {
+ dbg_xfrin_verb("Using RRSIG from previous iteration\n");
+ rrsig = *rrsigs_old;
+ } else {
+ ret = xfrin_copy_old_rrset(old, &rrsig, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ dbg_xfrin_detail("Copied RRSIGs: %p\n", rrsig);
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(rrsig, 0);
}
} else {
rrsig = old;
+ dbg_xfrin_verb("Using old RRSIGs: %p\n", rrsig);
}
// replace the old RRSIGs with the new ones
knot_rrset_set_rrsigs(*rrset, rrsig);
// merge the changeset RRSet to the copy
- dbg_xfrin_verb("Merging RRSIG to the one in the RRSet.\n");
- ret = knot_rrset_merge((void **)&rrsig, (void **)&add);
+ dbg_xfrin_detail("Merging RRSIG to the one in the RRSet.\n");
+ ret = knot_rrset_merge_no_dupl((void **)&rrsig, (void **)&add);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to merge changeset RRSIG to copy: %s"
".\n", knot_strerror(ret));
@@ -2232,23 +2227,50 @@ void xfrin_cleanup_successful_update(knot_changes_t **changes)
// delete old RDATA
for (int i = 0; i < (*changes)->old_rdata_count; ++i) {
dbg_xfrin_detail("Deleting old RDATA: %p, type: %s\n",
- (*changes)->old_rdata[i],
- knot_rrtype_to_string((*changes)->old_rdata_types[i]));
+ (*changes)->old_rdata[i],
+ knot_rrtype_to_string((*changes)->old_rdata_types[i]));
knot_rdata_dump((*changes)->old_rdata[i],
(*changes)->old_rdata_types[i], 0);
- knot_rdata_t *rdata = (*changes)->old_rdata[i];
- if (rdata != NULL) {
- do {
- knot_rdata_t *tmp = rdata->next;
- knot_rdata_deep_free(&rdata,
- (*changes)->old_rdata_types[i], 1);
- rdata = tmp;
- } while (rdata != NULL
- && rdata != (*changes)->old_rdata[i]);
- }
+ // RDATA are stored separately so do not delete the whole chain
+ knot_rdata_deep_free(&(*changes)->old_rdata[i],
+ (*changes)->old_rdata_types[i], 1);
+// knot_rdata_t *rdata = (*changes)->old_rdata[i];
+// if (rdata != NULL) {
+// do {
+// knot_rdata_t *tmp = rdata->next;
+// knot_rdata_deep_free(&rdata,
+// (*changes)->old_rdata_types[i], 1);
+// rdata = tmp;
+// } while (rdata != NULL
+// && rdata != (*changes)->old_rdata[i]);
+// }
}
+ // free the empty nodes
+ for (int i = 0; i < (*changes)->old_nodes_count; ++i) {
+dbg_xfrin_exec_detail(
+ char *name = knot_dname_to_str(
+ knot_node_owner((*changes)->old_nodes[i]));
+ dbg_xfrin_detail("Deleting old empty node: %p, owner: %s\n",
+ (*changes)->old_nodes[i], name);
+ free(name);
+);
+ knot_node_free(&(*changes)->old_nodes[i]);
+ }
+
+ // free empty NSEC3 nodes
+ for (int i = 0; i < (*changes)->old_nsec3_count; ++i) {
+dbg_xfrin_exec_detail(
+ char *name = knot_dname_to_str(
+ knot_node_owner((*changes)->old_nsec3[i]));
+ dbg_xfrin_detail("Deleting old empty node: %p, owner: %s\n",
+ (*changes)->old_nsec3[i], name);
+ free(name);
+);
+ knot_node_free(&(*changes)->old_nsec3[i]);
+ }
+
// free allocated arrays of nodes and rrsets
free((*changes)->new_rrsets);
free((*changes)->new_rdata);
@@ -2304,13 +2326,21 @@ static void xfrin_switch_node_in_dname_table(knot_dname_t *dname, void *data)
{
UNUSED(data);
- if (knot_dname_node(dname) == NULL) {
+dbg_xfrin_exec_detail(
+ char *name = knot_dname_to_str(dname);
+ dbg_xfrin_detail("Switching node in dname %s (%p)\n", name, dname->node);
+ free(name);
+);
+ /* dname is not checked here (for NULL value), which resulted in crash
+ * on howl recently. However, dname should not be NULL here at all,
+ * it is a sign of some other error.
+ */
+
+ if (dname->node == NULL) {
return;
}
- assert(knot_node_new_node(knot_dname_node(dname)) != NULL);
- knot_dname_set_node(dname, knot_node_get_new_node(
- knot_dname_get_node(dname)));
+ knot_dname_update_node(dname);
}
/*----------------------------------------------------------------------------*/
@@ -2333,10 +2363,10 @@ static int xfrin_switch_nodes(knot_zone_contents_t *contents_copy)
xfrin_switch_node_in_hash_table, NULL);
assert(ret == 0);
- // Traverse also the dname table and change the node pointers in dnames
- knot_zone_contents_dname_table_apply(contents_copy,
- xfrin_switch_node_in_dname_table,
- NULL);
+// // Traverse also the dname table and change the node pointers in dnames
+// knot_zone_contents_dname_table_apply(contents_copy,
+// xfrin_switch_node_in_dname_table,
+// NULL);
return KNOT_EOK;
}
@@ -2349,7 +2379,6 @@ static void xfrin_zone_contents_free2(knot_zone_contents_t **contents)
if ((*contents)->table != NULL) {
ck_destroy_table(&(*contents)->table, NULL, 0);
-// ck_table_free(&(*contents)->table);
}
// free the zone tree, but only the structure
@@ -2423,8 +2452,8 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents,
}
for (int i = 0; i < (*changes)->new_rdata_count; ++i) {
- dbg_xfrin("Freeing %d. RDATA chain: %p\n", i,
- (*changes)->new_rdata[i]);
+ dbg_xfrin_detail("Freeing %d. RDATA: %p\n", i,
+ (*changes)->new_rdata[i]);
/*
* In some case, the same RDATA may be stored in
@@ -2438,28 +2467,14 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents,
* already deleted, but that may be very time-consuming.
*/
- // discard the whole chain of RDATA
- knot_rdata_t *rdata = (*changes)->new_rdata[i];
- knot_rdata_t *rdata_next = NULL;
-
- while (rdata != NULL && rdata->next !=
- (*changes)->new_rdata[i]) {
- assert(rdata->next != rdata);
- rdata_next = rdata->next;
- dbg_xfrin(" Deleting RDATA: %p\n", rdata);
- knot_rdata_deep_free(&rdata,
- (*changes)->new_rdata_types[i], 1);
- rdata = rdata_next;
- }
-
- assert(rdata == NULL
- || rdata->next == (*changes)->new_rdata[i]);
+ /*
+ * Every RDATA from a chain is stored separately.
+ * We thus do not follow the RDATA chains and free only
+ * the first RDATA in each.
+ */
- dbg_xfrin(" Deleting RDATA: %p\n", rdata);
- knot_rdata_deep_free(&rdata,
+ knot_rdata_deep_free(&(*changes)->new_rdata[i],
(*changes)->new_rdata_types[i], 1);
-
- //(*changes)->new_rdata[i] = NULL;
}
// free allocated arrays of nodes and rrsets
@@ -2481,9 +2496,9 @@ void xfrin_rollback_update(knot_zone_contents_t *old_contents,
/*----------------------------------------------------------------------------*/
-static int xfrin_apply_remove2(knot_zone_contents_t *contents,
- knot_changeset_t *chset,
- knot_changes_t *changes)
+static int xfrin_apply_remove(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ knot_changes_t *changes)
{
/*
* Iterate over removed RRSets, and remove them from the new nodes
@@ -2492,7 +2507,7 @@ static int xfrin_apply_remove2(knot_zone_contents_t *contents,
*/
int ret = 0;
knot_node_t *node = NULL;
- knot_rrset_t *rrset = NULL;
+ knot_rrset_t *rrset = NULL, *rrsigs = NULL;
int is_nsec3 = 0;
@@ -2501,8 +2516,8 @@ dbg_xfrin_exec_verb(
char *name = knot_dname_to_str(
knot_rrset_owner(chset->remove[i]));
dbg_xfrin_verb("Removing RRSet: %s, type %s\n", name,
- knot_rrtype_to_string(
- knot_rrset_type(chset->remove[i])));
+ knot_rrtype_to_string(
+ knot_rrset_type(chset->remove[i])));
free(name);
);
dbg_xfrin_exec_detail(
@@ -2551,7 +2566,7 @@ dbg_xfrin_exec_detail(
// this should work also for UPDATE
ret = xfrin_apply_remove_rrsigs(changes,
chset->remove[i],
- node, &rrset);
+ node, &rrset, &rrsigs);
} else {
// this should work also for UPDATE
ret = xfrin_apply_remove_normal(changes,
@@ -2559,7 +2574,7 @@ dbg_xfrin_exec_detail(
node, &rrset);
}
- dbg_xfrin_verb("xfrin_apply_remove() ret = %d\n", ret);
+ dbg_xfrin_detail("xfrin_apply_remove() ret = %d\n", ret);
if (ret > 0) {
continue;
@@ -2573,13 +2588,14 @@ dbg_xfrin_exec_detail(
/*----------------------------------------------------------------------------*/
-static int xfrin_apply_add2(knot_zone_contents_t *contents,
- knot_changeset_t *chset,
- knot_changes_t *changes)
+static int xfrin_apply_add(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ knot_changes_t *changes)
{
int ret = 0;
knot_node_t *node = NULL;
knot_rrset_t *rrset = NULL;
+ knot_rrset_t *rrsigs = NULL;
int is_nsec3 = 0;
@@ -2588,8 +2604,8 @@ dbg_xfrin_exec_verb(
char *name = knot_dname_to_str(
knot_rrset_owner(chset->add[i]));
dbg_xfrin_verb("Adding RRSet: %s, type: %s\n", name,
- knot_rrtype_to_string(
- knot_rrset_type(chset->add[i])));
+ knot_rrtype_to_string(
+ knot_rrset_type(chset->add[i])));
free(name);
);
dbg_xfrin_exec_detail(
@@ -2605,14 +2621,14 @@ dbg_xfrin_exec_detail(
knot_rrset_rdata(chset->add[i]))
== KNOT_RRTYPE_NSEC3))
{
- dbg_xfrin_verb("This is NSEC3-related RRSet.\n");
+ dbg_xfrin_detail("This is NSEC3-related RRSet.\n");
is_nsec3 = 1;
}
// check if the old node is not the one we should use
if (!node || knot_rrset_owner(chset->add[i])
!= knot_node_owner(node)) {
- dbg_xfrin_verb("Searching for node...\n");
+ dbg_xfrin_detail("Searching for node...\n");
if (is_nsec3) {
node = knot_zone_contents_get_nsec3_node(
contents,
@@ -2624,7 +2640,8 @@ dbg_xfrin_exec_detail(
if (node == NULL) {
// create new node, connect it properly to the
// zone nodes
- dbg_xfrin_verb("Node not found. Creating new.\n");
+ dbg_xfrin_detail("Node not found. Creating new."
+ "\n");
node = xfrin_add_new_node(contents,
chset->add[i],
is_nsec3);
@@ -2640,7 +2657,8 @@ dbg_xfrin_exec_detail(
if (knot_rrset_type(chset->add[i]) == KNOT_RRTYPE_RRSIG) {
ret = xfrin_apply_add_rrsig(changes, chset->add[i],
- node, &rrset, contents);
+ node, &rrset, &rrsigs,
+ contents);
} else {
ret = xfrin_apply_add_normal(changes, chset->add[i],
node, &rrset, contents);
@@ -2648,8 +2666,8 @@ dbg_xfrin_exec_detail(
assert(ret != KNOT_EOK);
- dbg_xfrin_verb("xfrin_apply_..() returned %d, rrset: %p\n", ret,
- rrset);
+ dbg_xfrin_detail("xfrin_apply_..() returned %s, rrset: %p\n",
+ knot_strerror(ret), rrset);
if (ret > 0) {
if (ret == 1) {
@@ -2670,21 +2688,24 @@ dbg_xfrin_exec_detail(
= chset->add[i];
// the same goes for the RDATA
+ int count = knot_rrset_rdata_rr_count(
+ chset->add[i]);
// connect the RDATA to the list of new RDATA
- int res = xfrin_changes_check_rdata(&changes->new_rdata,
+ int res = xfrin_changes_check_rdata(
+ &changes->new_rdata,
&changes->new_rdata_types,
changes->new_rdata_count,
- &changes->new_rdata_allocated, 1);
+ &changes->new_rdata_allocated, count);
if (res != KNOT_EOK) {
return res;
}
xfrin_changes_add_rdata(changes->new_rdata,
- changes->new_rdata_types,
- &changes->new_rdata_count,
- knot_rrset_get_rdata(chset->add[i]),
- knot_rrset_type(chset->add[i]));
+ changes->new_rdata_types,
+ &changes->new_rdata_count,
+ knot_rrset_get_rdata(chset->add[i]),
+ knot_rrset_type(chset->add[i]));
chset->add[i] = NULL;
} else if (ret == 2) {
@@ -2712,9 +2733,9 @@ dbg_xfrin_exec_detail(
/*----------------------------------------------------------------------------*/
-static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents,
- knot_changes_t *changes,
- knot_changeset_t *chset)
+static int xfrin_apply_replace_soa(knot_zone_contents_t *contents,
+ knot_changes_t *changes,
+ knot_changeset_t *chset)
{
dbg_xfrin("Replacing SOA record.\n");
knot_node_t *node = knot_zone_contents_get_apex(contents);
@@ -2762,11 +2783,13 @@ static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents,
return ret;
}
+ int count = knot_rrset_rdata_rr_count(rrset);
+ count += knot_rrset_rdata_rr_count(chset->soa_to);
// save the new SOA RDATA
ret = xfrin_changes_check_rdata(&changes->new_rdata,
&changes->new_rdata_types,
changes->new_rdata_count,
- &changes->new_rdata_allocated, 1);
+ &changes->new_rdata_allocated, count);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to add new RDATA to list.\n");
return ret;
@@ -2813,9 +2836,9 @@ static int xfrin_apply_replace_soa2(knot_zone_contents_t *contents,
/*----------------------------------------------------------------------------*/
-static int xfrin_apply_changeset2(knot_zone_contents_t *contents,
- knot_changes_t *changes,
- knot_changeset_t *chset)
+static int xfrin_apply_changeset(knot_zone_contents_t *contents,
+ knot_changes_t *changes,
+ knot_changeset_t *chset)
{
/*
* Applies one changeset to the zone. Checks if the changeset may be
@@ -2836,19 +2859,17 @@ static int xfrin_apply_changeset2(knot_zone_contents_t *contents,
return KNOT_ERROR;
}
- int ret = xfrin_apply_remove2(contents, chset, changes);
+ int ret = xfrin_apply_remove(contents, chset, changes);
if (ret != KNOT_EOK) {
-// xfrin_clean_changes_after_fail2(changes);
return ret;
}
- ret = xfrin_apply_add2(contents, chset, changes);
+ ret = xfrin_apply_add(contents, chset, changes);
if (ret != KNOT_EOK) {
-// xfrin_clean_changes_after_fail(changes);
return ret;
}
- return xfrin_apply_replace_soa2(contents, changes, chset);
+ return xfrin_apply_replace_soa(contents, changes, chset);
}
/*----------------------------------------------------------------------------*/
@@ -2871,6 +2892,8 @@ static void xfrin_mark_empty(knot_node_t *node, void *data)
}
changes->old_nodes[changes->old_nodes_count++] = node;
+ // mark the node as empty
+ knot_node_set_empty(node);
if (node->parent != NULL) {
assert(node->parent->children > 0);
@@ -2903,6 +2926,8 @@ static void xfrin_mark_empty_nsec3(knot_node_t *node, void *data)
}
changes->old_nsec3[changes->old_nsec3_count++] = node;
+ // mark the node as empty
+ knot_node_set_empty(node);
if (node->parent != NULL) {
assert(node->parent->children > 0);
@@ -2950,11 +2975,13 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents,
zone_node = NULL;
hash_item = NULL;
+dbg_xfrin_exec_detail(
char *name = knot_dname_to_str(knot_node_owner(
changes->old_nodes[i]));
dbg_xfrin_detail("Old node #%d: %p, %s\n", i,
changes->old_nodes[i], name);
free(name);
+);
ret = knot_zone_contents_remove_node(
contents, changes->old_nodes[i], &zone_node,
@@ -2967,9 +2994,7 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents,
free(hash_item);
free(zone_node);
- knot_node_free(&changes->old_nodes[i], 0);
}
- changes->old_nodes_count = 0;
// remove NSEC3 nodes
for (int i = 0; i < changes->old_nsec3_count; ++i) {
@@ -2990,9 +3015,7 @@ static int xfrin_remove_empty_nodes(knot_zone_contents_t *contents,
}
free(zone_node);
- knot_node_free(&changes->old_nsec3[i], 0);
}
- changes->old_nsec3_count = 0;
return KNOT_EOK;
}
@@ -3060,9 +3083,6 @@ int xfrin_apply_changesets(knot_zone_t *zone,
dbg_xfrin("Applying changesets to zone...\n");
-// dbg_xfrin("\nOLD ZONE CONTENTS:\n\n");
-// knot_zone_contents_dump(old_contents, 1);
-
/*
* Ensure that the zone generation is set to 0.
*/
@@ -3130,7 +3150,7 @@ int xfrin_apply_changesets(knot_zone_t *zone,
dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n",
old_contents->apex, contents_copy->apex);
for (int i = 0; i < chsets->count; ++i) {
- if ((ret = xfrin_apply_changeset2(contents_copy, changes,
+ if ((ret = xfrin_apply_changeset(contents_copy, changes,
&chsets->sets[i]))
!= KNOT_EOK) {
xfrin_rollback_update(old_contents,
@@ -3155,7 +3175,8 @@ int xfrin_apply_changesets(knot_zone_t *zone,
*/
/*
- * Select and delete empty nodes.
+ * Select and remove empty nodes from zone trees. Do not free them right
+ * away as they may be referenced by some domain names.
*/
ret = xfrin_remove_empty_nodes(contents_copy, changes);
if (ret != KNOT_EOK) {
@@ -3165,11 +3186,9 @@ int xfrin_apply_changesets(knot_zone_t *zone,
return ret;
}
-
dbg_xfrin("Adjusting zone contents.\n");
dbg_xfrin_verb("Old contents apex: %p, new apex: %p\n",
old_contents->apex, contents_copy->apex);
-// ret = xfrin_adjust_contents(contents_copy, &changes);
ret = knot_zone_contents_adjust(contents_copy);
if (ret != KNOT_EOK) {
dbg_xfrin("Failed to finalize zone contents: %s\n",
@@ -3187,7 +3206,6 @@ int xfrin_apply_changesets(knot_zone_t *zone,
return ret;
}
- //xfrin_cleanup_update(&changes);
chsets->changes = changes;
*new_contents = contents_copy;
@@ -3215,6 +3233,20 @@ int xfrin_switch_zone(knot_zone_t *zone,
dbg_xfrin_verb("Old contents: %p, apex: %p, new apex: %p\n",
old, (old) ? old->apex : NULL, new_contents->apex);
+ // switch pointers in domain names, now only the new zone is used
+ if (transfer_type == XFR_TYPE_IIN) {
+ // Traverse also the dname table and change the node pointers
+ // in dnames
+ int ret = knot_zone_contents_dname_table_apply(
+ new_contents,
+ xfrin_switch_node_in_dname_table, NULL);
+ assert(ret == KNOT_EOK);
+ }
+
+ // set generation to old, so that the flags may be used in next transfer
+ // and we do not search for new nodes anymore
+ knot_zone_contents_set_gen_old(new_contents);
+
// wait for readers to finish
dbg_xfrin_verb("Waiting for readers to finish...\n");
synchronize_rcu();
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
index b009152..a762b81 100644..100755
--- a/src/libknot/updates/xfr-in.h
+++ b/src/libknot/updates/xfr-in.h
@@ -5,7 +5,7 @@
*
* \brief XFR client API.
*
- * \addtogroup query_processing
+ * \addtogroup xfr
* @{
*/
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
@@ -189,9 +189,6 @@ int xfrin_switch_zone(knot_zone_t *zone,
knot_zone_contents_t *new_contents,
int deep_free);
-//void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents,
-// knot_zone_contents_t **new_contents);
-
void xfrin_cleanup_successful_update(knot_changes_t **changes);
void xfrin_rollback_update(knot_zone_contents_t *old_contents,
diff --git a/src/libknot/util/conv.c b/src/libknot/util/conv.c
deleted file mode 100644
index 6626ddd..0000000
--- a/src/libknot/util/conv.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (c) 2001-2011, 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.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <ctype.h>
-#include "conv.h"
-
-#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
-
-
-static const char Base64[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char Pad64 = '=';
-
-static int b64rmap_initialized = 0;
-static uint8_t b64rmap[256];
-
-static const uint8_t b64rmap_special = 0xf0;
-static const uint8_t b64rmap_end = 0xfd;
-static const uint8_t b64rmap_space = 0xfe;
-static const uint8_t b64rmap_invalid = 0xff;
-
-/**
- * Initializing the reverse map is not thread safe.
- * Which is fine for NSD. For now...
- **/
-static void b64_initialize_rmap()
-{
- int i;
- char ch;
-
- /* Null: end of string, stop parsing */
- b64rmap[0] = b64rmap_end;
-
- for (i = 1; i < 256; ++i) {
- ch = (char)i;
- /* Whitespaces */
- if (isspace(ch)) {
- b64rmap[i] = b64rmap_space;
- }
- /* Padding: stop parsing */
- else if (ch == Pad64) {
- b64rmap[i] = b64rmap_end;
- }
- /* Non-base64 char */
- else {
- b64rmap[i] = b64rmap_invalid;
- }
- }
-
- /* Fill reverse mapping for base64 chars */
- for (i = 0; Base64[i] != '\0'; ++i) {
- b64rmap[(uint8_t)Base64[i]] = i;
- }
-
- b64rmap_initialized = 1;
-}
-
-static int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
-{
- int tarindex, state, ch;
- uint8_t ofs;
-
- state = 0;
- tarindex = 0;
-
- while (1) {
- ch = *src++;
- ofs = b64rmap[ch];
-
- if (ofs >= b64rmap_special) {
- /* Ignore whitespaces */
- if (ofs == b64rmap_space) {
- continue;
- }
- /* End of base64 characters */
- if (ofs == b64rmap_end) {
- break;
- }
- /* A non-base64 character. */
- return (-1);
- }
-
- switch (state) {
- case 0:
- if ((size_t)tarindex >= targsize) {
- return (-1);
- }
- target[tarindex] = ofs << 2;
- state = 1;
- break;
- case 1:
- if ((size_t)tarindex + 1 >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs >> 4;
- target[tarindex+1] = (ofs & 0x0f)
- << 4 ;
- tarindex++;
- state = 2;
- break;
- case 2:
- if ((size_t)tarindex + 1 >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs >> 2;
- target[tarindex+1] = (ofs & 0x03)
- << 6;
- tarindex++;
- state = 3;
- break;
- case 3:
- if ((size_t)tarindex >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs;
- tarindex++;
- state = 0;
- break;
- default:
- abort();
- }
- }
-
- /*
- * We are done decoding Base-64 chars. Let's see if we ended
- * on a byte boundary, and/or with erroneous trailing characters.
- */
-
- if (ch == Pad64) { /* We got a pad char. */
- ch = *src++; /* Skip it, get next. */
- switch (state) {
- case 0: /* Invalid = in first position */
- case 1: /* Invalid = in second position */
- return (-1);
-
- case 2: /* Valid, means one byte of info */
- /* Skip any number of spaces. */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- break;
- }
- /* Make sure there is another trailing = sign. */
- if (ch != Pad64) {
- return (-1);
- }
- ch = *src++; /* Skip the = */
- /* Fall through to "single trailing =" case. */
- /* FALLTHROUGH */
-
- case 3: /* Valid, means two bytes of info */
- /*
- * We know this char is an =. Is there anything but
- * whitespace after it?
- */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- return (-1);
- }
-
- /*
- * Now make sure for cases 2 and 3 that the "extra"
- * bits that slopped past the last full byte were
- * zeros. If we don't check them, they become a
- * subliminal channel.
- */
- if (target[tarindex] != 0) {
- return (-1);
- }
- }
- } else {
- /*
- * We ended by seeing the end of the string. Make sure we
- * have no partial bytes lying around.
- */
- if (state != 0) {
- return (-1);
- }
- }
-
- return (tarindex);
-}
-
-
-static int b64_pton_len(char const *src)
-{
- int tarindex, state, ch;
- uint8_t ofs;
-
- state = 0;
- tarindex = 0;
-
- while (1) {
- ch = *src++;
- ofs = b64rmap[ch];
-
- if (ofs >= b64rmap_special) {
- /* Ignore whitespaces */
- if (ofs == b64rmap_space) {
- continue;
- }
- /* End of base64 characters */
- if (ofs == b64rmap_end) {
- break;
- }
- /* A non-base64 character. */
- return (-1);
- }
-
- switch (state) {
- case 0:
- state = 1;
- break;
- case 1:
- tarindex++;
- state = 2;
- break;
- case 2:
- tarindex++;
- state = 3;
- break;
- case 3:
- tarindex++;
- state = 0;
- break;
- default:
- abort();
- }
- }
-
- /*
- * We are done decoding Base-64 chars. Let's see if we ended
- * on a byte boundary, and/or with erroneous trailing characters.
- */
-
- if (ch == Pad64) { /* We got a pad char. */
- ch = *src++; /* Skip it, get next. */
- switch (state) {
- case 0: /* Invalid = in first position */
- case 1: /* Invalid = in second position */
- return (-1);
-
- case 2: /* Valid, means one byte of info */
- /* Skip any number of spaces. */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- break;
- }
- /* Make sure there is another trailing = sign. */
- if (ch != Pad64) {
- return (-1);
- }
- ch = *src++; /* Skip the = */
- /* Fall through to "single trailing =" case. */
- /* FALLTHROUGH */
-
- case 3: /* Valid, means two bytes of info */
- /*
- * We know this char is an =. Is there anything but
- * whitespace after it?
- */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- return (-1);
- }
-
- }
- } else {
- /*
- * We ended by seeing the end of the string. Make sure we
- * have no partial bytes lying around.
- */
- if (state != 0) {
- return (-1);
- }
- }
-
- return (tarindex);
-}
-
-int b64_pton(char const *src, uint8_t *target, size_t targsize)
-{
- if (!b64rmap_initialized) {
- b64_initialize_rmap();
- }
-
- if (target) {
- return b64_pton_do(src, target, targsize);
- } else {
- return b64_pton_len(src);
- }
-}
-
-#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
diff --git a/src/libknot/util/conv.h b/src/libknot/util/conv.h
deleted file mode 100644
index d1e6dae..0000000
--- a/src/libknot/util/conv.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2001-2011, 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.
- */
-
-#ifndef _KNOT_CONV_H_
-#define _KNOT_CONV_H_
-
-#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
-
-/*!
- * \brief Base64 presentation to wire conversion.
- */
-int b64_pton(char const *src, uint8_t *target, size_t targsize);
-
-#endif // _KNOT_CONV_H_
diff --git a/src/libknot/util/debug.c b/src/libknot/util/debug.c
index f14c3cd..a6555ad 100644..100755
--- a/src/libknot/util/debug.c
+++ b/src/libknot/util/debug.c
@@ -105,7 +105,6 @@ void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone)
return;
}
- fprintf(stderr, " rdata count: %d\n", rrset->rdata->count);
knot_rdata_t *tmp = rrset->rdata;
while (tmp->next != rrset->rdata && tmp->next != NULL) {
@@ -133,11 +132,6 @@ void knot_node_dump(knot_node_t *node, void *loaded_zone)
hex_print((char *)node->owner->labels, node->owner->label_count);
fprintf(stderr, "node: %p\n", node);
fprintf(stderr, "node (in node's owner): %p\n", node->owner->node);
- if (loaded_zone && node->prev != NULL) {
- name = knot_dname_to_str(node->prev->owner);
- fprintf(stderr, "previous node: %s\n", name);
- free(name);
- }
if (knot_node_is_deleg_point(node)) {
fprintf(stderr, "delegation point\n");
@@ -197,6 +191,8 @@ void knot_node_dump(knot_node_t *node, void *loaded_zone)
} else {
fprintf(stderr, "none\n");
}
+
+ fprintf(stderr, "Zone: %p\n", node->zone);
fprintf(stderr, "RRSet count: %d\n", node->rrset_count);
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
index b6aba6e..731fed8 100644..100755
--- a/src/libknot/util/debug.h
+++ b/src/libknot/util/debug.h
@@ -42,6 +42,7 @@
#define KNOT_ZONE_DEBUG
#define KNOT_ZONEDB_DEBUG
#define KNOT_NODE_DEBUG
+ #define KNOT_ZONEDIFF_DEBUG
#endif
#ifdef KNOT_NS_DEBUG
@@ -368,6 +369,48 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
#define dbg_zonedb_exec_detail(cmds)
#endif
+#ifdef KNOT_ZONEDIFF_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zonediff(msg...) fprintf(stderr, msg)
+#define dbg_zonediff_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_zonediff(msg...)
+#define dbg_zonediff_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zonediff_verb(msg...) fprintf(stderr, msg)
+#define dbg_zonediff_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_zonediff_verb(msg...)
+#define dbg_zonediff_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zonediff_detail(msg...) fprintf(stderr, msg)
+#define dbg_zonediff_hex_detail(data, len) hex_print((data), (len))
+#define dbg_zonediff_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zonediff_detail(msg...)
+#define dbg_zonediff_hex_detail(data, len)
+#define dbg_zonediff_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zonediff(msg...)
+#define dbg_zonediff_hex(data, len)
+#define dbg_zonediff_verb(msg...)
+#define dbg_zonediff_hex_verb(data, len)
+#define dbg_zonediff_detail(msg...)
+#define dbg_zonediff_hex_detail(data, len)
+#define dbg_zonediff_exec_detail(cmds)
+#endif
+
/******************************************************************************/
#ifdef KNOT_RESPONSE_DEBUG
@@ -607,7 +650,6 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
/* Brief messages. */
#ifdef DEBUG_ENABLE_BRIEF
#define dbg_ck_hash(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
-#define dbg_ck_rehash(msg...) log_msg(LOG_SERVER, LOG_DEBUG, msg)
#define dbg_ck_hash_hex(data, len) hex_log(LOG_SERVER, (data), (len))
#else
#define dbg_ck_hash(msg...)
@@ -636,7 +678,6 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
/* No messages. */
#else
#define dbg_ck_hash(msg...)
-#define dbg_ck_rehash(msg...)
#define dbg_ck_hash_hex(data, len)
#define dbg_ck_hash_verb(msg...)
#define dbg_ck_hash_hex_verb(data, len)
@@ -646,6 +687,21 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
/******************************************************************************/
+#ifdef KNOT_STASH_DEBUG
+
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_stash(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
+#else
+#define dbg_stash(msg...)
+#endif
+
+#else
+#define dbg_stash(msg...)
+#endif
+
+
+/******************************************************************************/
+
#ifdef KNOT_XFRIN_DEBUG
/* Brief messages. */
@@ -780,37 +836,46 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
#ifdef DEBUG_ENABLE_BRIEF
#define dbg_rrset(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_rrset_hex(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_rrset_exec(cmds) do { cmds } while (0)
#else
#define dbg_rrset(msg...)
#define dbg_rrset_hex(data, len)
+#define dbg_rrset_exec(cmds)
#endif
/* Verbose messages. */
#ifdef DEBUG_ENABLE_VERBOSE
#define dbg_rrset_verb(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_rrset_hex_verb(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_rrset_exec_verb(cmds) do { cmds } while (0)
#else
#define dbg_rrset_verb(msg...)
#define dbg_rrset_hex_verb(data, len)
+#define dbg_rrset_exec_verb(cmds)
#endif
/* Detail messages. */
#ifdef DEBUG_ENABLE_DETAILS
#define dbg_rrset_detail(msg...) log_msg(LOG_ZONE, LOG_DEBUG, msg)
#define dbg_rrset_hex_detail(data, len) hex_log(LOG_ZONE, (data), (len))
+#define dbg_rrset_exec_detail(cmds) do { cmds } while (0)
#else
#define dbg_rrset_detail(msg...)
#define dbg_rrset_hex_detail(data, len)
+#define dbg_rrset_exec_detail(cmds)
#endif
/* No messages. */
#else
#define dbg_rrset(msg...)
#define dbg_rrset_hex(data, len)
+#define dbg_rrset_exec(cmds)
#define dbg_rrset_verb(msg...)
#define dbg_rrset_hex_verb(data, len)
+#define dbg_rrset_exec_verb(cmds)
#define dbg_rrset_detail(msg...)
#define dbg_rrset_hex_detail(data, len)
+#define dbg_rrset_exec_detail(cmds)
#endif
/******************************************************************************/
diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c
index 63e1747..35ae77e 100644..100755
--- a/src/libknot/util/descriptor.c
+++ b/src/libknot/util/descriptor.c
@@ -569,3 +569,5 @@ int knot_rrtype_is_metatype(uint16_t type)
|| type == KNOT_RRTYPE_OPT);
}
+/*! @} */
+
diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h
index b7e4a53..b7e4a53 100644..100755
--- a/src/libknot/util/descriptor.h
+++ b/src/libknot/util/descriptor.h
diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h
index 888669a..96eff68 100644..100755
--- a/src/libknot/util/error.h
+++ b/src/libknot/util/error.h
@@ -52,6 +52,7 @@ enum knot_error {
KNOT_EACCES, /*!< Permission is denied. */
KNOT_ECRYPTO, /*!< Error in crypto library. */
KNOT_ENSEC3PAR, /*!< Missing or wrong NSEC3PARAM record. */
+ KNOT_ENSEC3CHAIN, /*!< Missing or wrong NSEC3 chain in the zone. */
KNOT_EBADZONE, /*!< Domain name does not belong to the zone. */
KNOT_EHASH, /*!< Error in hash table. */
KNOT_EZONEIN, /*!< Error inserting zone. */
@@ -65,10 +66,12 @@ enum knot_error {
KNOT_ENOXFR, /*!< Transfer was not sent. */
KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */
KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */
+ KNOT_EXFRDENIED, /*!< Transfer not allowed. */
KNOT_ECONN, /*!< Connection reset. */
KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */
KNOT_ECNAME, /*!< CNAME loop found in zone. */
- KNOT_ERROR_COUNT = 34
+ KNOT_ENODIFF, /*!< No zone diff can be created. */
+ KNOT_ERROR_COUNT = 37
};
/*! \brief Table linking error messages to error codes. */
diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c
index f787565..dd4280d 100644..100755
--- a/src/libknot/util/libknot_error.c
+++ b/src/libknot/util/libknot_error.c
@@ -24,7 +24,7 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
{KNOT_ENOTSUP, "Operation not supported."},
{KNOT_EAGAIN, "OS lacked necessary resources."},
{KNOT_ERANGE, "Value is out of range."},
- {KNOT_EBADARG, "Wrong argument supported."},
+ {KNOT_EBADARG, "Wrong argument supplied."},
{KNOT_EFEWDATA, "Not enough data to parse."},
{KNOT_ESPACE, "Not enough space provided."},
{KNOT_EMALF, "Malformed data."},
@@ -32,6 +32,7 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
{KNOT_EACCES, "Permission to perform requested operation is denied."},
{KNOT_ECRYPTO, "Error in crypto library."},
{KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record."},
+ {KNOT_ENSEC3CHAIN, "Missing or wrong NSEC3 chain in the zone."},
{KNOT_EBADZONE, "Domain name does not belong to the given zone."},
{KNOT_EHASH, "Error in hash table."},
{KNOT_EZONEIN, "Error inserting zone."},
@@ -45,11 +46,13 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
{KNOT_ENOXFR, "Transfer was not sent."},
{KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)."},
{KNOT_EXFRREFUSED, "Zone transfer refused by the server."},
+ {KNOT_EXFRDENIED, "Transfer not allowed."},
{KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC." },
{KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." },
{KNOT_TSIG_EBADTIME, "TSIG signing time out of range." },
{KNOT_ECONN, "Connection reset."},
{KNOT_EIXFRSPACE, "IXFR reply did not fit in."},
{KNOT_ECNAME, "CNAME loop found in zone."},
+ {KNOT_ENODIFF, "Cannot create zone diff."},
{KNOT_ERROR, 0}
};
diff --git a/src/libknot/util/tolower.c b/src/libknot/util/tolower.c
index d71c467..d71c467 100644..100755
--- a/src/libknot/util/tolower.c
+++ b/src/libknot/util/tolower.c
diff --git a/src/libknot/util/tolower.h b/src/libknot/util/tolower.h
index 6b9e98c..2e92258 100644..100755
--- a/src/libknot/util/tolower.h
+++ b/src/libknot/util/tolower.h
@@ -55,3 +55,5 @@ static inline uint8_t knot_tolower(uint8_t c) {
}
#endif /* _KNOT_TOLOWER_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/utils.c b/src/libknot/util/utils.c
index 04e12c5..04e12c5 100644..100755
--- a/src/libknot/util/utils.c
+++ b/src/libknot/util/utils.c
diff --git a/src/libknot/util/utils.h b/src/libknot/util/utils.h
index fd275b3..fd275b3 100644..100755
--- a/src/libknot/util/utils.h
+++ b/src/libknot/util/utils.h
diff --git a/src/libknot/util/wire.h b/src/libknot/util/wire.h
index 0a24ff1..0a24ff1 100644..100755
--- a/src/libknot/util/wire.h
+++ b/src/libknot/util/wire.h
diff --git a/src/libknot/zone/dname-table.c b/src/libknot/zone/dname-table.c
index d2d97c2..d2d97c2 100644..100755
--- a/src/libknot/zone/dname-table.c
+++ b/src/libknot/zone/dname-table.c
diff --git a/src/libknot/zone/dname-table.h b/src/libknot/zone/dname-table.h
index 945b6de..945b6de 100644..100755
--- a/src/libknot/zone/dname-table.h
+++ b/src/libknot/zone/dname-table.h
diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c
index 4ad2a27..c196f29 100644..100755
--- a/src/libknot/zone/node.c
+++ b/src/libknot/zone/node.c
@@ -81,67 +81,29 @@ static inline void knot_node_flags_set_nonauth(uint8_t *flags)
*flags |= KNOT_NODE_FLAGS_NONAUTH;
}
-///*----------------------------------------------------------------------------*/
-///*!
-// * \brief Returns the old node flag
-// *
-// * \param flags Flags to retrieve the flag from.
-// *
-// * \return A byte with only the old node flag set if it was set in \a flags.
-// */
-//static inline uint8_t knot_node_flags_get_old(uint8_t flags)
-//{
-// return flags & KNOT_NODE_FLAGS_OLD;
-//}
-
-///*----------------------------------------------------------------------------*/
-///*!
-// * \brief Sets the old node flag.
-// *
-// * \param flags Flags to set the flag in.
-// */
-//static inline void knot_node_flags_set_new(uint8_t *flags)
-//{
-// *flags |= KNOT_NODE_FLAGS_NEW;
-//}
-
-///*----------------------------------------------------------------------------*/
-///*!
-// * \brief Returns the new node flag
-// *
-// * \param flags Flags to retrieve the flag from.
-// *
-// * \return A byte with only the new node flag set if it was set in \a flags.
-// */
-//static inline uint8_t knot_node_flags_get_new(uint8_t flags)
-//{
-// return flags & KNOT_NODE_FLAGS_NEW;
-//}
-
-///*----------------------------------------------------------------------------*/
-///*!
-// * \brief Sets the new node flag.
-// *
-// * \param flags Flags to set the flag in.
-// */
-//static inline void knot_node_flags_set_old(uint8_t *flags)
-//{
-// *flags |= KNOT_NODE_FLAGS_OLD;
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//static inline void knot_node_flags_clear_new(uint8_t *flags)
-//{
-// *flags &= ~KNOT_NODE_FLAGS_NEW;
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//static inline void knot_node_flags_clear_old(uint8_t *flags)
-//{
-// *flags &= ~KNOT_NODE_FLAGS_OLD;
-//}
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the empty node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_empty(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_EMPTY;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the empty node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the empty node flag set if it was set in \a flags.
+ */
+static inline uint8_t knot_node_flags_get_empty(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_EMPTY;
+}
/*----------------------------------------------------------------------------*/
/*!
@@ -166,26 +128,6 @@ static int compare_rrset_types(void *rr1, void *rr2)
}
/*----------------------------------------------------------------------------*/
-
-//static int knot_node_zone_gen_is_new(const knot_node_t *node)
-//{
-// assert(node->zone != NULL);
-// knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
-// assert(cont != NULL);
-// return knot_zone_contents_gen_is_new(cont);
-//}
-
-///*----------------------------------------------------------------------------*/
-
-//static int knot_node_zone_gen_is_old(const knot_node_t *node)
-//{
-// assert(node->zone != NULL);
-// knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
-// assert(cont != NULL);
-// return knot_zone_contents_gen_is_old(cont);
-//}
-
-/*----------------------------------------------------------------------------*/
/* API functions */
/*----------------------------------------------------------------------------*/
@@ -211,9 +153,12 @@ knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
}
/*----------------------------------------------------------------------------*/
-
+/*! \todo Consider replacing rrset_merge() with rrset_merge_no_dupl(). Currently
+ * this function is never called with merge=1, so it's not a problem,
+ * but it may be in the future.
+ */
int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
- int merge)
+ int merge)
{
if (node == NULL) {
return KNOT_EBADARG;
@@ -235,6 +180,24 @@ int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
}
}
+int knot_node_add_rrset_no_dupl(knot_node_t *node, knot_rrset_t *rrset)
+{
+ int ret = 0;
+
+ if ((ret = (gen_tree_add(node->rrset_tree, rrset,
+ knot_rrset_merge_no_dupl))) < 0) {
+ dbg_node("Failed to add rrset to node->rrset_tree.\n");
+ return KNOT_ERROR;
+ }
+
+ if (ret >= 0) {
+ node->rrset_count += (ret > 0 ? 0 : 1);
+ return ret;
+ } else {
+ return KNOT_ERROR;
+ }
+}
+
/*----------------------------------------------------------------------------*/
const knot_rrset_t *knot_node_rrset(const knot_node_t *node,
@@ -315,6 +278,8 @@ struct knot_node_save_rrset_arg {
size_t max_count;
};
+/*----------------------------------------------------------------------------*/
+
static void save_rrset_to_array(void *node, void *data)
{
struct knot_node_save_rrset_arg *args =
@@ -329,6 +294,8 @@ static void save_rrset_to_array(void *node, void *data)
args->array[args->count++] = rrset;
}
+/*----------------------------------------------------------------------------*/
+
knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
{
if (node == NULL || node->rrset_count == 0) {
@@ -463,17 +430,6 @@ knot_node_t *knot_node_get_previous(const knot_node_t *node)
/*----------------------------------------------------------------------------*/
-const knot_node_t *knot_node_next(const knot_node_t *node)
-{
- if (node == NULL) {
- return NULL;
- }
-
- return node->next;
-}
-
-/*----------------------------------------------------------------------------*/
-
void knot_node_set_previous(knot_node_t *node, knot_node_t *prev)
{
if (node == NULL) {
@@ -620,7 +576,7 @@ void knot_node_set_new_node(knot_node_t *node,
/*----------------------------------------------------------------------------*/
-void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone)
+void knot_node_set_zone(knot_node_t *node, const knot_zone_t *zone)
{
if (node == NULL) {
return;
@@ -631,6 +587,17 @@ void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone)
/*----------------------------------------------------------------------------*/
+const knot_zone_t *knot_node_zone(const knot_node_t *node)
+{
+ if (node == NULL) {
+ return NULL;
+ }
+
+ return node->zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
void knot_node_update_ref(knot_node_t **ref)
{
if (*ref != NULL && (*ref)->new_node != NULL) {
@@ -644,8 +611,6 @@ void knot_node_update_refs(knot_node_t *node)
{
// reference to previous node
knot_node_update_ref(&node->prev);
- // reference to next node
- knot_node_update_ref(&node->next);
// reference to parent
knot_node_update_ref(&node->parent);
// reference to wildcard child
@@ -713,6 +678,20 @@ int knot_node_is_auth(const knot_node_t *node)
/*----------------------------------------------------------------------------*/
+int knot_node_is_empty(const knot_node_t *node)
+{
+ return knot_node_flags_get_empty(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_empty(knot_node_t *node)
+{
+ knot_node_flags_set_empty(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
static void knot_node_free_rrsets_from_tree(void *item, void *data)
{
if (item == NULL) {
@@ -727,14 +706,6 @@ static void knot_node_free_rrsets_from_tree(void *item, void *data)
void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames)
{
- /* CLEANUP */
-// knot_rrset_t **rrsets = knot_node_get_rrsets(node);
-// for (int i = 0; i < node->rrset_count; i++) {
-// knot_rrset_deep_free(&(rrsets[i]), 0, 1, free_rdata_dnames);
-// }
-
-// free(rrsets);
-
if (node == NULL) {
return;
}
@@ -748,16 +719,16 @@ void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames)
/*----------------------------------------------------------------------------*/
-void knot_node_free(knot_node_t **node, int fix_refs)
+void knot_node_free(knot_node_t **node)
{
if (node == NULL || *node == NULL) {
return;
}
- dbg_node("Freeing node: %p\n", *node);
+ dbg_node_detail("Freeing node: %p\n", *node);
if ((*node)->rrset_tree != NULL) {
- dbg_node("Freeing RRSets.\n");
+ dbg_node_detail("Freeing RRSets.\n");
gen_tree_destroy(&(*node)->rrset_tree, NULL, NULL);
}
@@ -768,53 +739,13 @@ void knot_node_free(knot_node_t **node, int fix_refs)
knot_dname_set_node((*node)->owner, NULL);
}
- dbg_node("Releasing owner.\n");
+ dbg_node_detail("Releasing owner.\n");
knot_dname_release((*node)->owner);
- // check nodes referencing this node and fix the references
-
- if (fix_refs) {
- // previous node
- dbg_node("Checking previous.\n");
- if ((*node)->prev && (*node)->prev->next == (*node)) {
- (*node)->prev->next = (*node)->next;
- }
-
- dbg_node("Checking next.\n");
- if ((*node)->next && (*node)->next->prev == (*node)) {
- (*node)->next->prev = (*node)->prev;
- }
-
- // NSEC3 node
- dbg_node("Checking NSEC3.\n");
- if ((*node)->nsec3_node
- && (*node)->nsec3_node->nsec3_referer == (*node)) {
- (*node)->nsec3_node->nsec3_referer = NULL;
- }
-
- dbg_node("Checking NSEC3 ref.\n");
- if ((*node)->nsec3_referer
- && (*node)->nsec3_referer->nsec3_node == (*node)) {
- (*node)->nsec3_referer->nsec3_node = NULL;
- }
-
- // wildcard child node
- dbg_node("Checking parent's wildcard child.\n");
- if ((*node)->parent
- && (*node)->parent->wildcard_child == (*node)) {
- (*node)->parent->wildcard_child = NULL;
- }
-
- // fix parent's children count
- if ((*node)->parent) {
- --(*node)->parent->children;
- }
- }
-
free(*node);
*node = NULL;
- dbg_node("Done.\n");
+ dbg_node_detail("Done.\n");
}
/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h
index 11cd0ce..1ab814a 100644..100755
--- a/src/libknot/zone/node.h
+++ b/src/libknot/zone/node.h
@@ -62,8 +62,6 @@ struct knot_node {
*/
struct knot_node *prev;
- struct knot_node *next;
-
/*!
* \brief NSEC3 node corresponding to this node.
*
@@ -74,7 +72,7 @@ struct knot_node {
struct knot_node *nsec3_referer;
- struct knot_zone *zone;
+ const struct knot_zone *zone;
struct knot_node *new_node;
@@ -83,22 +81,12 @@ struct knot_node {
unsigned short rrset_count; /*!< Number of RRSets stored in the node. */
/*!
- * \brief Generation of node to be used.
- *
- * If set to 0, the old node will be used. Otherwise new nodes will
- * be used. This applies when getting some referenced node.
-
- */
-// short **generation;
-
- /*!
* \brief Various flags.
*
* Currently only two:
* 0x01 - node is a delegation point
* 0x02 - node is non-authoritative (under a delegation point)
- * 0x80 - node is old and will be removed (during update)
- * 0x40 - node is new, should not be used while zone is old
+ * 0x10 - node is empty and will be deleted after update
*/
uint8_t flags;
};
@@ -113,9 +101,11 @@ typedef enum {
/*! \brief Node is not authoritative (i.e. below a zone cut). */
KNOT_NODE_FLAGS_NONAUTH = (uint8_t)0x02,
/*! \brief Node is old and will be removed (during update). */
- KNOT_NODE_FLAGS_OLD = (uint8_t)0x80,
+ KNOT_NODE_FLAGS_OLD = (uint8_t)0x04,
/*! \brief Node is new and should not be used while zoen is old. */
- KNOT_NODE_FLAGS_NEW = (uint8_t)0x40
+ KNOT_NODE_FLAGS_NEW = (uint8_t)0x08,
+ /*! \brief Node is empty and will be deleted after update. */
+ KNOT_NODE_FLAGS_EMPTY = (uint8_t)0x10
} knot_node_flags_t;
/*----------------------------------------------------------------------------*/
@@ -147,6 +137,8 @@ knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
int merge);
+int knot_node_add_rrset_no_dupl(knot_node_t *node, knot_rrset_t *rrset);
+
/*!
* \brief Returns the RRSet of the given type from the node.
*
@@ -255,8 +247,6 @@ const knot_node_t *knot_node_previous(const knot_node_t *node);
*/
knot_node_t *knot_node_get_previous(const knot_node_t *node);
-const knot_node_t *knot_node_next(const knot_node_t *node);
-
/*!
* \brief Sets the previous node of the given node.
*
@@ -351,7 +341,9 @@ knot_node_t *knot_node_get_new_node(const knot_node_t *node);
void knot_node_set_new_node(knot_node_t *node, knot_node_t *new_node);
-void knot_node_set_zone(knot_node_t *node, struct knot_zone *zone);
+void knot_node_set_zone(knot_node_t *node, const struct knot_zone *zone);
+
+const struct knot_zone *knot_node_zone(const knot_node_t *node);
void knot_node_update_ref(knot_node_t **ref);
@@ -405,6 +397,10 @@ void knot_node_clear_new(knot_node_t *node);
void knot_node_clear_old(knot_node_t *node);
+int knot_node_is_empty(const knot_node_t *node);
+
+void knot_node_set_empty(knot_node_t *node);
+
/*!
* \brief Destroys the RRSets within the node structure.
*
@@ -428,7 +424,7 @@ void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames);
*
* \todo Document missing parameters.
*/
-void knot_node_free(knot_node_t **node, int fix_refs);
+void knot_node_free(knot_node_t **node);
/*!
* \brief Compares two nodes according to their owner.
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index 19268c4..7e453a5 100644..100755
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -20,6 +20,8 @@
#include "util/error.h"
#include "util/debug.h"
#include "common/base32hex.h"
+/*! \todo XXX TODO FIXME remove once testing is done. */
+#include "zcompile/zcompile.h"
#include "consts.h"
/*----------------------------------------------------------------------------*/
@@ -42,7 +44,7 @@ typedef struct {
const uint8_t KNOT_ZONE_FLAGS_GEN_OLD = 0; /* xxxxxx00 */
const uint8_t KNOT_ZONE_FLAGS_GEN_NEW = 1 << 0; /* xxxxxx01 */
-const uint8_t KNOT_ZONE_FLAGS_GEN_FIN = 1 << 2; /* xxxxxx10 */
+const uint8_t KNOT_ZONE_FLAGS_GEN_FIN = 1 << 1; /* xxxxxx10 */
const uint8_t KNOT_ZONE_FLAGS_GEN_MASK = 3; /* 00000011 */
const uint8_t KNOT_ZONE_FLAGS_ANY_MASK = 4; /* 00000100 */
const uint8_t KNOT_ZONE_FLAGS_ANY = 4; /* 00000100 */
@@ -91,8 +93,8 @@ dbg_zone_exec(
char *node_owner = knot_dname_to_str(knot_node_owner(node));
char *apex_owner = knot_dname_to_str(contents->apex->owner);
dbg_zone("zone: Trying to insert foreign node to a "
- "zone. Node owner: %s, zone apex: %s\n",
- node_owner, apex_owner);
+ "zone. Node owner: %s, zone apex: %s\n",
+ node_owner, apex_owner);
free(node_owner);
free(apex_owner);
);
@@ -137,7 +139,7 @@ static void knot_zone_contents_destroy_node_owner_from_tree(
UNUSED(data);
/*!< \todo change completely! */
- knot_node_free(&tnode->node, 0);
+ knot_node_free(&tnode->node);
}
/*----------------------------------------------------------------------------*/
@@ -159,11 +161,8 @@ static int knot_zone_contents_dnames_from_rdata_to_table(
== KNOT_RDATA_WF_UNCOMPRESSED_DNAME
|| d->wireformat[j]
== KNOT_RDATA_WF_LITERAL_DNAME) {
-// printf("Saving dname from rdata to dname table: "
-// "%p.\n", knot_rdata_get_item(rdata, j)->dname);
rc = knot_dname_table_add_dname_check(table,
&knot_rdata_get_item(rdata, j)->dname);
-// printf("Returned: %d\n", rc);
if (rc < 0) {
dbg_zone("Error: %s\n", knot_strerror(rc));
return rc;
@@ -171,7 +170,7 @@ static int knot_zone_contents_dnames_from_rdata_to_table(
}
}
- dbg_zone("RDATA OK.\n");
+ dbg_zone_detail("RDATA OK.\n");
return KNOT_EOK;
}
@@ -187,7 +186,7 @@ static int knot_zone_contents_dnames_from_rrset_to_table(
// discard the old owner and replace it with the new
knot_rrset_set_owner(rrset, owner);
}
- dbg_zone("RRSet owner: %p\n", rrset->owner);
+ dbg_zone_detail("RRSet owner: %p\n", rrset->owner);
knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(
knot_rrset_type(rrset));
@@ -197,10 +196,6 @@ static int knot_zone_contents_dnames_from_rrset_to_table(
return KNOT_EOK;
}
// for each RDATA in RRSet
-// char *name = knot_dname_to_str(rrset->owner);
-// char *type = knot_rrtype_to_string(rrset->type);
-// printf("Storing dnames from RDATA from RRSet %s, %s\n", name, type);
-// free(name);
knot_rdata_t *rdata = knot_rrset_get_rdata(rrset);
while (rdata != NULL) {
int rc = knot_zone_contents_dnames_from_rdata_to_table(table,
@@ -227,10 +222,10 @@ static int knot_zone_contents_dnames_from_node_to_table(
// insert owner
char *name = knot_dname_to_str(node->owner);
- dbg_zone("Node owner before inserting to dname table: %p.\n",
- node->owner);
- dbg_zone("Node owner before inserting to dname table: %s.\n",
- name);
+ dbg_zone_detail("Node owner before inserting to dname table: %p.\n",
+ node->owner);
+ dbg_zone_detail("Node owner before inserting to dname table: %s.\n",
+ name);
free(name);
//knot_dname_t *old_owner = node->owner;
int rc = knot_dname_table_add_dname_check(table, &node->owner);
@@ -239,17 +234,18 @@ static int knot_zone_contents_dnames_from_node_to_table(
return rc;
}
int replace_owner = (rc > 0);
- dbg_zone("Node owner after inserting to dname table: %p.\n",
- node->owner);
+
+dbg_zone_exec_detail(
name = knot_dname_to_str(node->owner);
- dbg_zone("Node owner after inserting to dname table: %s.\n",
- name);
+ dbg_zone_detail("Node owner after inserting to dname table: %p (%s).\n",
+ node->owner, name);
free(name);
+);
knot_rrset_t **rrsets = knot_node_get_rrsets(node);
// for each RRSet
for (int i = 0; i < knot_node_rrset_count(node); ++i) {
- dbg_zone("Inserting RRSets from node to table.\n");
+ dbg_zone_detail("Inserting RRSets from node to table.\n");
rc = knot_zone_contents_dnames_from_rrset_to_table(table,
rrsets[i], replace_owner, node->owner);
if (rc != KNOT_EOK) {
@@ -264,27 +260,46 @@ static int knot_zone_contents_dnames_from_node_to_table(
}
/*----------------------------------------------------------------------------*/
-/*!
- * \brief Finds and sets wildcard child for given node's owner.
- *
- * \param zone Current zone.
- * \param node Node to be used.
- */
-//static void find_and_set_wildcard_child(knot_zone_contents_t *zone,
-// knot_node_t *node)
-//{
-// knot_dname_t *chopped = knot_dname_left_chop(node->owner);
-// assert(chopped);
-// knot_node_t *wildcard_parent;
-// wildcard_parent =
-// knot_zone_contents_get_node(zone, chopped);
-// knot_dname_free(&chopped);
+static const knot_node_t *knot_zone_contents_find_wildcard_child(
+ knot_zone_contents_t *zone, const knot_node_t *closest_encloser)
+{
+ assert(zone != NULL);
+ assert(closest_encloser != NULL);
+
+ knot_dname_t *tmp = knot_dname_new_from_str("*", 1, NULL);
+ CHECK_ALLOC(tmp, NULL);
-// assert(wildcard_parent); /* it *has* to be there */
+ knot_dname_t *wildcard = knot_dname_cat(tmp, knot_node_owner(
+ closest_encloser));
+ if (wildcard == NULL) {
+ free(tmp);
+ return NULL;
+ }
-// knot_node_set_wildcard_child(wildcard_parent, node);
-//}
+ assert(wildcard == tmp);
+
+dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(knot_node_owner(closest_encloser));
+ char *name2 = knot_dname_to_str(wildcard);
+ dbg_zone_detail("Searching for wildcard child of %s (%s)\n", name,
+ name2);
+ free(name);
+ free(name2);
+);
+
+ const knot_node_t *found = NULL, *ce = NULL, *prev = NULL;
+ int ret = knot_zone_contents_find_dname(zone, wildcard, &found, &ce,
+ &prev);
+
+ knot_dname_free(&wildcard);
+
+ if (ret != KNOT_ZONE_NAME_FOUND) {
+ return NULL;
+ } else {
+ return found;
+ }
+}
/*----------------------------------------------------------------------------*/
/*!
@@ -303,54 +318,79 @@ static int knot_zone_contents_dnames_from_node_to_table(
* \param zone Zone to which the RDATA belongs.
* \param pos Position of the RDATA item in the RDATA.
*/
-//static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata,
-// knot_zone_contents_t *zone,
-// knot_node_t *node,
-// int pos)
-//{
-// return;
-// const knot_rdata_item_t *dname_item
-// = knot_rdata_item(rdata, pos);
-
-// assert(dname_item);
-
-// if (dname_item != NULL) {
-// knot_dname_t *dname = dname_item->dname;
-// const knot_node_t *n = NULL;
-// const knot_node_t *closest_encloser = NULL;
-// const knot_node_t *prev = NULL;
-
-// if (knot_dname_is_wildcard(dname)) {
-// find_and_set_wildcard_child(zone, node);
-// }
-
-// int ret = knot_zone_contents_find_dname(zone, dname, &n,
-// &closest_encloser, &prev);
-
-// // n = knot_zone_find_node(zone, dname);
-
-// if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) {
-// // TODO: do some cleanup if needed
-// return;
-// }
-
-// assert(ret != KNOT_ZONE_NAME_FOUND
-// || n == closest_encloser);
-
-// if (ret != KNOT_ZONE_NAME_FOUND
-// && (closest_encloser != NULL)) {
-// dbg_zone("Saving closest encloser to RDATA.\n");
-// // save pointer to the closest encloser
-// knot_rdata_item_t *item =
-// knot_rdata_get_item(rdata, pos);
-// assert(item->dname != NULL);
-// assert(item->dname->node == NULL);
-// //skip_insert(list, (void *)item->dname,
-// // (void *)closest_encloser->owner, NULL);
-// item->dname->node = closest_encloser->owner->node;
-// }
-// }
-//}
+static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata,
+ knot_zone_contents_t *zone,
+ knot_node_t *node, int pos)
+{
+ const knot_rdata_item_t *dname_item = knot_rdata_item(rdata, pos);
+
+ assert(dname_item);
+
+ if (dname_item != NULL) {
+ knot_dname_t *dname = dname_item->dname;
+
+ /*
+ * The case when dname.node is already set is handled here.
+ * No use to check it later.
+ */
+ if (knot_dname_node(dname) != NULL
+ || !knot_dname_is_subdomain(dname, knot_node_owner(
+ knot_zone_contents_apex(zone)))) {
+ // The name's node is either already set
+ // or the name does not belong to the zone
+ dbg_zone_detail("Name's node either set or the name "
+ "does not belong to the zone (%p).\n",
+ knot_dname_node(dname));
+ return;
+ }
+
+ const knot_node_t *n = NULL;
+ const knot_node_t *closest_encloser = NULL;
+ const knot_node_t *prev = NULL;
+
+ int ret = knot_zone_contents_find_dname(zone, dname, &n,
+ &closest_encloser, &prev);
+
+ if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) {
+ // TODO: do some cleanup if needed
+ dbg_zone_detail("Failed to find the name in zone: %s\n",
+ knot_strerror(ret));
+ return;
+ }
+
+ assert(ret != KNOT_ZONE_NAME_FOUND || n == closest_encloser);
+
+ if (ret != KNOT_ZONE_NAME_FOUND && (closest_encloser != NULL)) {
+ /*!
+ * \note There is no need to set closer encloser to the
+ * name. We may find the possible wildcard child
+ * right away.
+ * Having the closest encloser saved in the dname
+ * would disrupt the query processing algorithms
+ * anyway.
+ */
+
+ dbg_zone_verb("Trying to find wildcard child.\n");
+
+ n = knot_zone_contents_find_wildcard_child(zone,
+ closest_encloser);
+
+ if (n != NULL) {
+ knot_dname_set_node(dname, (knot_node_t *)n);
+ dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(
+ knot_node_owner(n));
+ char *name2 = knot_dname_to_str(dname);
+ dbg_zone_detail("Set wildcard node %s "
+ "to RDATA dname %s.\n",
+ name, name2);
+ free(name);
+ free(name2);
+ );
+ }
+ }
+ }
+}
/*----------------------------------------------------------------------------*/
/*!
@@ -364,65 +404,64 @@ static int knot_zone_contents_dnames_from_node_to_table(
* \param rrset RRSet to adjust RDATA in.
* \param zone Zone to which the RRSet belongs.
*/
-//static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
-// knot_zone_contents_t *zone,
-// knot_node_t *node)
-//{
-// uint16_t type = knot_rrset_type(rrset);
+static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
+ knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ uint16_t type = knot_rrset_type(rrset);
-// knot_rrtype_descriptor_t *desc =
-// knot_rrtype_descriptor_by_type(type);
-// assert(desc);
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc);
-// knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset);
-// knot_rdata_t *rdata = rdata_first;
+ knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset);
+ knot_rdata_t *rdata = rdata_first;
-// if (rdata == NULL) {
-// return;
-// }
+ if (rdata == NULL) {
+ return;
+ }
-// while (rdata->next != rdata_first) {
-// for (int i = 0; i < rdata->count; ++i) {
-// if (desc->wireformat[i]
-// == KNOT_RDATA_WF_COMPRESSED_DNAME
-// || desc->wireformat[i]
-// == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
-// || desc->wireformat[i]
-// == KNOT_RDATA_WF_LITERAL_DNAME) {
-// dbg_zone("Adjusting domain name at "
-// "position %d of RDATA of record with owner "
-// "%s and type %s.\n",
-// i, rrset->owner->name,
-// knot_rrtype_to_string(type));
-
-// knot_zone_contents_adjust_rdata_item(rdata,
-// zone,
-// node,
-// i);
-// }
-// }
-// rdata = rdata->next;
-// }
+ while (rdata->next != rdata_first) {
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata,
+ zone, node,
+ i);
+ }
+ }
+ rdata = rdata->next;
+ }
-// for (int i = 0; i < rdata->count; ++i) {
-// if (desc->wireformat[i]
-// == KNOT_RDATA_WF_COMPRESSED_DNAME
-// || desc->wireformat[i]
-// == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
-// || desc->wireformat[i]
-// == KNOT_RDATA_WF_LITERAL_DNAME) {
-// dbg_zone("Adjusting domain name at "
-// "position %d of RDATA of record with owner "
-// "%s and type %s.\n",
-// i, rrset->owner->name,
-// knot_rrtype_to_string(type));
-
-// knot_zone_contents_adjust_rdata_item(rdata, zone,
-// node, i);
-// }
-// }
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata, zone, node,
+ i);
+ }
+ }
-//}
+}
/*----------------------------------------------------------------------------*/
/*!
@@ -435,32 +474,29 @@ static int knot_zone_contents_dnames_from_node_to_table(
* \param node Zone node to adjust the RRSets in.
* \param zone Zone to which the node belongs.
*/
-//static void knot_zone_contents_adjust_rrsets(knot_node_t *node,
-// knot_zone_contents_t *zone)
-//{
-// //return;
-// knot_rrset_t **rrsets = knot_node_get_rrsets(node);
-// short count = knot_node_rrset_count(node);
-
-// assert(count == 0 || rrsets != NULL);
-
-// for (int r = 0; r < count; ++r) {
-// assert(rrsets[r] != NULL);
-// dbg_zone("Adjusting next RRSet.\n");
-// knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
-// node);
-// knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
-// if (rrsigs != NULL) {
-// dbg_zone("Adjusting next RRSIGs.\n");
-// knot_zone_contents_adjust_rdata_in_rrset(rrsigs,
-// zone,
-// node);
-// }
-// }
-
-// free(rrsets);
-//}
+static void knot_zone_contents_adjust_rrsets(knot_node_t *node,
+ knot_zone_contents_t *zone)
+{
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ short count = knot_node_rrset_count(node);
+
+ assert(count == 0 || rrsets != NULL);
+
+ for (int r = 0; r < count; ++r) {
+ assert(rrsets[r] != NULL);
+ dbg_zone("Adjusting next RRSet.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
+ node);
+ knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
+ if (rrsigs != NULL) {
+ dbg_zone("Adjusting next RRSIGs.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsigs, zone,
+ node);
+ }
+ }
+ free(rrsets);
+}
/*----------------------------------------------------------------------------*/
/*!
* \brief Adjusts zone node for faster query processing.
@@ -481,35 +517,24 @@ static int knot_zone_contents_dnames_from_node_to_table(
static void knot_zone_contents_adjust_node(knot_node_t *node,
knot_zone_contents_t *zone)
{
-
-dbg_zone_exec(
- char *name = knot_dname_to_str(node->owner);
- dbg_zone("----- Adjusting node %s -----\n", name);
- free(name);
-);
-
// adjust domain names in RDATA
- /*!
- * \note This is unnecessary, as the code in adjust_rdata_item() is not
- * reachable anyway. However, it's not clear why we disabled the
- * code, so this would need further investigation.
- */
- //knot_zone_contents_adjust_rrsets(node, zone);
+ /*! \note Enabled again after a LONG time. Should test thoroughly. */
+ knot_zone_contents_adjust_rrsets(node, zone);
-dbg_zone_exec(
+dbg_zone_exec_detail(
if (knot_node_parent(node)) {
char *name = knot_dname_to_str(knot_node_owner(
knot_node_parent(node)));
- dbg_zone("Parent: %s\n", name);
- dbg_zone("Parent is delegation point: %s\n",
+ dbg_zone_detail("Parent: %s\n", name);
+ dbg_zone_detail("Parent is delegation point: %s\n",
knot_node_is_deleg_point(knot_node_parent(node))
? "yes" : "no");
- dbg_zone("Parent is non-authoritative: %s\n",
+ dbg_zone_detail("Parent is non-authoritative: %s\n",
knot_node_is_non_auth(knot_node_parent(node))
? "yes" : "no");
free(name);
} else {
- dbg_zone("No parent!\n");
+ dbg_zone_detail("No parent!\n");
}
);
// delegation point / non-authoritative node
@@ -539,17 +564,19 @@ dbg_zone_exec(
int match = knot_zone_contents_find_nsec3_for_name(zone,
knot_node_owner(node),
&nsec3, &prev);
+ UNUSED(prev);
+
if (match != KNOT_ZONE_NAME_FOUND) {
nsec3 = NULL;
}
knot_node_set_nsec3_node(node, (knot_node_t *)nsec3);
- dbg_zone("Set flags to the node: \n");
- dbg_zone("Delegation point: %s\n",
- knot_node_is_deleg_point(node) ? "yes" : "no");
- dbg_zone("Non-authoritative: %s\n",
- knot_node_is_non_auth(node) ? "yes" : "no");
+ dbg_zone_detail("Set flags to the node: \n");
+ dbg_zone_detail("Delegation point: %s\n",
+ knot_node_is_deleg_point(node) ? "yes" : "no");
+ dbg_zone_detail("Non-authoritative: %s\n",
+ knot_node_is_non_auth(node) ? "yes" : "no");
}
/*----------------------------------------------------------------------------*/
@@ -578,19 +605,16 @@ static void knot_zone_contents_adjust_node_in_tree(
return;
}
- /*
- * 1) Set previous node pointer.
- */
- knot_node_set_previous(node, args->previous_node);
+dbg_zone_exec_verb(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_zone_verb("----- Adjusting node %s -----\n", name);
+ free(name);
+);
- if (args->first_node == NULL) {
- args->first_node = node;
- }
knot_zone_contents_t *zone = args->zone;
/*
- * 2) Store domain names to dname table.
- *
+ * 1) Store domain names to dname table.
* TODO: make optional!
*/
assert(zone->dname_table != NULL);
@@ -605,13 +629,35 @@ static void knot_zone_contents_adjust_node_in_tree(
}
/*
- * 3) Do other adjusting (flags, closest enclosers, wildcard children,
+ * 2) Do other adjusting (flags, closest enclosers, wildcard children,
* etc.).
*/
knot_zone_contents_adjust_node(node, zone);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_adjust_node_in_tree_ptr(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
/*
- * 4) Store previous node depending on the type of this node.
+ * 1) Set previous node pointer.
+ */
+ knot_node_set_previous(node, args->previous_node);
+
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+
+ /*
+ * 2) Store previous node depending on the type of this node.
*/
if (!knot_node_is_non_auth(node)
&& knot_node_rrset_count(node) > 0) {
@@ -639,8 +685,11 @@ static void knot_zone_contents_adjust_nsec3_node_in_tree(
knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
knot_node_t *node = tnode->node;
- // set previous node
- knot_node_set_previous(node, args->previous_node);
+ if (args->err != KNOT_EOK) {
+ dbg_xfrin_detail("Error during adjusting: %s, skipping node.\n",
+ knot_strerror(args->err));
+ return;
+ }
// assure that owner has proper node
if (knot_dname_node(knot_node_owner(node)) == NULL) {
@@ -661,6 +710,22 @@ static void knot_zone_contents_adjust_nsec3_node_in_tree(
args->err = ret;
return;
}
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_adjust_nsec3_node_in_tree_ptr(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
+
+ // set previous node
+ knot_node_set_previous(node, args->previous_node);
// here is nothing to consider, all nodes are the same
args->previous_node = node;
@@ -710,9 +775,9 @@ dbg_zone_exec(
uint8_t *hashed_name = NULL;
size_t hash_size = 0;
-dbg_zone_exec(
+dbg_zone_exec_verb(
char *n = knot_dname_to_str(name);
- dbg_zone("Hashing name %s.\n", n);
+ dbg_zone_verb("Hashing name %s.\n", n);
free(n);
);
@@ -737,8 +802,7 @@ dbg_zone_exec(
if (size == 0) {
char *n = knot_dname_to_str(name);
- dbg_zone("Error while encoding hashed name %s to "
- "base32.\n", n);
+ dbg_zone("Error while encoding hashed name %s to base32.\n", n);
free(n);
if (name_b32 != NULL) {
free(name_b32);
@@ -749,7 +813,7 @@ dbg_zone_exec(
assert(name_b32 != NULL);
free(hashed_name);
- dbg_zone("Base32-encoded hash: %s\n", name_b32);
+ dbg_zone_verb("Base32-encoded hash: %s\n", name_b32);
/* Will be returned to caller, make sure it is released after use. */
*nsec3_name = knot_dname_new_from_str(name_b32, size, NULL);
@@ -757,8 +821,7 @@ dbg_zone_exec(
free(name_b32);
if (*nsec3_name == NULL) {
- dbg_zone("Error while creating domain name for hashed"
- " name.\n");
+ dbg_zone("Error while creating domain name for hashed name.\n");
return KNOT_ERROR;
}
@@ -767,7 +830,7 @@ dbg_zone_exec(
if (ret == NULL) {
dbg_zone("Error while creating NSEC3 domain name for "
- "hashed name.\n");
+ "hashed name.\n");
knot_dname_release(*nsec3_name);
return KNOT_ERROR;
}
@@ -805,31 +868,14 @@ static int knot_zone_contents_find_in_tree(knot_zone_tree_t *tree,
assert(previous != NULL);
knot_node_t *found = NULL, *prev = NULL;
-// knot_node_t *found2 = NULL, *prev2 = NULL;
int exact_match = knot_zone_tree_get_less_or_equal(tree, name, &found,
&prev);
-// assert(prev != NULL);
assert(exact_match >= 0);
*node = found;
*previous = prev;
-// if (prev == NULL) {
-// // either the returned node is the root of the tree, or it is
-// // the leftmost node in the tree; in both cases node was found
-// // set the previous node of the found node
-// assert(exact_match);
-// assert(found != NULL);
-// *previous = knot_node_get_previous(found, 1);
-// } else {
-// // otherwise check if the previous node is not an empty
-// // non-terminal
-// *previous = (knot_node_rrset_count(prev) == 0)
-// ? knot_node_get_previous(prev, 1)
-// : prev;
-// }
-
return exact_match;
}
@@ -852,7 +898,6 @@ static void knot_zone_contents_node_to_hash(knot_zone_tree_node_t *tnode,
*/
#ifdef USE_HASH_TABLE
- //assert(zone->table != NULL);
// add the node also to the hash table if authoritative, or deleg. point
if (zone->table != NULL
&& ck_insert_item(zone->table,
@@ -977,23 +1022,6 @@ static void knot_zone_contents_check_loops_in_tree(knot_zone_tree_node_t *tnode,
args->zone, next_name,
&next_node, &ce);
-// char *name1 = knot_dname_to_str(next_name);
-// char *name2 = (next_node != NULL)
-// ? knot_dname_to_str(knot_node_owner(next_node))
-// : "none";
-// char *name3 = (ce != NULL)
-// ? knot_dname_to_str(knot_node_owner(ce))
-// : "none";
-// printf("Searched: %s, found: %p (%s), %p (%s); ret: %d"
-// ".\n", name1, next_node, name2, ce, name3, ret);
-// free(name1);
-// if (next_node != NULL) {
-// free(name2);
-// }
-// if (ce != NULL) {
-// free(name3);
-// }
-
if (ret != KNOT_ZONE_NAME_FOUND
&& ce != NULL) {
// try to find wildcard child
@@ -1025,6 +1053,32 @@ static void knot_zone_contents_check_loops_in_tree(knot_zone_tree_node_t *tnode,
}
/*----------------------------------------------------------------------------*/
+
+static int knot_zc_nsec3_parameters_match(const knot_rdata_t *rdata,
+ const knot_nsec3_params_t *params)
+{
+ assert(rdata != NULL && params != NULL);
+
+ dbg_zone_detail("RDATA algo: %u, iterations: %u, salt length: %u, salt:"
+ " %.*s\n",
+ knot_rdata_nsec3_algorithm(rdata),
+ knot_rdata_nsec3_iterations(rdata),
+ knot_rdata_nsec3_salt_length(rdata),
+ knot_rdata_nsec3_salt_length(rdata),
+ knot_rdata_nsec3_salt(rdata));
+ dbg_zone_detail("NSEC3PARAM algo: %u, iterations: %u, salt length: %u, "
+ "salt: %.*s\n", params->algorithm, params->iterations,
+ params->salt_length, params->salt_length, params->salt);
+
+ return (knot_rdata_nsec3_algorithm(rdata) == params->algorithm
+ && knot_rdata_nsec3_iterations(rdata) == params->iterations
+ && knot_rdata_nsec3_salt_length(rdata) == params->salt_length
+ && strncmp((const char *)knot_rdata_nsec3_salt(rdata),
+ (const char *)params->salt, params->salt_length)
+ == 0);
+}
+
+/*----------------------------------------------------------------------------*/
/* API functions */
/*----------------------------------------------------------------------------*/
@@ -1041,22 +1095,19 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
return NULL;
}
-// printf("created cont: %p (%s)\n",
-// contents, knot_dname_to_str(apex->owner));
-
contents->apex = apex;
contents->zone = zone;
- knot_node_set_zone(apex, zone);
+ knot_node_set_zone(apex, contents->zone);
contents->node_count = 1;
- dbg_zone("Creating tree for normal nodes.\n");
+ dbg_zone_verb("Creating tree for normal nodes.\n");
contents->nodes = malloc(sizeof(knot_zone_tree_t));
if (contents->nodes == NULL) {
ERR_ALLOC_FAILED;
goto cleanup;
}
- dbg_zone("Creating tree for NSEC3 nodes.\n");
+ dbg_zone_verb("Creating tree for NSEC3 nodes.\n");
contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t));
if (contents->nsec3_nodes == NULL) {
ERR_ALLOC_FAILED;
@@ -1064,7 +1115,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
}
if (use_domain_table) {
- dbg_zone("Creating domain name table.\n");
+ dbg_zone_verb("Creating domain name table.\n");
contents->dname_table = knot_dname_table_new();
if (contents->dname_table == NULL) {
ERR_ALLOC_FAILED;
@@ -1077,20 +1128,20 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
//contents->node_count = node_count;
/* Initialize NSEC3 params */
- dbg_zone("Initializing NSEC3 parameters.\n");
+ dbg_zone_verb("Initializing NSEC3 parameters.\n");
contents->nsec3_params.algorithm = 0;
contents->nsec3_params.flags = 0;
contents->nsec3_params.iterations = 0;
contents->nsec3_params.salt_length = 0;
contents->nsec3_params.salt = NULL;
- dbg_zone("Initializing zone trees.\n");
+ dbg_zone_verb("Initializing zone trees.\n");
if (knot_zone_tree_init(contents->nodes) != KNOT_EOK
|| knot_zone_tree_init(contents->nsec3_nodes) != KNOT_EOK) {
goto cleanup;
}
- dbg_zone("Inserting apex into the zone tree.\n");
+ dbg_zone_verb("Inserting apex into the zone tree.\n");
if (knot_zone_tree_insert(contents->nodes, apex) != KNOT_EOK) {
dbg_zone("Failed to insert apex to the zone tree.\n");
goto cleanup;
@@ -1098,14 +1149,14 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
#ifdef USE_HASH_TABLE
if (node_count > 0) {
- dbg_zone("Creating hash table.\n");
+ dbg_zone_verb("Creating hash table.\n");
contents->table = ck_create_table(node_count);
if (contents->table == NULL) {
goto cleanup;
}
// insert the apex into the hash table
- dbg_zone("Inserting apex into the hash table.\n");
+ dbg_zone_verb("Inserting apex into the hash table.\n");
if (ck_insert_item(contents->table,
(const char *)knot_dname_name(
knot_node_owner(apex)),
@@ -1121,7 +1172,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
// insert names from the apex to the domain table
if (use_domain_table) {
- dbg_zone("Inserting names from apex to table.\n");
+ dbg_zone_verb("Inserting names from apex to table.\n");
int rc = knot_zone_contents_dnames_from_node_to_table(
contents->dname_table, apex);
if (rc != KNOT_EOK) {
@@ -1133,7 +1184,7 @@ knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
return contents;
cleanup:
- dbg_zone("Cleaning up.\n");
+ dbg_zone_verb("Cleaning up.\n");
free(contents->dname_table);
free(contents->nodes);
free(contents->nsec3_nodes);
@@ -1234,6 +1285,12 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
return KNOT_EBADARG;
}
+dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(knot_node_owner(node));
+ dbg_zone_detail("Adding node to zone: %s.\n", name);
+ free(name);
+);
+
int ret = 0;
if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
dbg_zone("Node check failed.\n");
@@ -1257,10 +1314,6 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
}
#ifdef USE_HASH_TABLE
-// char *name = knot_dname_to_str(node->owner);
-// dbg_zone("Adding node with owner %s to hash table.\n", name);
-// free(name);
- //assert(zone->table != NULL);
// add the node also to the hash table if authoritative, or deleg. point
if (zone->table != NULL
&& ck_insert_item(zone->table,
@@ -1281,7 +1334,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
return KNOT_EOK;
}
- dbg_zone("Creating parents of the node.\n");
+ dbg_zone_detail("Creating parents of the node.\n");
knot_dname_t *chopped =
knot_dname_left_chop(knot_node_owner(node));
@@ -1293,7 +1346,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
}
if (knot_dname_compare(knot_node_owner(zone->apex), chopped) == 0) {
- dbg_zone("Zone apex is the parent.\n");
+ dbg_zone_detail("Zone apex is the parent.\n");
knot_node_set_parent(node, zone->apex);
// check if the node is not wildcard child of the parent
@@ -1307,7 +1360,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
= knot_zone_contents_get_node(zone, chopped)) == NULL &&
chopped != NULL) {
/* Adding new dname to zone + add to table. */
- dbg_zone("Creating new node.\n");
+ dbg_zone_detail("Creating new node.\n");
assert(chopped);
next_node = knot_node_new(chopped, NULL, flags);
@@ -1322,7 +1375,7 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
knot_zone_contents_dnames_from_node_to_table(
zone->dname_table, next_node);
if (ret != KNOT_EOK) {
- knot_node_free(&next_node, 0);
+ knot_node_free(&next_node);
knot_dname_release(chopped);
return ret;
}
@@ -1339,14 +1392,13 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
== NULL);
assert(knot_node_owner(next_node) == chopped);
- dbg_zone("Inserting new node to zone tree.\n");
-// TREE_INSERT(zone->tree, knot_node, avl, next_node);
+ dbg_zone_detail("Inserting new node to zone tree.\n");
ret = knot_zone_tree_insert(zone->nodes,
next_node);
if (ret != KNOT_EOK) {
dbg_zone("Failed to insert new node "
- "to zone tree.\n");
+ "to zone tree.\n");
/*! \todo Delete the node?? */
/* Directly discard. */
knot_dname_release(chopped);
@@ -1354,11 +1406,11 @@ int knot_zone_contents_add_node(knot_zone_contents_t *zone,
}
#ifdef USE_HASH_TABLE
-dbg_zone_exec(
+dbg_zone_exec_detail(
char *name = knot_dname_to_str(
knot_node_owner(next_node));
- dbg_zone("Adding new node with owner %s to "
- "hash table.\n", name);
+ dbg_zone_detail("Adding new node with owner %s to "
+ "hash table.\n", name);
free(name);
);
@@ -1369,7 +1421,7 @@ dbg_zone_exec(
knot_dname_size(knot_node_owner(next_node)),
(void *)next_node) != 0) {
dbg_zone("Error inserting node into "
- "hash table!\n");
+ "hash table!\n");
/*! \todo Delete the node?? */
/* Directly discard. */
knot_dname_release(chopped);
@@ -1390,7 +1442,7 @@ dbg_zone_exec(
++zone->node_count;
- dbg_zone("Next parent.\n");
+ dbg_zone_detail("Next parent.\n");
node = next_node;
knot_dname_t *chopped_last = chopped;
chopped = knot_dname_left_chop(chopped);
@@ -1406,7 +1458,7 @@ dbg_zone_exec(
assert(knot_node_parent(node) == NULL);
knot_node_set_parent(node, next_node);
- dbg_zone("Created all parents.\n");
+ dbg_zone_detail("Created all parents.\n");
}
/* Directly discard. */
@@ -1419,15 +1471,22 @@ dbg_zone_exec(
/*----------------------------------------------------------------------------*/
int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
- knot_rrset_t *rrset, knot_node_t **node,
- knot_rrset_dupl_handling_t dupl,
- int use_domain_table)
+ knot_rrset_t *rrset, knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
{
if (zone == NULL || rrset == NULL || zone->apex == NULL
|| zone->apex->owner == NULL || node == NULL) {
return KNOT_EBADARG;
}
+dbg_zone_exec_detail(
+ char *name = knot_dname_to_str(knot_rrset_owner(rrset));
+ dbg_zone_detail("Adding RRSet to zone contents: %s, type %s\n",
+ name, knot_rrtype_to_string(knot_rrset_type(rrset)));
+ free(name);
+);
+
// check if the RRSet belongs to the zone
if (knot_dname_compare(knot_rrset_owner(rrset),
zone->apex->owner) != 0
@@ -1448,8 +1507,12 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
int rc;
/*! \todo REMOVE RRSET */
- rc = knot_node_add_rrset(*node, rrset,
- dupl == KNOT_RRSET_DUPL_MERGE);
+ if (dupl == KNOT_RRSET_DUPL_MERGE) {
+ rc = knot_node_add_rrset_no_dupl(*node, rrset);
+ } else {
+ rc = knot_node_add_rrset(*node, rrset, 0);
+ }
+
if (rc < 0) {
dbg_zone("Failed to add RRSet to node.\n");
return rc;
@@ -1458,14 +1521,13 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
int ret = rc;
if (use_domain_table) {
- dbg_zone("Saving RRSet to table.\n");
+ dbg_zone_detail("Saving RRSet to table.\n");
rc = knot_zone_contents_dnames_from_rrset_to_table(
zone->dname_table, rrset, 0, (*node)->owner);
if (rc != KNOT_EOK) {
dbg_zone("Error saving domain names from "
- "RRSIGs to the domain name table.\n "
- "The zone may be in an inconsistent "
- "state.\n");
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent state.\n");
// WARNING: the zone is not in consistent state now -
// there may be domain names in it that are not inserted
// into the domain table
@@ -1480,7 +1542,7 @@ int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
knot_rrset_set_owner(rrset, (*node)->owner);
}
- dbg_zone("RRSet OK.\n");
+ dbg_zone_detail("RRSet OK.\n");
return ret;
}
@@ -1493,11 +1555,13 @@ int knot_zone_contents_add_rrsigs(knot_zone_contents_t *zone,
knot_rrset_dupl_handling_t dupl,
int use_domain_table)
{
+ dbg_zone_verb("Adding RRSIGs to zone contents.\n");
+
if (zone == NULL || rrsigs == NULL || rrset == NULL || node == NULL
|| zone->apex == NULL || zone->apex->owner == NULL) {
dbg_zone_exec(
dbg_zone("Parameters: zone=%p, rrsigs=%p, rrset=%p, "
- "node=%p\n", zone, rrsigs, rrset, node);
+ "node=%p\n", zone, rrsigs, rrset, node);
if (zone != NULL) {
dbg_zone("zone->apex=%p\n", zone->apex);
if (zone->apex != NULL) {
@@ -1522,7 +1586,7 @@ dbg_zone_exec(
if (*rrset != NULL
&& (knot_dname_compare(knot_rrset_owner(rrsigs),
knot_rrset_owner(*rrset)) != 0)) {
- dbg_zone("RRSIGs does not belong to the given RRSet.\n");
+ dbg_zone("RRSIGs do not belong to the given RRSet.\n");
return KNOT_EBADARG;
}
@@ -1548,8 +1612,8 @@ dbg_zone_exec(
// find the RRSet in the node
// take only the first RDATA from the RRSIGs
- dbg_zone("Finding RRSet for type %s\n",
- knot_rrtype_to_string(
+ dbg_zone_detail("Finding RRSet for type %s\n",
+ knot_rrtype_to_string(
knot_rdata_rrsig_type_covered(
knot_rrset_rdata(rrsigs))));
*rrset = knot_node_get_rrset(
@@ -1563,28 +1627,28 @@ dbg_zone_exec(
assert(*rrset != NULL);
- // add all domain names from the RRSet to domain name table
int rc;
int ret = KNOT_EOK;
rc = knot_rrset_add_rrsigs(*rrset, rrsigs, dupl);
if (rc < 0) {
- dbg_dname("Failed to add RRSIGs to RRSet.\n");
+ dbg_zone("Failed to add RRSIGs to RRSet.\n");
return rc;
} else if (rc > 0) {
- assert(dupl == KNOT_RRSET_DUPL_MERGE);
+ assert(dupl == KNOT_RRSET_DUPL_MERGE ||
+ dupl == KNOT_RRSET_DUPL_SKIP);
ret = 1;
}
+ // add all domain names from the RRSet to domain name table
if (use_domain_table) {
- dbg_zone("Saving RRSIG RRSet to table.\n");
+ dbg_zone_detail("Saving RRSIG RRSet to table.\n");
rc = knot_zone_contents_dnames_from_rrset_to_table(
- zone->dname_table, rrsigs, 0, (*rrset)->owner);
+ zone->dname_table, (*rrset)->rrsigs, 0, (*rrset)->owner);
if (rc != KNOT_EOK) {
dbg_zone("Error saving domain names from "
- "RRSIGs to the domain name table.\n "
- "The zone may be in an inconsistent "
- "state.\n");
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent state.\n");
// WARNING: the zone is not in consistent state now -
// there may be domain names in it that are not inserted
// into the domain table
@@ -1598,7 +1662,7 @@ dbg_zone_exec(
knot_rrset_set_owner((*rrset)->rrsigs, (*rrset)->owner);
}
- dbg_zone("RRSIGs OK\n");
+ dbg_zone_detail("RRSIGs OK\n");
return ret;
}
@@ -1617,6 +1681,7 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
int ret = 0;
if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
+ dbg_zone("Failed node check: %s\n", knot_strerror(ret));
return ret;
}
@@ -1624,6 +1689,8 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
// TREE_INSERT(zone->nsec3_nodes, knot_node, avl, node);
ret = knot_zone_tree_insert(zone->nsec3_nodes, node);
if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert node into NSEC3 tree: %s.\n",
+ knot_strerror(ret));
return ret;
}
@@ -1632,7 +1699,8 @@ int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
zone->dname_table, node);
if (ret != KNOT_EOK) {
/*! \todo Remove the node from the tree. */
- dbg_zone("Failed to add dnames into table.\n");
+ dbg_zone("Failed to add dnames into table: %s.\n",
+ knot_strerror(ret));
return ret;
}
}
@@ -1682,8 +1750,12 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
int rc;
/*! \todo REMOVE RRSET */
- rc = knot_node_add_rrset(*node, rrset,
- dupl == KNOT_RRSET_DUPL_MERGE);
+ if (dupl == KNOT_RRSET_DUPL_MERGE) {
+ rc = knot_node_add_rrset_no_dupl(*node, rrset);
+ } else {
+ rc = knot_node_add_rrset(*node, rrset, 0);
+ }
+
if (rc < 0) {
return rc;
}
@@ -1691,14 +1763,13 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
int ret = rc;
if (use_domain_table) {
- dbg_zone("Saving NSEC3 RRSet to table.\n");
+ dbg_zone_detail("Saving NSEC3 RRSet to table.\n");
rc = knot_zone_contents_dnames_from_rrset_to_table(
zone->dname_table, rrset, 0, (*node)->owner);
if (rc != KNOT_EOK) {
dbg_zone("Error saving domain names from "
- "RRSIGs to the domain name table.\n "
- "The zone may be in an inconsistent "
- "state.\n");
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent state.\n");
// WARNING: the zone is not in consistent state now -
// there may be domain names in it that are not inserted
// into the domain table
@@ -1713,7 +1784,7 @@ int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
knot_rrset_set_owner(rrset, (*node)->owner);
}
- dbg_zone("NSEC3 OK\n");
+ dbg_zone_detail("NSEC3 OK\n");
return ret;
}
@@ -1740,9 +1811,6 @@ dbg_zone_exec_verb(
*removed_hash = ck_remove_item(contents->table,
(const char *)knot_dname_name(owner),
knot_dname_size(owner));
-// int ret = ck_detete_item(contents->table,
-// (const char *)knot_dname_name(owner),
-// knot_dname_size(owner), NULL, 0);
if (*removed_hash == NULL) {
return KNOT_ENONODE;
}
@@ -1837,11 +1905,6 @@ knot_node_t *knot_zone_contents_get_node(const knot_zone_contents_t *zone,
return NULL;
}
- // create dummy node to use for lookup
-// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
-// knot_node_t *n = TREE_FIND(zone->tree, knot_node, avl, tmp);
-// knot_node_free(&tmp, 0);
-
knot_node_t *n;
int ret = knot_zone_tree_get(zone->nodes, name, &n);
if (ret != KNOT_EOK) {
@@ -1861,10 +1924,6 @@ knot_node_t *knot_zone_contents_get_nsec3_node(
return NULL;
}
- // create dummy node to use for lookup
-// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
-// knot_node_t *n = TREE_FIND(zone->nsec3_nodes, knot_node, avl, tmp);
-// knot_node_free(&tmp, 0);
knot_node_t *n;
int ret = knot_zone_tree_get(zone->nsec3_nodes, name, &n);
@@ -1899,11 +1958,11 @@ int knot_zone_contents_find_dname(const knot_zone_contents_t *zone,
return KNOT_EBADARG;
}
-dbg_zone_exec(
+dbg_zone_exec_verb(
char *name_str = knot_dname_to_str(name);
char *zone_str = knot_dname_to_str(zone->apex->owner);
- dbg_zone("Searching for name %s in zone %s...\n",
- name_str, zone_str);
+ dbg_zone_verb("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
free(name_str);
free(zone_str);
);
@@ -1928,14 +1987,14 @@ dbg_zone_exec(
*node = found;
*previous = prev;
-dbg_zone_exec(
+dbg_zone_exec_detail(
char *name_str = (*node) ? knot_dname_to_str((*node)->owner)
: "(nil)";
char *name_str2 = (*previous != NULL)
? knot_dname_to_str((*previous)->owner)
: "(nil)";
- dbg_zone("Search function returned %d, node %s and prev: %s\n",
- exact_match, name_str, name_str2);
+dbg_zone_detail("Search function returned %d, node %s (%p) and prev: %s (%p)\n",
+ exact_match, name_str, *node, name_str2, *previous);
if (*node) {
free(name_str);
@@ -1944,19 +2003,23 @@ dbg_zone_exec(
free(name_str2);
}
);
-
- *closest_encloser = *node;
-
// there must be at least one node with domain name less or equal to
// the searched name if the name belongs to the zone (the root)
- if (*node == NULL) {
+ if (*node == NULL && *previous == NULL) {
return KNOT_EBADZONE;
}
- // TODO: this could be replaced by saving pointer to closest encloser
- // in node
+ /* This function was quite out of date. The find_in_tree() function
+ * may return NULL in the 'found' field, so we cannot search for the
+ * closest encloser from this node.
+ */
+
+ if (exact_match) {
+ *closest_encloser = *node;
+ } else {
+ *closest_encloser = *previous;
+ assert(*closest_encloser != NULL);
- if (!exact_match) {
int matched_labels = knot_dname_matched_labels(
knot_node_owner((*closest_encloser)), name);
while (matched_labels < knot_dname_label_count(
@@ -1968,11 +2031,11 @@ dbg_zone_exec(
}
dbg_zone_exec(
char *n = knot_dname_to_str(knot_node_owner((*closest_encloser)));
- dbg_zone("Closest encloser: %s\n", n);
+ dbg_zone_detail("Closest encloser: %s\n", n);
free(n);
);
- dbg_zone("find_dname() returning %d\n", exact_match);
+ dbg_zone_verb("find_dname() returning %d\n", exact_match);
return (exact_match)
? KNOT_ZONE_NAME_FOUND
@@ -2055,11 +2118,11 @@ int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *zone,
return KNOT_EBADARG;
}
-dbg_zone_exec(
+dbg_zone_exec_verb(
char *name_str = knot_dname_to_str(name);
char *zone_str = knot_dname_to_str(zone->apex->owner);
- dbg_zone("Searching for name %s in zone %s...\n",
- name_str, zone_str);
+ dbg_zone_verb("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
free(name_str);
free(zone_str);
);
@@ -2092,9 +2155,9 @@ dbg_zone_exec(
*node = (const knot_node_t *)item->value;
*closest_encloser = *node;
- dbg_zone("Found node in hash table: %p (owner %p, "
- "labels: %d)\n", *node, (*node)->owner,
- knot_dname_label_count((*node)->owner));
+ dbg_zone_detail("Found node in hash table: %p (owner %p, "
+ "labels: %d)\n", *node, (*node)->owner,
+ knot_dname_label_count((*node)->owner));
assert(*node != NULL);
assert(*closest_encloser != NULL);
return KNOT_ZONE_NAME_FOUND;
@@ -2104,34 +2167,22 @@ dbg_zone_exec(
// chop leftmost labels until some node is found
// copy the name for chopping
- /* Local allocation, will be discarded. */
- //knot_dname_t *name_copy = knot_dname_deep_copy(name);
-dbg_zone_exec(
- //char *n = knot_dname_to_str(name_copy);
- dbg_zone("Finding closest encloser..\nStarting with: %.*s\n",
- (int)name_size, name_tmp);
- //free(n);
-);
+
+ dbg_zone_detail("Finding closest encloser..\nStarting with: %.*s\n",
+ (int)name_size, name_tmp);
while (item == NULL) {
- //knot_dname_left_chop_no_copy(name_copy);
knot_zone_contents_left_chop(name_tmp, &name_size);
-dbg_zone_exec(
- //char *n = knot_dname_to_str(name_copy);
- dbg_zone("Chopped leftmost label: %.*s\n",
- (int)name_size, name_tmp);
- //free(n);
+dbg_zone_exec_detail(
+ dbg_zone_detail("Chopped leftmost label: %.*s\n",
+ (int)name_size, name_tmp);
);
// not satisfied in root zone!!
- //assert(name_copy->label_count > 0);
assert(name_size > 0);
item = ck_find_item(zone->table, name_tmp, name_size);
}
- /* Directly discard. */
- //knot_dname_free(&name_copy);
-
assert(item != NULL);
*closest_encloser = (const knot_node_t *)item->value;
@@ -2165,9 +2216,16 @@ int knot_zone_contents_find_nsec3_for_name(const knot_zone_contents_t *zone,
return ret;
}
-dbg_zone_exec(
+ // check if the NSEC3 tree is not empty
+ if (zone->nsec3_nodes->th_root == NULL) {
+ dbg_zone("NSEC3 tree is empty.\n");
+ knot_dname_release(nsec3_name);
+ return KNOT_ENSEC3CHAIN;
+ }
+
+dbg_zone_exec_verb(
char *n = knot_dname_to_str(nsec3_name);
- dbg_zone("NSEC3 node name: %s.\n", n);
+ dbg_zone_verb("NSEC3 node name: %s.\n", n);
free(n);
);
@@ -2180,26 +2238,33 @@ dbg_zone_exec(
knot_dname_release(nsec3_name);
-dbg_zone_exec(
+dbg_zone_exec_detail(
if (found) {
char *n = knot_dname_to_str(found->owner);
- dbg_zone("Found NSEC3 node: %s.\n", n);
+ dbg_zone_detail("Found NSEC3 node: %s.\n", n);
free(n);
} else {
- dbg_zone("Found no NSEC3 node.\n");
+ dbg_zone_detail("Found no NSEC3 node.\n");
}
if (prev) {
assert(prev->owner);
char *n = knot_dname_to_str(prev->owner);
- dbg_zone("Found previous NSEC3 node: %s.\n", n);
+ dbg_zone_detail("Found previous NSEC3 node: %s.\n", n);
free(n);
} else {
- dbg_zone("Found no previous NSEC3 node.\n");
+ dbg_zone_detail("Found no previous NSEC3 node.\n");
}
);
*nsec3_node = found;
+ // This check cannot be used now, the function returns proper return
+ // value if the node was not found
+// if (*nsec3_node == NULL) {
+// // there is no NSEC3 node even if there should be
+// return KNOT_ENSEC3CHAIN;
+// }
+
if (prev == NULL) {
// either the returned node is the root of the tree, or it is
// the leftmost node in the tree; in both cases node was found
@@ -2211,7 +2276,55 @@ dbg_zone_exec(
*nsec3_previous = prev;
}
- dbg_zone("find_nsec3_for_name() returning %d\n", exact_match);
+ dbg_zone_verb("find_nsec3_for_name() returning %d\n", exact_match);
+
+ /* The previous may be from wrong NSEC3 chain. Search for previous
+ * from the right chain. Check iterations, hash algorithm and salt
+ * values and compare them to the ones from NSEC3PARAM.
+ */
+ const knot_rrset_t *nsec3_rrset = knot_node_rrset(*nsec3_previous,
+ KNOT_RRTYPE_NSEC3);
+ const knot_rdata_t *nsec3_rdata = (nsec3_rrset != NULL)
+ ? knot_rrset_rdata(nsec3_rrset)
+ : NULL;
+ const knot_node_t *original_prev = *nsec3_previous;
+
+ while (nsec3_rdata != NULL
+ && !knot_zc_nsec3_parameters_match(nsec3_rdata,
+ &zone->nsec3_params)) {
+ /* Try other RDATA if there are some. In case of name collision
+ * the node would contain records from both NSEC3 chains.
+ */
+ if ((nsec3_rdata = knot_rrset_rdata_next(
+ nsec3_rrset, nsec3_rdata)) != NULL) {
+ continue;
+ }
+
+ /* If there is none, try previous node. */
+
+ *nsec3_previous = knot_node_previous(*nsec3_previous);
+ nsec3_rrset = knot_node_rrset(*nsec3_previous,
+ KNOT_RRTYPE_NSEC3);
+ nsec3_rdata = (nsec3_rrset != NULL)
+ ? knot_rrset_rdata(nsec3_rrset)
+ : NULL;
+dbg_zone_exec_detail(
+ char *name = (*nsec3_previous)
+ ? knot_dname_to_str(
+ knot_node_owner(*nsec3_previous))
+ : "none";
+ dbg_zone_detail("Previous node: %s, checking parameters...\n",
+ name);
+ if (*nsec3_previous) {
+ free(name);
+ }
+);
+ if (*nsec3_previous == original_prev || nsec3_rdata == NULL) {
+ // cycle
+ *nsec3_previous = NULL;
+ break;
+ }
+ }
return (exact_match)
? KNOT_ZONE_NAME_FOUND
@@ -2257,23 +2370,18 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone)
adjust_arg.first_node = NULL;
adjust_arg.previous_node = NULL;
adjust_arg.err = KNOT_EOK;
-// adjust_arg.check_ver = check_ver;
/*
- * Adjust the NSEC3 nodes first.
- * There are independent on the normal nodes, but the normal nodes are
- * dependent on them.
+ * First of all we must set node.prev pointers, as these are used in
+ * the search functions.
*/
-
- dbg_zone("Adjusting NSEC3 nodes.\n");
- int ret = knot_zone_tree_forward_apply_inorder(
- zone->nsec3_nodes,
- knot_zone_contents_adjust_nsec3_node_in_tree,
- &adjust_arg);
+ dbg_zone("Setting 'prev' pointers to NSEC3 nodes.\n");
+ int ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes,
+ knot_zone_contents_adjust_nsec3_node_in_tree_ptr, &adjust_arg);
assert(ret == KNOT_EOK);
if (adjust_arg.err != KNOT_EOK) {
- dbg_zone("Failed to adjust NSEC3 nodes: %s\n",
+ dbg_zone("Failed to set 'prev' pointers to NSEC3 nodes: %s\n",
knot_strerror(adjust_arg.err));
return adjust_arg.err;
}
@@ -2281,13 +2389,46 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone)
// set the last node as previous of the first node
if (adjust_arg.first_node) {
knot_node_set_previous(adjust_arg.first_node,
- adjust_arg.previous_node);
+ adjust_arg.previous_node);
}
dbg_zone("Done.\n");
adjust_arg.first_node = NULL;
adjust_arg.previous_node = NULL;
+ dbg_zone("Setting 'prev' pointers to normal nodes.\n");
+ ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_contents_adjust_node_in_tree_ptr, &adjust_arg);
+ assert(ret == KNOT_EOK);
+
+ if (adjust_arg.err != KNOT_EOK) {
+ dbg_zone("Failed to set 'prev' pointers to normal nodes: %s\n",
+ knot_strerror(adjust_arg.err));
+ return adjust_arg.err;
+ }
+
+ // set the last node as previous of the first node
+ assert(zone->apex == adjust_arg.first_node);
+ knot_node_set_previous(zone->apex, adjust_arg.previous_node);
+ dbg_zone("Done.\n");
+
+ /*
+ * Adjust the NSEC3 nodes first.
+ * There are independent on the normal nodes, but the normal nodes are
+ * dependent on them.
+ */
+
+ dbg_zone("Adjusting NSEC3 nodes.\n");
+ ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes,
+ knot_zone_contents_adjust_nsec3_node_in_tree, &adjust_arg);
+ assert(ret == KNOT_EOK);
+
+ if (adjust_arg.err != KNOT_EOK) {
+ dbg_zone("Failed to adjust NSEC3 nodes: %s\n",
+ knot_strerror(adjust_arg.err));
+ return adjust_arg.err;
+ }
+
dbg_zone("Adjusting normal nodes.\n");
ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
knot_zone_contents_adjust_node_in_tree,
@@ -2300,9 +2441,6 @@ int knot_zone_contents_adjust(knot_zone_contents_t *zone)
return adjust_arg.err;
}
- assert(zone->apex == adjust_arg.first_node);
- knot_node_set_previous(zone->apex, adjust_arg.previous_node);
-
dbg_zone("Done.\n");
return ret;
@@ -2339,58 +2477,6 @@ int knot_zone_contents_check_loops(knot_zone_contents_t *zone)
/*----------------------------------------------------------------------------*/
-int knot_zone_contents_adjust_old(knot_zone_contents_t *zone)
-{
- if (zone == NULL) {
- return KNOT_EBADARG;
- }
-
- // load NSEC3PARAM (needed on adjusting function)
- knot_zone_contents_load_nsec3param(zone);
-
- knot_zone_adjust_arg_t adjust_arg;
- adjust_arg.zone = zone;
- adjust_arg.first_node = NULL;
- adjust_arg.previous_node = NULL;
-// adjust_arg.check_ver = check_ver;
- adjust_arg.err = KNOT_EOK;
-
- dbg_zone("Adjusting normal nodes.\n");
- int ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
- knot_zone_contents_adjust_node_in_tree,
- &adjust_arg);
- if (ret != KNOT_EOK) {
- return ret;
- }
- if (adjust_arg.err != KNOT_EOK) {
- dbg_zone("Failed node adjusting: %s\n",
- knot_strerror(adjust_arg.err));
- return adjust_arg.err;
- }
-
- dbg_zone("Done.\n");
-
- assert(zone->apex == adjust_arg.first_node);
- knot_node_set_previous(zone->apex, adjust_arg.previous_node);
-
- adjust_arg.first_node = NULL;
- adjust_arg.previous_node = NULL;
-
- dbg_zone("Adjusting NSEC3 nodes.\n");
- ret = knot_zone_tree_forward_apply_inorder(zone->nsec3_nodes,
- knot_zone_contents_adjust_nsec3_node_in_tree, &adjust_arg);
-
- dbg_zone("Done.\n");
- if (adjust_arg.first_node) {
- knot_node_set_previous(adjust_arg.first_node,
- adjust_arg.previous_node);
- }
-
- return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-
int knot_zone_contents_load_nsec3param(knot_zone_contents_t *zone)
{
if (zone == NULL || zone->apex == NULL) {
@@ -2418,8 +2504,8 @@ int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *zone)
return KNOT_EBADARG;
}
- //return (zone->nsec3_params.algorithm != 0);
- return (zone->nsec3_nodes->th_root != NULL);
+ return (zone->nsec3_params.algorithm != 0
+ && zone->nsec3_nodes->th_root != NULL);
}
/*----------------------------------------------------------------------------*/
@@ -2662,16 +2748,15 @@ int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
// ret = ck_copy_table(from->table, &contents->table);
ret = ck_shallow_copy(from->table, &contents->table);
if (ret != 0) {
- dbg_zone("knot_zone_contents_shallow_copy: "
- "hash table copied\n");
+ dbg_zone_verb("knot_zone_contents_shallow_copy: "
+ "hash table copied\n");
ret = KNOT_ERROR;
goto cleanup;
}
}
#endif
- dbg_zone("knot_zone_contents_shallow_copy: "
- "finished OK\n");
+ dbg_zone("knot_zone_contents_shallow_copy: finished OK\n");
*to = contents;
return KNOT_EOK;
@@ -2740,13 +2825,11 @@ int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from,
contents->node_count = from->node_count;
contents->flags = from->flags;
+ // set the 'new' flag
+ knot_zone_contents_set_gen_new(contents);
contents->zone = from->zone;
-// /* Initialize NSEC3 params */
-// memcpy(&contents->nsec3_params, &from->nsec3_params,
-// sizeof(knot_nsec3_params_t));
-
if ((ret = knot_zone_tree_deep_copy(from->nodes,
contents->nodes)) != KNOT_EOK
|| (ret = knot_zone_tree_deep_copy(from->nsec3_nodes,
@@ -2758,8 +2841,8 @@ int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from,
if (from->table != NULL) {
ret = ck_deep_copy(from->table, &contents->table);
if (ret != 0) {
- dbg_zone("knot_zone_contents_shallow_copy: "
- "hash table copied\n");
+ dbg_zone_verb("knot_zone_contents_shallow_copy: "
+ "hash table copied\n");
ret = KNOT_ERROR;
goto cleanup;
}
@@ -2903,16 +2986,6 @@ static void knot_zc_integrity_check_previous(const knot_node_t *node,
++check_data->errors;
}
-// if (knot_node_next(check_data->previous) != node) {
-// char *name2 = knot_dname_to_str(knot_node_owner(
-// knot_node_next(check_data->previous)));
-// fprintf(stderr, "Wrong next node: node %s, next %s. "
-// "Should be %s.\n", name_prev, name2 ,name);
-// free(name2);
-
-// ++check_data->errors;
-// }
-
free(name_prev);
}
}
@@ -2995,11 +3068,6 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node,
&& knot_dname_matched_labels(node_owner, parent_owner)
== knot_dname_label_count(parent_owner)) {
-// // increase the parent's children count
-// fprintf(stderr, "Parent: %s, node: %s. Increasing children count.\n",
-// pname, name);
-// ++check_data->children;
-
// check the parent pointer
const knot_node_t *parent = knot_node_parent(node);
if (parent != check_data->parent) {
@@ -3038,22 +3106,6 @@ static void knot_zc_integrity_check_parent(const knot_node_t *node,
++check_data->errors;
}
}
- } else {
- // not a direct child, check children count
-// if (check_data->parent
-// && knot_node_children(check_data->parent)
-// != check_data->children) {
-// fprintf(stderr, "Wrong children count: node %s, count: "
-// "%u. Should be: %u\n", pname,
-// knot_node_children(check_data->parent),
-// check_data->children);
-
-// ++check_data->errors;
-// }
-
- // reset the children count
- //check_data->parent = node;
-// check_data->children = 0;
}
free(pname);
@@ -3110,15 +3162,6 @@ static int knot_zc_integrity_check_find_dname(const knot_zone_contents_t *zone,
const char *node_name)
{
int ret = 0;
-
-// find_dname_data_t data_find;
-// data_find.found = NULL;
-// data_find.to_find = to_find;
-
-// int res = knot_zone_contents_dname_table_apply(
-// (knot_zone_contents_t *)zone,
-// find_in_dname_table, (void *)&data_find);
-// assert(res == KNOT_EOK);
knot_dname_t *found = knot_dname_table_find_dname(zone->dname_table,
(knot_dname_t *)to_find);
@@ -3364,27 +3407,12 @@ static void reset_new_nodes(knot_zone_tree_node_t *tree_node, void *data)
/*----------------------------------------------------------------------------*/
-/*!< \todo remove debug code. */
-//static void print_child_count(knot_node_t *node, void *data)
-//{
-// UNUSED(data);
-// assert(node != NULL);
-
-// char *name = knot_dname_to_str(knot_node_owner(node));
-// fprintf(stderr, "Node: %s, children count: %d\n", name,
-// knot_node_children(node));
-// free(name);
-//}
-
-/*----------------------------------------------------------------------------*/
-
static void count_nsec3_nodes(knot_zone_tree_node_t *tree_node, void *data)
{
assert(tree_node != NULL);
assert(tree_node->node != NULL);
assert(data != NULL);
-// int *count = (int *)data;
knot_node_t *apex = (knot_node_t *)data;
assert(apex != NULL);
@@ -3406,10 +3434,6 @@ int knot_zc_integrity_check_child_count(check_data_t *data)
knot_zone_tree_init(nodes_copy);
-// int ret = knot_zone_contents_tree_apply_inorder(data->contents,
-// print_child_count,
-// NULL);
-
int ret = knot_zone_tree_deep_copy(data->contents->nodes, nodes_copy);
assert(ret == KNOT_EOK);
@@ -3425,9 +3449,8 @@ int knot_zc_integrity_check_child_count(check_data_t *data)
knot_zone_tree_forward_apply_inorder(nodes_copy, count_children, NULL);
// add count of NSEC3 nodes to the apex' children count
-// int nsec3_nodes = 0;
- dbg_zone("Children count of new apex before NSEC3: %d\n",
- data->contents->apex->new_node->children);
+ fprintf(stderr, "Children count of new apex before NSEC3: %d\n",
+ data->contents->apex->new_node->children);
knot_zone_tree_forward_apply_inorder(data->contents->nsec3_nodes,
count_nsec3_nodes,
(void *)apex_copy);
@@ -3435,7 +3458,6 @@ int knot_zc_integrity_check_child_count(check_data_t *data)
// now compare the children counts
// iterate over the old zone and search for nodes in the copy
-// data->children = nsec3_nodes;
knot_zone_tree_forward_apply_inorder(nodes_copy, check_child_count,
(void *)data);
diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h
index 3143ef9..2ca333e 100644..100755
--- a/src/libknot/zone/zone-contents.h
+++ b/src/libknot/zone/zone-contents.h
@@ -55,7 +55,7 @@ typedef struct knot_zone_contents_t {
knot_nsec3_params_t nsec3_params;
- /*!
+ /*!
* \todo Unify the use of this field - authoritative nodes vs. all.
*/
uint node_count;
@@ -80,17 +80,14 @@ typedef struct knot_zone_contents_t {
/*----------------------------------------------------------------------------*/
knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
- uint node_count,
- int use_domain_table,
- struct knot_zone *zone);
-
-//short knot_zone_contents_generation(const knot_zone_contents_t *contents);
+ uint node_count,
+ int use_domain_table,
+ struct knot_zone *zone);
int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents);
int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents);
int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents);
-//void knot_zone_contents_switch_generation(knot_zone_contents_t *contents);
void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents);
void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents);
@@ -187,9 +184,6 @@ int knot_zone_contents_remove_node(knot_zone_contents_t *contents,
const knot_node_t *node, knot_zone_tree_node_t **removed_tree,
ck_hash_table_item_t **removed_hash);
-//knot_zone_tree_node_t *knot_zone_contents_remove_node(
-// knot_zone_contents_t *contents, const knot_node_t *node);
-
int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents,
const knot_node_t *node, knot_zone_tree_node_t **removed);
@@ -357,9 +351,6 @@ const knot_node_t *knot_zone_contents_apex(
knot_node_t *knot_zone_contents_get_apex(
const knot_zone_contents_t *contents);
-//knot_dname_t *knot_zone_contents_name(
-// const knot_zone_contents_t *contents);
-
/*!
* \brief Optimizes zone by replacing domain names in RDATA with references to
* domain names present in zone (as node owners).
@@ -555,12 +546,6 @@ int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
int knot_zone_contents_shallow_copy2(const knot_zone_contents_t *from,
knot_zone_contents_t **to);
-//int knot_zone_contents_dnames_from_node_to_table(
-// knot_dname_table_t *table, knot_node_t *node);
-
-//void knot_zone_contents_adjust_node(knot_node_t *node,
-// knot_zone_contents_t *zone, int check_ver);
-
void knot_zone_contents_free(knot_zone_contents_t **contents);
void knot_zone_contents_deep_free(knot_zone_contents_t **contents,
diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c
new file mode 100755
index 0000000..d3fd961
--- /dev/null
+++ b/src/libknot/zone/zone-diff.c
@@ -0,0 +1,1004 @@
+/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <config.h>
+
+#include "libknot/util/error.h"
+#include "libknot/util/debug.h"
+#include "libknot/rdata.h"
+#include "zone-diff.h"
+#include "libknot/nameserver/name-server.h"
+
+struct zone_diff_param {
+ const knot_zone_contents_t *contents;
+ char nsec3;
+ knot_changeset_t *changeset;
+ int ret;
+};
+
+// forward declaration
+static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2,
+ knot_changeset_t *changeset);
+
+static int knot_zone_diff_load_soas(const knot_zone_contents_t *zone1,
+ const knot_zone_contents_t *zone2,
+ knot_changeset_t *changeset)
+{
+ if (zone1 == NULL || zone2 == NULL || changeset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_node_t *apex1 = knot_zone_contents_apex(zone1);
+ const knot_node_t *apex2 = knot_zone_contents_apex(zone2);
+ if (apex1 == NULL || apex2 == NULL) {
+ dbg_zonediff("zone_diff: "
+ "both zones must have apex nodes.\n");
+ return KNOT_EBADARG;
+ }
+
+ knot_rrset_t *soa_rrset1 = knot_node_get_rrset(apex1, KNOT_RRTYPE_SOA);
+ knot_rrset_t *soa_rrset2 = knot_node_get_rrset(apex2, KNOT_RRTYPE_SOA);
+ if (soa_rrset1 == NULL || soa_rrset2 == NULL) {
+ dbg_zonediff("zone_diff: "
+ "both zones must have apex nodes.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_rrset_rdata(soa_rrset1) == NULL ||
+ knot_rrset_rdata(soa_rrset2) == NULL) {
+ dbg_zonediff("zone_diff: "
+ "both zones must have apex nodes with SOA "
+ "RRs.\n");
+ return KNOT_EBADARG;
+ }
+
+ int64_t soa_serial1 =
+ knot_rdata_soa_serial(knot_rrset_rdata(soa_rrset1));
+ if (soa_serial1 == -1) {
+ dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n");
+ }
+
+ int64_t soa_serial2 =
+ knot_rdata_soa_serial(knot_rrset_rdata(soa_rrset2));
+
+ if (soa_serial2 == -1) {
+ dbg_zonediff("zone_diff: load_soas: Got bad SOA.\n");
+ }
+
+ if (ns_serial_compare(soa_serial1, soa_serial2) == 0) {
+ dbg_zonediff("zone_diff: "
+ "second zone must have higher serial than the "
+ "first one. (%lld vs. %lld)\n",
+ soa_serial1, soa_serial2);
+ return KNOT_ENODIFF;
+ }
+
+ if (ns_serial_compare(soa_serial1, soa_serial2) > 0) {
+ dbg_zonediff("zone_diff: "
+ "second zone must have higher serial than the "
+ "first one. (%lld vs. %lld)\n",
+ soa_serial1, soa_serial2);
+ return KNOT_ERANGE;
+ }
+
+ /* We will not touch SOA later, now is the time to handle RRSIGs. */
+ int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(soa_rrset1),
+ knot_rrset_rrsigs(soa_rrset2),
+ changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff_verb("zone_diff: load_soas: Failed to diff SOAs' RRSIGs."
+ " Reason: %s.\n", knot_strerror(ret));
+ /* This might not necasarilly be an error. */
+ }
+
+ assert(changeset);
+
+ ret = knot_rrset_deep_copy(soa_rrset1, &changeset->soa_from, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: load_soas: Cannot copy RRSet.\n");
+ return ret;
+ }
+
+ /* We MUST NOT save this RRSIG. */
+ knot_rrset_deep_free(&changeset->soa_from->rrsigs, 1, 1, 1);
+ assert(changeset->soa_from->rrsigs == NULL);
+
+ ret = knot_rrset_deep_copy(soa_rrset2, &changeset->soa_to, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: load_soas: Cannot copy RRSet.\n");
+ return ret;
+ }
+
+ knot_rrset_deep_free(&changeset->soa_to->rrsigs, 1, 1, 1);
+ assert(changeset->soa_to->rrsigs == NULL);
+
+ changeset->serial_from = soa_serial1;
+ changeset->serial_to = soa_serial2;
+
+ dbg_zonediff_verb("zone_diff: load_soas: SOAs diffed. (%lld -> %lld)\n",
+ soa_serial1, soa_serial2);
+
+ return KNOT_EOK;
+}
+
+/*!< \todo Only use add or remove function, not both as they are the same. */
+/*!< \todo Also, this might be all handled by function in changesets.h!!! */
+static int knot_zone_diff_changeset_add_rrset(knot_changeset_t *changeset,
+ const knot_rrset_t *rrset)
+{
+ /* Remove all RRs of the RRSet. */
+ if (changeset == NULL || rrset == NULL) {
+ dbg_zonediff("zone_diff: add_rrset: NULL parameters.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_rrset_rdata_rr_count(rrset) == 0) {
+ dbg_zonediff_detail("zone_diff: Nothing to add.\n");
+ return KNOT_EOK;
+ }
+
+ dbg_zonediff_detail("zone_diff: add_rrset: Adding RRSet (%d RRs):\n",
+ knot_rrset_rdata_rr_count(rrset));
+ knot_rrset_dump(rrset, 1);
+
+ knot_rrset_t *rrset_copy = NULL;
+ int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: add_rrset: Cannot copy RRSet.\n");
+ return ret;
+ }
+ if (rrset_copy->rrsigs != NULL) {
+ knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1);
+ }
+ assert(knot_rrset_rrsigs(rrset_copy) == NULL);
+
+ ret = knot_changeset_add_new_rr(changeset, rrset_copy,
+ XFRIN_CHANGESET_ADD);
+ if (ret != KNOT_EOK) {
+ /* We have to free the copy now! */
+ knot_rrset_deep_free(&rrset_copy, 1, 1, 1);
+ dbg_zonediff("zone_diff: add_rrset: Could not add RRSet. "
+ "Reason: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_changeset_remove_rrset(knot_changeset_t *changeset,
+ const knot_rrset_t *rrset)
+{
+ /* Remove all RRs of the RRSet. */
+ if (changeset == NULL) {
+ dbg_zonediff("zone_diff: remove_rrset: NULL parameters.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (rrset == NULL) {
+ return KNOT_EOK;
+ }
+
+ if (knot_rrset_rdata_rr_count(rrset) == 0) {
+ dbg_zonediff_detail("zone_diff: Nothing to remove.\n");
+ return KNOT_EOK;
+ }
+
+ dbg_zonediff_detail("zone_diff: remove_rrset: Removing RRSet (%d RRs):\n",
+ knot_rrset_rdata_rr_count(rrset));
+ knot_rrset_dump(rrset, 1);
+
+ knot_rrset_t *rrset_copy = NULL;
+ int ret = knot_rrset_deep_copy(rrset, &rrset_copy, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: remove_rrset: Cannot copy RRSet.\n");
+ return ret;
+ }
+ if (rrset_copy->rrsigs != NULL) {
+ knot_rrset_deep_free(&rrset_copy->rrsigs, 1, 1, 1);
+ }
+ assert(knot_rrset_rrsigs(rrset_copy) == NULL);
+
+ ret = knot_changeset_add_new_rr(changeset, rrset_copy,
+ XFRIN_CHANGESET_REMOVE);
+ if (ret != KNOT_EOK) {
+ /* We have to free the copy now. */
+ knot_rrset_deep_free(&rrset_copy, 1, 1, 1);
+ dbg_zonediff("zone_diff: remove_rrset: Could not remove RRSet. "
+ "Reason: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_add_node(const knot_node_t *node,
+ knot_changeset_t *changeset)
+{
+ if (node == NULL || changeset == NULL) {
+ dbg_zonediff("zone_diff: add_node: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Add all rrsets from node. */
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ /* Empty non-terminals - legal case. */
+ dbg_zonediff_detail("zone_diff: Node has no RRSets.\n");
+ return KNOT_EOK;
+ }
+
+ for (uint i = 0; i < knot_node_rrset_count(node); i++) {
+ assert(rrsets[i]);
+ int ret = knot_zone_diff_changeset_add_rrset(changeset,
+ rrsets[i]);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: add_node: Cannot add RRSet (%s).\n",
+ knot_strerror(ret));
+ free(rrsets);
+ return ret;
+ }
+ }
+
+ free(rrsets);
+
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_remove_node(knot_changeset_t *changeset,
+ const knot_node_t *node)
+{
+ if (changeset == NULL || node == NULL) {
+ dbg_zonediff("zone_diff: remove_node: NULL parameters.\n");
+ return KNOT_EBADARG;
+ }
+
+ dbg_zonediff("zone_diff: remove_node: Removing node:\n");
+dbg_zonediff_exec_detail(
+ knot_node_dump((knot_node_t *)node, 1);
+);
+
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ dbg_zonediff_verb("zone_diff: remove_node: "
+ "Nothing to remove.\n");
+ return KNOT_EOK;
+ }
+
+ dbg_zonediff_detail("zone_diff: remove_node: Will be removing %d RRSets.\n",
+ knot_node_rrset_count(node));
+
+ /* Remove all the RRSets of the node. */
+ for (uint i = 0; i < knot_node_rrset_count(node); i++) {
+ int ret = knot_zone_diff_changeset_remove_rrset(changeset,
+ rrsets[i]);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: remove_node: Failed to "
+ "remove rrset. Error: %s\n",
+ knot_strerror(ret));
+ free(rrsets);
+ return ret;
+ }
+ }
+
+ free(rrsets);
+
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_rdata_return_changes(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2,
+ knot_rrset_t **changes)
+{
+ if (rrset1 == NULL || rrset2 == NULL) {
+ dbg_zonediff("zone_diff: diff_rdata: NULL arguments. (%p) (%p).\n",
+ rrset1, rrset2);
+ return KNOT_EBADARG;
+ }
+
+ /*
+ * Take one rdata from first list and search through the second list
+ * looking for an exact match. If no match occurs, it means that this
+ * particular RR has changed.
+ * After the list has been traversed, we have a list of
+ * changed/removed rdatas. This has awful computation time.
+ */
+ dbg_zonediff_detail("zone_diff: diff_rdata: Diff of %s, type=%s. "
+ "RR count 1=%d RR count 2=%d.\n",
+ knot_dname_to_str(rrset1->owner),
+ knot_rrtype_to_string(rrset1->type),
+ knot_rrset_rdata_rr_count(rrset1),
+ knot_rrset_rdata_rr_count(rrset2));
+
+ /* Create fake RRSet, it will be easier to handle. */
+ *changes = knot_rrset_new(knot_rrset_get_owner(rrset1),
+ knot_rrset_type(rrset1),
+ knot_rrset_class(rrset1),
+ knot_rrset_ttl(rrset1));
+ if (*changes == NULL) {
+ dbg_zonediff("zone_diff: diff_rdata: "
+ "Could not create RRSet with changes.\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(knot_rrset_type(rrset1));
+ assert(desc);
+
+ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset1);
+ while(tmp_rdata != NULL) {
+ const knot_rdata_t *tmp_rdata_second_rrset =
+ knot_rrset_rdata(rrset2);
+ while ((tmp_rdata_second_rrset != NULL) &&
+ (knot_rdata_compare(tmp_rdata,
+ tmp_rdata_second_rrset,
+ desc->wireformat) != 0)) {
+ tmp_rdata_second_rrset =
+ knot_rrset_rdata_next(rrset2,
+ tmp_rdata_second_rrset);
+ }
+ if (tmp_rdata_second_rrset == NULL) {
+ /*
+ * This means that the while cycle above has finished
+ * because the list was traversed - there's no match.
+ */
+ dbg_zonediff("zone_diff: diff_rdata: "
+ "No match for RR (type=%s owner=%s).\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset1)),
+ knot_dname_to_str(rrset1->owner));
+ /* Make a copy of tmp_rdata. */
+ knot_rdata_t *tmp_rdata_copy =
+ knot_rdata_deep_copy(tmp_rdata,
+ knot_rrset_type(rrset1),
+ 1);
+ int ret = knot_rrset_add_rdata(*changes,
+ tmp_rdata_copy);
+ /*!< \todo dispose of the copy. */
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rdata: "
+ "Could not add rdata to rrset.");
+ knot_rrset_deep_free(changes, 1, 1, 0);
+ return ret;
+ }
+ } else {
+ dbg_zonediff_detail("zone_diff: diff_rdata: "
+ "Found matching RR for type %s.\n",
+ knot_rrtype_to_string(rrset1->type));
+ }
+ tmp_rdata = knot_rrset_rdata_next(rrset1, tmp_rdata);
+ }
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_rdata(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2,
+ knot_changeset_t *changeset)
+{
+ if ((changeset == NULL) || (rrset1 == NULL && rrset2 == NULL)) {
+ dbg_zonediff("zone_diff: diff_rdata: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+ /*
+ * The easiest solution is to remove all the RRs that had no match and
+ * to add all RRs that had no match, but those from second RRSet. */
+
+ /* Get RRs to remove from zone. */
+ knot_rrset_t *to_remove = NULL;
+ if (rrset1 != NULL && rrset2 == NULL) {
+ assert(rrset1->type == KNOT_RRTYPE_RRSIG);
+ dbg_zonediff_detail("zone_diff: diff_rdata: RRSIG will be "
+ "removed.\n");
+ int ret = knot_rrset_deep_copy(rrset1, &to_remove, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+ } else if (rrset1 != NULL && rrset2 != NULL) {
+ int ret = knot_zone_diff_rdata_return_changes(rrset1, rrset2,
+ &to_remove);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rdata: Could not get changes. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+ } else {
+ dbg_zonediff("zone_diff: diff_rdata: These are not the diffs you "
+ "are looking for.\n");
+ }
+
+ dbg_zonediff_detail("zone_diff: diff_rdata: To remove:\n");
+ knot_rrset_dump(to_remove, 1);
+
+ int ret = knot_zone_diff_changeset_remove_rrset(changeset,
+ to_remove);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&to_remove, 1, 1, 1);
+ dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Copy was made in add_rrset function, we can free now. */
+ knot_rrset_deep_free(&to_remove, 1, 1, 1);
+
+ /* Get RRs to add to zone. */
+ knot_rrset_t *to_add = NULL;
+ if (rrset2 != NULL && rrset1 == NULL) {
+ assert(rrset2->type == KNOT_RRTYPE_RRSIG);
+ dbg_zonediff_detail("zone_diff: diff_rdata: RRSIG will be "
+ "added.\n");
+ int ret = knot_rrset_deep_copy(rrset2, &to_add, 1);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rdata: Could not copy rrset. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+ } else if (rrset1 != NULL && rrset2 != NULL) {
+ ret = knot_zone_diff_rdata_return_changes(rrset2, rrset1,
+ &to_add);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rdata: Could not get changes. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+ } else {
+ dbg_zonediff("zone_diff: diff_rdata: These are not the diffs you "
+ "are looking for.\n");
+ }
+
+ dbg_zonediff_detail("zone_diff: diff_rdata: To add:\n");
+ knot_rrset_dump(to_add, 1);
+
+ ret = knot_zone_diff_changeset_add_rrset(changeset,
+ to_add);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&to_add, 1, 1, 1);
+ dbg_zonediff("zone_diff: diff_rdata: Could not remove RRs. "
+ "Error: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Copy was made in add_rrset function, we can free now. */
+ knot_rrset_deep_free(&to_add, 1, 1, 1);
+
+ return KNOT_EOK;
+}
+
+static int knot_zone_diff_rrsets(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2,
+ knot_changeset_t *changeset)
+{
+// if (rrset1 == NULL || rrset2 == NULL) {
+// /* This could happen when diffing RRSIGs. */
+// if (rrset1 == NULL && rrset2 != NULL) {
+// dbg_zonediff("zone_diff: diff_rrsets: RRSIG missing in first"
+// " rrset1.\n");
+// int ret =
+// knot_zone_diff_changeset_add_rrset(changeset,
+// rrset2);
+// if (ret != KNOT_EOK) {
+// dbg_zonediff("zone_diff: diff_rrsets: "
+// "Cannot add RRSIG. (%s)\n",
+// knot_strerror(ret));
+// }
+// } else if (rrset1 != NULL && rrset2 == NULL) {
+// dbg_zonediff("zone_diff: diff_rrsets: RRSIG missing in second"
+// " rrset1.\n");
+// int ret =
+// knot_zone_diff_changeset_remove_rrset(changeset,
+// rrset1);
+// if (ret != KNOT_EOK) {
+// dbg_zonediff("zone_diff: diff_rrsets: "
+// "Cannot remove RRSIG. (%s)\n",
+// knot_strerror(ret));
+// }
+// }
+// dbg_zonediff_detail("zone_diff: diff_rrsets: "
+// "NULL arguments (RRSIGs?). (%p) (%p)\n",
+// rrset1, rrset2);
+// return KNOT_EOK;
+// }
+
+ assert(knot_dname_compare(knot_rrset_owner(rrset1),
+ knot_rrset_owner(rrset2)) == 0);
+ assert(knot_rrset_type(rrset1) == knot_rrset_type(rrset2));
+
+ int ret = knot_zone_diff_rdata(knot_rrset_rrsigs(rrset1),
+ knot_rrset_rrsigs(rrset2), changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_rrsets (%s:%s): Failed to diff RRSIGs. "
+ "They were: %p %p. (%s).\n",
+ knot_dname_to_str(rrset1->owner),
+ knot_rrtype_to_string(rrset1->type),
+ rrset1->rrsigs,
+ rrset2->rrsigs, knot_strerror(ret));
+ }
+
+ /* RRs (=rdata) have to be cross-compared, unfortunalely. */
+ return knot_zone_diff_rdata(rrset1, rrset2, changeset);
+}
+
+/*!< \todo this could be generic function for adding / removing. */
+static void knot_zone_diff_node(knot_node_t *node, void *data)
+{
+ if (node == NULL || data == NULL) {
+ dbg_zonediff("zone_diff: diff_node: NULL arguments.\n");
+ return;
+ }
+
+ struct zone_diff_param *param = (struct zone_diff_param *)data;
+ if (param->changeset == NULL || param->contents == NULL) {
+ dbg_zonediff("zone_diff: diff_node: NULL arguments.\n");
+ param->ret = KNOT_EBADARG;
+ return;
+ }
+
+ if (param->ret != KNOT_EOK) {
+ /* Error occured before, no point in continuing. */
+ dbg_zonediff_detail("zone_diff: diff_node: error: %s\n",
+ knot_strerror(param->ret));
+ return;
+ }
+
+ /*
+ * First, we have to search the second tree to see if there's according
+ * node, if not, the whole node has been removed.
+ */
+ const knot_node_t *node_in_second_tree = NULL;
+ const knot_dname_t *node_owner = knot_node_owner(node);
+ assert(node_owner);
+ if (!param->nsec3) {
+ node_in_second_tree =
+ knot_zone_contents_find_node(param->contents,
+ node_owner);
+ } else {
+ dbg_zonediff_verb("zone_diff: diff_node: NSEC3 zone.\n");
+ node_in_second_tree =
+ knot_zone_contents_find_nsec3_node(param->contents,
+ node_owner);
+ }
+
+ if (node_in_second_tree == NULL) {
+ dbg_zonediff_detail("zone_diff: diff_node: Node %s is not "
+ "in the second tree.\n",
+ knot_dname_to_str(node_owner));
+ int ret = knot_zone_diff_remove_node(param->changeset,
+ node);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: failed to remove node.\n");
+ param->ret = ret;
+ return;
+ }
+ param->ret = KNOT_EOK;
+ return;
+ }
+
+ assert(node_in_second_tree != node);
+
+ dbg_zonediff_detail("zone_diff: diff_node: Node %s is present in "
+ "both trees.\n", knot_dname_to_str(node_owner));
+ /* The nodes are in both trees, we have to diff each RRSet. */
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ dbg_zonediff("zone_diff: Node in first tree has no RRSets.\n");
+ /*
+ * If there are no RRs in the first tree, all of the RRs
+ * in the second tree will have to be inserted to ADD section.
+ */
+ int ret = knot_zone_diff_add_node(node_in_second_tree,
+ param->changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_node: "
+ "Could not add node from second tree. "
+ "Reason: %s.\n", knot_strerror(ret));
+ }
+ param->ret = ret;
+ return;
+ }
+
+ for (uint i = 0; i < knot_node_rrset_count(node); i++) {
+ /* Search for the RRSet in the node from the second tree. */
+ const knot_rrset_t *rrset = rrsets[i];
+ assert(rrset);
+
+ /* SOAs are handled explicitly. */
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
+ continue;
+ }
+
+ const knot_rrset_t *rrset_from_second_node =
+ knot_node_rrset(node_in_second_tree,
+ knot_rrset_type(rrset));
+ if (rrset_from_second_node == NULL) {
+ dbg_zonediff("zone_diff: diff_node: There is no counterpart "
+ "for RRSet of type %s in second tree.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+ /* RRSet has been removed. Make a copy and remove. */
+ assert(rrset);
+ int ret = knot_zone_diff_changeset_remove_rrset(
+ param->changeset,
+ rrset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_node: "
+ "Failed to remove RRSet.\n");
+ param->ret = ret;
+ free(rrsets);
+ return;
+ }
+ } else {
+ dbg_zonediff("zone_diff: diff_node: There is a counterpart "
+ "for RRSet of type %s in second tree.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+ /* Diff RRSets. */
+ int ret = knot_zone_diff_rrsets(rrset,
+ rrset_from_second_node,
+ param->changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: "
+ "Failed to diff RRSets.\n");
+ param->ret = ret;
+ free(rrsets);
+ return;
+ }
+
+// dbg_zonediff_verb("zone_diff: diff_node: Changes in "
+// "RRSIGs.\n");
+// /*! \todo There is ad-hoc solution in the function, maybe handle here. */
+// ret = knot_zone_diff_rrsets(rrset->rrsigs,
+// rrset_from_second_node->rrsigs,
+// param->changeset);
+// if (ret != KNOT_EOK) {
+// dbg_zonediff("zone_diff: "
+// "Failed to diff RRSIGs.\n");
+// param->ret = ret;
+// return;
+// }
+ }
+ }
+
+ free(rrsets);
+
+ /*! \todo move to one function with the code above. */
+ rrsets = knot_node_rrsets(node_in_second_tree);
+ if (rrsets == NULL) {
+ dbg_zonediff("zone_diff: Node in second tree has no RRSets.\n");
+ /*
+ * This can happen when node in second
+ * tree is empty non-terminal and as such has no RRs.
+ * Whole node from the first tree has to be removed.
+ */
+ // TODO following code creates duplicated RR in diff.
+ // IHMO such case should be handled here
+// int ret = knot_zone_diff_remove_node(param->changeset,
+// node);
+// if (ret != KNOT_EOK) {
+// dbg_zonediff("zone_diff: diff_node: "
+// "Cannot remove node. Reason: %s.\n",
+// knot_strerror(ret));
+// }
+ param->ret = KNOT_EOK;
+ return;
+ }
+
+ for (uint i = 0; i < knot_node_rrset_count(node_in_second_tree); i++) {
+ /* Search for the RRSet in the node from the second tree. */
+ const knot_rrset_t *rrset = rrsets[i];
+ assert(rrset);
+
+ /* SOAs are handled explicitly. */
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
+ continue;
+ }
+
+ const knot_rrset_t *rrset_from_first_node =
+ knot_node_rrset(node,
+ knot_rrset_type(rrset));
+ if (rrset_from_first_node == NULL) {
+ dbg_zonediff("zone_diff: diff_node: There is no counterpart "
+ "for RRSet of type %s in first tree.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+ /* RRSet has been added. Make a copy and add. */
+ assert(rrset);
+ int ret = knot_zone_diff_changeset_add_rrset(
+ param->changeset,
+ rrset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: diff_node: "
+ "Failed to add RRSet.\n");
+ param->ret = ret;
+ free(rrsets);
+ return;
+ }
+ } else {
+ /* Already handled. */
+ ;
+ }
+ }
+
+ free(rrsets);
+
+ assert(param->ret == KNOT_EOK);
+}
+
+/*!< \todo possibly not needed! */
+static void knot_zone_diff_add_new_nodes(knot_node_t *node, void *data)
+{
+ assert(node);
+ if (node == NULL || data == NULL) {
+ dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n");
+ return;
+ }
+
+ struct zone_diff_param *param = (struct zone_diff_param *)data;
+ if (param->changeset == NULL || param->contents == NULL) {
+ dbg_zonediff("zone_diff: add_new_nodes: NULL arguments.\n");
+ param->ret = KNOT_EBADARG;
+ return;
+ }
+
+ if (param->ret != KNOT_EOK) {
+ /* Error occured before, no point in continuing. */
+ dbg_zonediff_detail("zone_diff: add_new_nodes: error: %s\n",
+ knot_strerror(param->ret));
+ return;
+ }
+
+ /*
+ * If a node is not present in the second zone, it is a new node
+ * and has to be added to changeset. Differencies on the RRSet level are
+ * already handled.
+ */
+ const knot_zone_contents_t *other_zone = param->contents;
+ assert(other_zone);
+
+ const knot_dname_t *node_owner = knot_node_owner(node);
+ /*
+ * Node should definitely have an owner, otherwise it would not be in
+ * the tree.
+ */
+ assert(node_owner);
+
+ knot_node_t *new_node = NULL;
+ if (!param->nsec3) {
+ new_node = knot_zone_contents_get_node(other_zone, node_owner);
+ } else {
+ new_node = knot_zone_contents_get_nsec3_node(other_zone,
+ node_owner);
+ }
+
+ if (!new_node) {
+ assert(node);
+ int ret = knot_zone_diff_add_node(node, param->changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: add_new_nodes: Cannot add "
+ "node: %s to changeset. Reason: %s.\n",
+ knot_dname_to_str(node->owner),
+ knot_strerror(ret));
+ }
+ }
+
+ assert(param->ret == KNOT_EOK);
+}
+
+int knot_zone_contents_diff(const knot_zone_contents_t *zone1,
+ const knot_zone_contents_t *zone2,
+ knot_changeset_t *changeset)
+{
+ if (zone1 == NULL || zone2 == NULL) {
+ dbg_zonediff("zone_diff: NULL argument(s).\n");
+ return KNOT_EBADARG;
+ }
+
+// /* Create changeset structure. */
+// *changeset = malloc(sizeof(knot_changeset_t));
+// if (*changeset == NULL) {
+// ERR_ALLOC_FAILED;
+// return KNOT_ENOMEM;
+// }
+ memset(changeset, 0, sizeof(knot_changeset_t));
+
+ /* Settle SOAs first. */
+ int ret = knot_zone_diff_load_soas(zone1, zone2, changeset);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: loas_SOAs failed with error: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ dbg_zonediff("zone_diff: SOAs loaded.\n");
+
+ /* Traverse one tree, compare every node, each RRSet with its rdata. */
+ struct zone_diff_param param;
+ param.contents = zone2;
+ param.nsec3 = 0;
+ param.changeset = changeset;
+ param.ret = KNOT_EOK;
+ ret = knot_zone_contents_tree_apply_inorder(
+ (knot_zone_contents_t *)zone1,
+ knot_zone_diff_node,
+ &param);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: Tree traversal failed "
+ "with error: %s. Error from inner function: %s\n",
+ knot_strerror(ret),
+ knot_strerror(param.ret));
+ return ret;
+ }
+
+ /* Do the same for NSEC3 nodes. */
+ param.nsec3 = 1;
+ ret = knot_zone_contents_nsec3_apply_inorder((knot_zone_contents_t *)zone1, knot_zone_diff_node,
+ &param);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: Tree traversal failed "
+ "with error: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Some nodes may have been added. The code above will not notice,
+ * we have to go through the second tree and add missing nodes to
+ * changeset.
+ */
+ param.nsec3 = 0;
+ param.contents = zone1;
+ ret = knot_zone_contents_tree_apply_inorder((knot_zone_contents_t *)zone2,
+ knot_zone_diff_add_new_nodes,
+ &param);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: Tree traversal failed "
+ "with error: %s. Error from inner function: %s\n",
+ knot_strerror(ret),
+ knot_strerror(param.ret));
+ return ret;
+ }
+
+ /* NSEC3 nodes. */
+ param.nsec3 = 1;
+ param.contents = zone1;
+ ret = knot_zone_contents_nsec3_apply_inorder((knot_zone_contents_t *)zone2,
+ knot_zone_diff_add_new_nodes,
+ &param);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: Tree traversal failed "
+ "with error: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+#ifdef KNOT_ZONEDIFF_DEBUG
+#ifdef DEBUG_ENABLE_DETAILS
+static void knot_zone_diff_dump_changeset(knot_changeset_t *ch)
+{
+ dbg_zonediff_detail("Changeset FROM: %d\n", ch->serial_from);
+ rrset_dump_text(ch->soa_from, stderr);
+ dbg_zonediff_detail("\n");
+ dbg_zonediff_detail("Changeset TO: %d\n", ch->serial_to);
+ rrset_dump_text(ch->soa_to, stderr);
+ dbg_zonediff_detail("\n");
+ dbg_zonediff_detail("Adding %d RRs.\n", ch->add_count);
+ dbg_zonediff_detail("Removing %d RRs.\n", ch->remove_count);
+
+ dbg_zonediff_detail("ADD section:\n");
+ dbg_zonediff_detail("**********************************************\n");
+ for (int i = 0; i < ch->add_count; i++) {
+ rrset_dump_text(ch->add[i], stderr);
+ dbg_zonediff_detail("\n");
+ }
+ dbg_zonediff_detail("REMOVE section:\n");
+ dbg_zonediff_detail("**********************************************\n");
+ for (int i = 0; i < ch->remove_count; i++) {
+ rrset_dump_text(ch->remove[i], stderr);
+ dbg_zonediff_detail("\n");
+ }
+}
+#endif
+#endif
+
+int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1,
+ const knot_zone_contents_t *z2,
+ knot_changesets_t **changesets)
+{
+ if (z1 == NULL || z2 == NULL) {
+ dbg_zonediff("zone_diff: create_changesets: NULL arguments.\n");
+ return KNOT_EBADARG;
+ }
+ /* Create changesets. */
+ int ret = knot_changeset_allocate(changesets);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: create_changesets: "
+ "Could not allocate changesets."
+ "Reason: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ memset((*changesets)->sets, 0, sizeof(knot_changeset_t));
+
+ ret = knot_zone_contents_diff(z1, z2, (*changesets)->sets);
+ if (ret != KNOT_EOK) {
+ dbg_zonediff("zone_diff: create_changesets: "
+ "Could not diff zones. "
+ "Reason: %s.\n", knot_strerror(ret));
+ return ret;
+ }
+
+ (*changesets)->count = 1;
+
+ dbg_zonediff("Changesets created successfully!\n");
+ dbg_zonediff_detail("Changeset dump:\n");
+dbg_zonediff_exec_detail(
+ knot_zone_diff_dump_changeset((*changesets)->sets);
+);
+
+ return KNOT_EOK;
+}
+
+/* Mostly just for testing. We only shall diff zones in memory later. */
+//int knot_zone_diff_zones(const char *zonefile1, const char *zonefile2)
+//{
+ /* Compile test zones. */
+// int ret = zone_read("example.com.", "/home/jan/test/testzone1", "tmpzone1.db", 0);
+// assert(ret == KNOT_EOK);
+// ret = zone_read("example.com.", "/home/jan/test/testzone2", "tmpzone2.db", 0);
+// assert(ret == KNOT_EOK);
+// /* Load test zones. */
+// zloader_t *loader = NULL;
+// int ret = knot_zload_open(&loader, "tmpzone1.db");
+// assert(ret == KNOT_EOK);
+// knot_zone_t *z1 = knot_zload_load(loader);
+// ret = knot_zload_open(&loader, "tmpzone2.db");
+// assert(ret == KNOT_EOK);
+// knot_zone_t *z2 = knot_zload_load(loader);
+// assert(z1 && z2);
+// knot_changeset_t *changeset = malloc(sizeof(knot_changeset_t));
+// memset(changeset, 0, sizeof(knot_changeset_t));
+// assert(knot_zone_contents_diff(z1->contents, z2->contents,
+// changeset) == KNOT_EOK);
+// dbg_zonediff("Changeset created: From=%d to=%d.\n", changeset.serial_from,
+// changeset.serial_to);
+//// knot_changesets_t chngsets;
+//// chngsets->sets = malloc(sizeof(knot_changeset_t));
+//// chngsets->sets[0] = changeset;
+//// chngsets->count = 1;
+//// chngsets->allocated = 1;
+//// knot_zone_contents_t *new_zone = NULL;
+//// ret = xfrin_apply_changesets(z1, chngsets, &new_zone);
+//// if (ret != KNOT_EOK) {
+//// dbg_zonediff("Application of changesets failed. (%s)\n",
+//// knot_strerror(ret));
+//// }
+
+//// assert(new_zone);
+
+// /* Dump creted zone. */
+//// FILE *f = fopen("testovani", "w");
+//// zone_dump_text(new_zone, f);
+
+// knot_zone_deep_free(&z2, 0);
+// knot_zone_deep_free(&z1, 0);
+//// knot_zone_contents_deep_free(&new_zone, 1);
+//// knot_zone_free(&z1);
+
+// knot_free_changeset(&changeset);
+// exit(0);
+//}
+
diff --git a/src/tests/common/da_tests.h b/src/libknot/zone/zone-diff.h
index d51b7be..6e0eb1d 100644..100755
--- a/src/tests/common/da_tests.h
+++ b/src/libknot/zone/zone-diff.h
@@ -14,12 +14,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _KNOTD_DA_TESTS_H_
-#define _KNOTD_DA_TESTS_H_
+#ifndef _KNOT_ZONE_DIFF_H_
+#define _KNOT_ZONE_DIFF_H_
-#include "common/libtap/tap_unit.h"
+#include "libknot/zone/zone-contents.h"
+#include "libknot/updates/changesets.h"
-/* Unit API. */
-unit_api da_tests_api;
+/*! \brief zone1 -> zone2 */
+int knot_zone_diff_create_changesets(const knot_zone_contents_t *z1,
+ const knot_zone_contents_t *z2,
+ knot_changesets_t **changesets);
-#endif /* _KNOTD_DA_TESTS_H_ */
+#endif // _KNOT_ZONE_DIFF_H_
diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c
index 2b79f86..7de460a 100644..100755
--- a/src/libknot/zone/zone-tree.c
+++ b/src/libknot/zone/zone-tree.c
@@ -110,7 +110,7 @@ static void knot_zone_tree_free_node(knot_zone_tree_node_t *node,
knot_zone_tree_free_node(node->avl.avl_right, free_data);
if (free_data) {
- knot_node_free(&node->node, 0);
+ knot_node_free(&node->node);
}
free(node);
@@ -132,8 +132,7 @@ static int knot_zone_tree_deep_copy_node(knot_zone_tree_node_t *from,
}
int ret = knot_node_shallow_copy(from->node, &(*to)->node);
-// printf("Copied node: %p to node %p. New node1: %p, new node 2: %p\n",
-// from->node, (*to)->node, from->node->new_node, (*to)->node->new_node);
+
if (ret != KNOT_EOK) {
dbg_zone_verb("Failed to do shallow copy of node.\n");
free(*to);
@@ -155,7 +154,7 @@ static int knot_zone_tree_deep_copy_node(knot_zone_tree_node_t *from,
dbg_zone_verb("Failed to do shallow copy of right subtree.\n");
knot_zone_tree_free_node((*to)->avl.avl_left, 1);
(*to)->avl.avl_left = NULL;
- knot_node_free(&(*to)->node, 0);
+ knot_node_free(&(*to)->node);
free(*to);
*to = NULL;
return ret;
@@ -250,7 +249,7 @@ int knot_zone_tree_get(knot_zone_tree_t *tree, const knot_dname_t *owner,
knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl,
tmp);
- knot_node_free(&tmp_data, 0);
+ knot_node_free(&tmp_data);
free(tmp);
if (n != NULL) {
@@ -313,7 +312,7 @@ int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
int exact_match = TREE_FIND_LESS_EQUAL(
tree, knot_zone_tree_node, avl, tmp, &f, &prev);
- knot_node_free(&tmp_data, 0);
+ knot_node_free(&tmp_data);
free(tmp);
*found = (exact_match > 0) ? f->node : NULL;
@@ -356,9 +355,17 @@ dbg_zone_exec_detail(
/*! \todo Here we assume that the 'prev' pointer always points
* to an empty non-terminal.
*/
+ /*! \todo What did I mean by the previous TODO??
+ * Nevertheless, it seems to me that node->prev can be
+ * an empty non-terminal too, cannot it?
+ */
+ dbg_zone_detail("Previous: %p\n", prev->node);
*previous = (knot_node_rrset_count(prev->node) == 0)
? knot_node_get_previous(prev->node)
: prev->node;
+ dbg_zone_detail("Previous: %p, is empty: %d\n", *previous,
+ (*previous) ? knot_node_is_empty(*previous)
+ : -1);
}
assert(exact_match >= 0);
@@ -399,11 +406,9 @@ int knot_zone_tree_remove(knot_zone_tree_t *tree,
/*! \todo How to know if this was successful? */
TREE_REMOVE(tree, knot_zone_tree_node, avl, tmp);
- knot_node_free(&tmp_data, 0);
+ knot_node_free(&tmp_data);
free(tmp);
-// *removed = (n) ? n->node : NULL;
-// free(n);
*removed = n;
return KNOT_EOK;
diff --git a/src/libknot/zone/zone-tree.h b/src/libknot/zone/zone-tree.h
index ca65fe6..95b4e23 100644..100755
--- a/src/libknot/zone/zone-tree.h
+++ b/src/libknot/zone/zone-tree.h
@@ -39,8 +39,6 @@ typedef struct knot_zone_tree_node {
TREE_ENTRY(knot_zone_tree_node) avl;
/*! \brief Zone tree data. */
knot_node_t *node;
- /*! \brief Owner of the node. */
-// knot_dname_t *owner;
} knot_zone_tree_node_t;
/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/zone/zone.c b/src/libknot/zone/zone.c
index 122b014..65f810d 100644..100755
--- a/src/libknot/zone/zone.c
+++ b/src/libknot/zone/zone.c
@@ -33,6 +33,12 @@
#include "hash/cuckoo-hash-table.h"
#include "zone/zone-contents.h"
+/*! \brief Adaptor for knot_zone_deep_free() */
+static void knot_zone_dtor(struct ref_t *p) {
+ knot_zone_t *z = (knot_zone_t *)p;
+ knot_zone_deep_free(&z, 0);
+}
+
/*----------------------------------------------------------------------------*/
/* API functions */
/*----------------------------------------------------------------------------*/
@@ -55,6 +61,13 @@ knot_zone_t *knot_zone_new_empty(knot_dname_t *name)
// save the zone name
dbg_zone("Setting zone name.\n");
zone->name = name;
+
+ /* Initialize reference counting. */
+ ref_init(&zone->ref, knot_zone_dtor);
+
+ /* Set reference counter to 1, caller should release it after use. */
+ knot_zone_retain(zone);
+
return zone;
}
@@ -133,14 +146,18 @@ void knot_zone_set_version(knot_zone_t *zone, time_t version)
short knot_zone_is_master(const knot_zone_t *zone)
{
- return zone->master;
+ return zone->flags & KNOT_ZONE_MASTER;
}
/*----------------------------------------------------------------------------*/
void knot_zone_set_master(knot_zone_t *zone, short master)
{
- zone->master = master;
+ if (master) {
+ zone->flags |= KNOT_ZONE_MASTER;
+ } else {
+ zone->flags &= ~KNOT_ZONE_MASTER;
+ }
}
/*----------------------------------------------------------------------------*/
@@ -175,6 +192,7 @@ knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone,
knot_zone_contents_t *old_contents =
rcu_xchg_pointer(&zone->contents, new_contents);
+
return old_contents;
}
@@ -192,7 +210,7 @@ void knot_zone_free(knot_zone_t **zone)
&& !knot_zone_contents_gen_is_old((*zone)->contents)) {
// zone is in the middle of an update, report
dbg_zone("Destroying zone that is in the middle of an "
- "update.\n");
+ "update.\n");
}
knot_dname_release((*zone)->name);
@@ -221,7 +239,7 @@ void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table)
&& !knot_zone_contents_gen_is_old((*zone)->contents)) {
// zone is in the middle of an update, report
dbg_zone("Destroying zone that is in the middle of an "
- "update.\n");
+ "update.\n");
}
dbg_zone_exec(
@@ -241,3 +259,21 @@ dbg_zone_exec(
free(*zone);
*zone = NULL;
}
+
+void knot_zone_set_dtor(knot_zone_t *zone, int (*dtor)(struct knot_zone *))
+{
+ if (zone != NULL) {
+ zone->dtor = dtor;
+ }
+}
+
+void knot_zone_set_flag(knot_zone_t *zone, knot_zone_flag_t flag, unsigned on)
+{
+ if (zone != NULL) {
+ if (on) {
+ zone->flags |= flag;
+ } else {
+ zone->flags &= ~flag;
+ }
+ }
+}
diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h
index 331ef1f..31ff2ac 100644..100755
--- a/src/libknot/zone/zone.h
+++ b/src/libknot/zone/zone.h
@@ -34,6 +34,7 @@
#include "nsec3.h"
#include "zone/dname-table.h"
#include "common/tree.h"
+#include "common/ref.h"
#include "hash/cuckoo-hash-table.h"
#include "zone-tree.h"
@@ -41,11 +42,6 @@
#include "zone/zone-contents.h"
/*----------------------------------------------------------------------------*/
-
-//typedef TREE_HEAD(avl_tree, knot_node) avl_tree_t;
-//struct event_t;
-
-/*----------------------------------------------------------------------------*/
/*!
* \brief Return values for search functions.
*
@@ -58,6 +54,15 @@ enum knot_zone_retvals {
typedef enum knot_zone_retvals knot_zone_retvals_t;
+/*!
+ * \brief Zone flags.
+ */
+typedef enum knot_zone_flag_t {
+ KNOT_ZONE_SLAVE = 0 << 0, /*! Slave zone */
+ KNOT_ZONE_MASTER = 1 << 0, /*! Master zone. */
+ KNOT_ZONE_DISCARDED = 1 << 1 /*! Zone waiting to be discarded. */
+} knot_zone_flag_t;
+
/*----------------------------------------------------------------------------*/
/*!
@@ -68,14 +73,14 @@ typedef enum knot_zone_retvals knot_zone_retvals_t;
* double-free errors when destroying the zone.
*/
struct knot_zone {
+ ref_t ref; /*!< Reference counting. */
knot_dname_t *name;
knot_zone_contents_t *contents;
time_t version;
- /*! \todo Set when loading zone. */
- short master;
+ unsigned flags;
void *data; /*!< Pointer to generic zone-related data. */
int (*dtor)(struct knot_zone *); /*!< Data destructor. */
@@ -88,7 +93,7 @@ typedef struct knot_zone knot_zone_t;
/*!
* \brief Creates new empty DNS zone.
*
- * \notice Zone will be created without contents.
+ * \note Zone will be created without contents.
*
* \param name Zone owner.
*
@@ -152,6 +157,54 @@ void knot_zone_free(knot_zone_t **zone);
*/
void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table);
+/*!
+ * \brief Set destructor and initialize reference counter to 1.
+ *
+ * \param zone Related zone.
+ * \param dtor Destructor.
+ */
+void knot_zone_set_dtor(knot_zone_t *zone, int (*dtor)(struct knot_zone *));
+
+/*!
+ * \brief Increment reference counter for dname.
+ *
+ * \param zone Referenced zone.
+ */
+ static inline void knot_zone_retain(knot_zone_t *zone) {
+ if (zone != NULL) {
+ ref_retain(&zone->ref);
+ }
+}
+
+/*!
+ * \brief Decrement reference counter for dname.
+ *
+ * \param zone Referenced zone.
+ */
+ static inline void knot_zone_release(knot_zone_t *zone) {
+ if (zone != NULL) {
+ ref_release(&zone->ref);
+ }
+}
+
+/*!
+ * \brief Return zone flags.
+ *
+ * \param zone Zone.
+ */
+static inline unsigned knot_zone_flags(knot_zone_t *zone) {
+ return zone->flags;
+}
+
+/*!
+ * \brief Set zone flag.
+ *
+ * \param zone Zone.
+ * \param flag Respected flag.
+ * \param on 1 to set, 0 to unset flag.
+ */
+void knot_zone_set_flag(knot_zone_t *zone, knot_zone_flag_t flag, unsigned on);
+
#endif
/*! @} */
diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c
index a9b6545..43b4489 100644..100755
--- a/src/libknot/zone/zonedb.c
+++ b/src/libknot/zone/zonedb.c
@@ -51,11 +51,11 @@ static int knot_zonedb_compare_zone_names(void *p1, void *p2)
int ret = knot_dname_compare(zone1->name, zone2->name);
-dbg_zonedb_exec(
+dbg_zonedb_exec_detail(
char *name1 = knot_dname_to_str(zone1->name);
char *name2 = knot_dname_to_str(zone2->name);
- dbg_zonedb("Compared names %s and %s, result: %d.\n",
- name1, name2, ret);
+ dbg_zonedb_detail("Compared names %s and %s, result: %d.\n",
+ name1, name2, ret);
free(name1);
free(name2);
);
@@ -64,23 +64,6 @@ dbg_zonedb_exec(
}
/*----------------------------------------------------------------------------*/
-
-//static int knot_zonedb_replace_zone_in_list(void **list_item, void **new_zone)
-//{
-// assert(list_item != NULL);
-// assert(*list_item != NULL);
-// assert(new_zone != NULL);
-// assert(*new_zone != NULL);
-
-// dbg_zonedb("Replacing list item %p with new zone %p\n",
-// *list_item, *new_zone);
-
-// *list_item = *new_zone;
-
-// return 0;
-//}
-
-/*----------------------------------------------------------------------------*/
/* API functions */
/*----------------------------------------------------------------------------*/
@@ -152,63 +135,13 @@ knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db,
// remove the zone from the skip list, but do not destroy it
gen_tree_remove(db->zone_tree, &dummy_zone);
-// if (destroy_zone) {
-// // properly destroy the zone and all its contents
-// knot_zone_deep_free(&z, 0);
-// }
-
db->zone_count--;
- //return KNOT_EOK;
return z;
}
/*----------------------------------------------------------------------------*/
-//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
-// knot_zone_t *zone)
-//{
-// knot_zone_t *z = knot_zonedb_find_zone(db,
-// knot_node_owner(knot_zone_apex(zone)));
-// if (z == NULL) {
-// return NULL;
-// }
-
-// /*! \todo The replace should be atomic!!! */
-
-// dbg_zonedb("Found zone: %p\n", z);
-
-// int ret = skip_remove(db->zones,
-// (void *)knot_node_owner(knot_zone_apex(zone)),
-// NULL, NULL);
-// if (ret != 0) {
-// return NULL;
-// }
-
-// dbg_zonedb("Removed zone, return value: %d\n", ret);
-// dbg_zonedb("Old zone: %p\n", z);
-
-// ret = skip_insert(db->zones,
-// (void *)knot_node_owner(knot_zone_apex(zone)),
-// (void *)zone, NULL);
-
-// dbg_zonedb("Inserted zone, return value: %d\n", ret);
-
-// if (ret != 0) {
-// // return the removed zone back
-// skip_insert(db->zones,
-// (void *)knot_node_owner(knot_zone_apex(z)),
-// (void *)z, NULL);
-// /*! \todo There may be problems and the zone may remain
-// removed. */
-// return NULL;
-// }
-
-// return z;
-//}
-
-/*----------------------------------------------------------------------------*/
-
knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
const knot_dname_t *zone_name)
{
@@ -229,11 +162,7 @@ const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
knot_zone_t dummy_zone;
dummy_zone.name = knot_dname_deep_copy(dname);
void *found = NULL;
-
-// int exact_match = gen_tree_find_less_or_equal(db->zone_tree,
-// &dummy_zone,
-// &found);
-// UNUSED(exact_match);
+
found = gen_tree_find(db->zone_tree, &dummy_zone);
while (found == NULL && knot_dname_label_count(dummy_zone.name) > 0) {
knot_dname_left_chop_no_copy(dummy_zone.name);
@@ -351,54 +280,16 @@ static void delete_zone_from_db(void *node, void *data)
knot_zone_t *zone = (knot_zone_t *)node;
assert(zone);
synchronize_rcu();
- knot_zone_deep_free(&zone, 0);
+ knot_zone_set_flag(zone, KNOT_ZONE_DISCARDED, 1);
+ knot_zone_release(zone);
+ zone = NULL;
}
void knot_zonedb_deep_free(knot_zonedb_t **db)
{
dbg_zonedb("Deleting zone db (%p).\n", *db);
-// dbg_zonedb("Is it empty (%p)? %s\n",
-// (*db)->zones, skip_is_empty((*db)->zones) ? "yes" : "no");
-
-//dbg_zonedb_exec(
-// int i = 1;
-// char *name = NULL;
-// while (zn != NULL) {
-// dbg_zonedb("%d. zone: %p, key: %p\n", i, zn->value,
-// zn->key);
-// assert(zn->key == ((knot_zone_t *)zn->value)->apex->owner);
-// name = knot_dname_to_str((knot_dname_t *)zn->key);
-// dbg_zonedb(" zone name: %s\n", name);
-// free(name);
-
-// zn = skip_next(zn);
-// }
-
-// zn = skip_first((*db)->zones);
-//);
-
-// while (zn != NULL) {
-// zone = (knot_zone_t *)zn->value;
-// assert(zone != NULL);
-
-// // remove the zone from the database
-// skip_remove((*db)->zones, zn->key, NULL, NULL);
-// // wait for all readers to finish
-// synchronize_rcu;
-// // destroy the zone
-// knot_zone_deep_free(&zone, 0);
-
-// zn = skip_first((*db)->zones);
-// }
-
-// assert(skip_is_empty((*db)->zones));
-
-// skip_destroy_list(&(*db)->zones, NULL, NULL);
gen_tree_destroy(&((*db)->zone_tree), delete_zone_from_db, NULL);
assert((*db)->zone_tree == NULL);
free(*db);
*db = NULL;
}
-
-/*----------------------------------------------------------------------------*/
-
diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h
index d5a4992..81326bf 100644..100755
--- a/src/libknot/zone/zonedb.h
+++ b/src/libknot/zone/zonedb.h
@@ -85,9 +85,6 @@ int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone);
knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db,
const knot_dname_t *zone_name);
-//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
-// knot_zone_t *zone);
-
/*!
* \brief Finds zone exactly matching the given zone name.
*
diff --git a/src/tests/README b/src/tests/README
index 2f299ad..2f299ad 100644..100755
--- a/src/tests/README
+++ b/src/tests/README
diff --git a/src/tests/common/acl_tests.c b/src/tests/common/acl_tests.c
index c1884cd..c1884cd 100644..100755
--- a/src/tests/common/acl_tests.c
+++ b/src/tests/common/acl_tests.c
diff --git a/src/tests/common/acl_tests.h b/src/tests/common/acl_tests.h
index a928e2d..a928e2d 100644..100755
--- a/src/tests/common/acl_tests.h
+++ b/src/tests/common/acl_tests.h
diff --git a/src/tests/common/da_tests.c b/src/tests/common/da_tests.c
deleted file mode 100644
index 627e8aa..0000000
--- a/src/tests/common/da_tests.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "tests/common/da_tests.h"
-#include "common/dynamic-array.h"
-#include <unistd.h>
-#include <urcu.h>
-
-static int da_tests_count(int argc, char *argv[]);
-static int da_tests_run(int argc, char *argv[]);
-
-/*
- * Unit API.
- */
-unit_api da_tests_api = {
- "Dynamic array",
- &da_tests_count,
- &da_tests_run
-};
-
-/*
- * Unit implementation.
- */
-
-static const int DA_TEST_COUNT = 5;
-static const int RCU_THREADS = 3;
-static const int DA_FRAGMENT = 10;
-static const int DA_DEF_SIZE = 1000;
-static const int DA_OPERATIONS = 1000;
-enum Operations {
- DA_RESERVE = 0,
- DA_OCCUPY = 1,
- DA_RELEASE = 2,
- DA_OPCOUNT = 3
-};
-
-static int da_tests_count(int argc, char *argv[])
-{
- return DA_TEST_COUNT;
-}
-
-static void do_something(int loops)
-{
- int i;
- int res = 1;
-
- static const int LOOPS = 10000;
-
- for (int j = 1; j <= LOOPS; ++j) {
- for (i = 1; i <= loops; ++i) {
- res *= i;
- }
- }
-}
-
-static void *test_rcu_routine(void *obj)
-{
- rcu_register_thread();
- rcu_read_lock();
-
- do_something(1000);
-
- rcu_read_unlock();
- rcu_unregister_thread();
-
- return NULL;
-}
-
-static int test_rcu_threads()
-{
- // Create threads
- pthread_t *threads = malloc(RCU_THREADS * sizeof(pthread_t));
- for (int i = 0; i < RCU_THREADS; ++i) {
- if (pthread_create(&threads[i], NULL, test_rcu_routine, NULL)) {
- diag("rcu: failed to create thread %d", i);
- free(threads);
- return 0;
- }
- }
-
- // Join threads
- void *pret = NULL;
- for (int i = 0; i < RCU_THREADS; ++i) {
- if (pthread_join(threads[i], &pret)) {
- diag("rcu: failed to join thread %d", i);
- free(threads);
- return 0;
- }
- }
-
- synchronize_rcu();
- free(threads);
-
- return 1;
-}
-
-static int test_da_init(da_array_t *arr)
-{
- return da_initialize(arr, DA_DEF_SIZE, sizeof(uint)) == 0;
-}
-
-static int test_da_random_op(da_array_t *arr)
-{
- unsigned seed = (unsigned)time(0);
- uint allocated = DA_DEF_SIZE;
- uint size = 0;
-
- for (int i = 0; i < DA_OPERATIONS; ++i) {
- int r = rand_r(&seed) % DA_OPCOUNT;
- int count = rand_r(&seed) % DA_FRAGMENT + 1;
-
- switch (r) {
-
- // Perform reserve operation
- case DA_RESERVE:
- if (da_reserve(arr, count) >= 0 &&
- size <= allocated) {
- if ((allocated - size) < count) {
- allocated *= 2;
- }
- } else {
- diag("dynamic-array: da_reserve(%p, %d) failed"
- " (size %d, alloc'd %d)",
- arr, count, size, allocated);
- return 0;
- }
- break;
-
- // Perform occupy operation
- case DA_OCCUPY:
- if (da_occupy(arr, count) == 0) {
- uint *items = (uint *) da_get_items(arr);
- for (int j = 0; j < da_get_count(arr); ++j) {
- items[j] = rand_r(&seed);
- }
- if (size <= allocated &&
- (allocated - size) >= count) {
- size += count;
- } else {
- return 0;
- }
- } else {
- diag("dynamic-array: da_occupy(%p, %d) failed"
- " (size %d, alloc'd %d)",
- arr, count, size, allocated);
- return 0;
- }
- break;
-
- // Perform release operation
- case DA_RELEASE:
- if (arr->count > 0) {
- count = (rand_r(&seed) % DA_FRAGMENT) % arr->count;
- da_release(arr, count);
-
- if (size <= allocated && size >= count) {
- size -= count;
- } else {
- return 0;
- }
- }
- break;
-
- default:
- break;
- }
-
- // Check allocated / size
- if (allocated != arr->allocated || size != arr->count) {
- diag("dynamic-array: allocated memory %d (expected %d)"
- " size %d (expected %d) mismatch",
- arr->allocated, allocated, arr->count, size);
- return 0;
- }
- }
-
- return 1;
-}
-
-void *test_da_read(void *obj)
-{
- rcu_register_thread();
- rcu_read_lock();
-
- unsigned seed = (unsigned)time(0);
- da_array_t *arr = (da_array_t *) obj;
- int index = rand_r(&seed) % da_get_count(arr);
-
- note(" dynamic-array: read thread");
- note(" read thread: saving pointer to %d. item", index);
- uint *item = &((uint *) da_get_items(arr))[index];
- note(" read thread: before: pointer: %p item: %u", item, *item);
-
- do_something(100000);
-
- note(" read thread after: pointer: %p item: %u", item, *item);
- rcu_read_unlock();
- note(" read thread unlocked: pointer: %p item: %u", item, *item);
-
- do_something(10000);
-
- note(" read thread: now the item should be deallocated");
- //note(" read thread: pointer: %p item: %u", item, *item);
-
- rcu_unregister_thread();
-
- return NULL;
-}
-
-static int test_da_resize_holding(da_array_t *arr)
-{
- int ret = 1;
- rcu_register_thread();
- pthread_t reader;
-
- // Create thread for reading
- note("dynamic-array: creating read threads");
- if (pthread_create(&reader, NULL, test_da_read, (void *)arr)) {
- diag("dynamic-array: failed to create reading thread",
- __func__);
- rcu_unregister_thread();
- return 0;
- }
-
- // Wait some time, so the other thread gets the item for reading
- do_something(5000);
-
- // Force resize
- note(" dynamic-array: array resized");
- if (da_reserve(arr, arr->allocated - arr->count + 1) == -1) {
- diag("dynamic-array: da_reserve(%p, %d) failed", arr,
- arr->allocated - arr->count + 1);
- ret = 0;
- }
-
- //Wait for the thread to finish
- void *pret = NULL;
- if (pthread_join(reader, &pret)) {
- diag("dynamic-array: failed to join reading thread",
- __func__);
- ret = 0;
- }
-
- rcu_unregister_thread();
- return ret;
-}
-
-static int test_da_resize(da_array_t *arr)
-{
- unsigned seed = (unsigned)time(0);
- int orig_count = da_get_count(arr);
- note("dynamic-array: allocated: %d, items: %d", arr->allocated,
- orig_count);
- // store the items currently in the array
- int *items = (int *)malloc(orig_count * sizeof(int));
- for (int i = 0; i < orig_count; ++i) {
- items[i] = ((int *)da_get_items(arr))[i];
- }
-
- // force resize
- int res = 0;
- while ((res = da_reserve(arr, 10)) == 0) {
- int i = da_get_count(arr);
- da_occupy(arr, 10);
- for (; i < da_get_count(arr); ++i) {
- ((int *)da_get_items(arr))[i] = rand_r(&seed);
- }
- }
-
- if (res < 0) {
- diag("dynamic-array: failed to reserve space");
- return 0;
- }
-
- int errors = 0;
- for (int i = 0; i < orig_count; ++i) {
- if (items[i] != ((int *)da_get_items(arr))[i]) {
- diag("dynamic-array: Wrong item on position %d."
- "Should be: %d, "
- "present value: %d", i, items[i],
- ((int *)da_get_items(arr))[i]);
- ++errors;
- }
- }
-
- free(items);
-
- return errors == 0;
-}
-
-static int da_tests_run(int argc, char *argv[])
-{
- // Init
- rcu_init();
- da_array_t array;
-
- // Test 1: test rcu
- ok(test_rcu_threads(), "dynamic-array: rcu tests");
-
- // Test 2: init
- ok(test_da_init(&array), "dynamic-array: init");
-
- // Test 3: reserve/occupy random operations
- ok(test_da_random_op(&array),
- "dynamic-array: randomized reserve/occupy/release");
-
- // Test 4: resizing array while holding an item
- ok(test_da_resize_holding(&array),
- "dynamic-array: resize array while holding an item");
-
- // Test 5: resize
- ok(test_da_resize(&array), "dynamic-array: resize array");
-
- // Cleanup
- da_destroy(&array);
- return 0;
-}
diff --git a/src/tests/common/events_tests.c b/src/tests/common/events_tests.c
index 0acd706..0acd706 100644..100755
--- a/src/tests/common/events_tests.c
+++ b/src/tests/common/events_tests.c
diff --git a/src/tests/common/events_tests.h b/src/tests/common/events_tests.h
index b54b6da..b54b6da 100644..100755
--- a/src/tests/common/events_tests.h
+++ b/src/tests/common/events_tests.h
diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c
index 08e0577..08e0577 100644..100755
--- a/src/tests/common/fdset_tests.c
+++ b/src/tests/common/fdset_tests.c
diff --git a/src/tests/common/fdset_tests.h b/src/tests/common/fdset_tests.h
index d29e1a9..d29e1a9 100644..100755
--- a/src/tests/common/fdset_tests.h
+++ b/src/tests/common/fdset_tests.h
diff --git a/src/tests/common/skiplist_tests.c b/src/tests/common/skiplist_tests.c
index 4fe99ec..4fe99ec 100644..100755
--- a/src/tests/common/skiplist_tests.c
+++ b/src/tests/common/skiplist_tests.c
diff --git a/src/tests/common/skiplist_tests.h b/src/tests/common/skiplist_tests.h
index ff91706..ff91706 100644..100755
--- a/src/tests/common/skiplist_tests.h
+++ b/src/tests/common/skiplist_tests.h
diff --git a/src/tests/common/slab_tests.c b/src/tests/common/slab_tests.c
index f362ca0..5724a23 100644..100755
--- a/src/tests/common/slab_tests.c
+++ b/src/tests/common/slab_tests.c
@@ -23,6 +23,11 @@
#include "common/slab/slab.h"
#include "knot/common.h"
+/*! \brief Type-safe maximum macro. */
+#define SLAB_MAX(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
+
+
/* Explicitly ask for symbols,
* as the constructor and destructor
* aren't created for test modules.
@@ -119,7 +124,7 @@ static int slab_tests_run(int argc, char *argv[])
for(int i = 0; i < alloc_count; ++i) {
double roll = rand() / (double) RAND_MAX;
size_t bsize = roll * 2048;
- bsize = MAX(bsize, 8);
+ bsize = SLAB_MAX(bsize, 8);
if ((ptrs_i == 0) || (roll < 0.6)) {
void* m = slab_alloc_alloc(&alloc, bsize);
if (m == 0) {
diff --git a/src/tests/common/slab_tests.h b/src/tests/common/slab_tests.h
index 4d45fb8..4d45fb8 100644..100755
--- a/src/tests/common/slab_tests.h
+++ b/src/tests/common/slab_tests.h
diff --git a/src/tests/files/sample_conf b/src/tests/files/sample_conf
index b15fce5..b15fce5 100644..100755
--- a/src/tests/files/sample_conf
+++ b/src/tests/files/sample_conf
diff --git a/src/tests/knot/conf_tests.c b/src/tests/knot/conf_tests.c
index 61520ea..61520ea 100644..100755
--- a/src/tests/knot/conf_tests.c
+++ b/src/tests/knot/conf_tests.c
diff --git a/src/tests/knot/conf_tests.h b/src/tests/knot/conf_tests.h
index dfd2fd7..dfd2fd7 100644..100755
--- a/src/tests/knot/conf_tests.h
+++ b/src/tests/knot/conf_tests.h
diff --git a/src/tests/knot/dthreads_tests.c b/src/tests/knot/dthreads_tests.c
index 982329b..982329b 100644..100755
--- a/src/tests/knot/dthreads_tests.c
+++ b/src/tests/knot/dthreads_tests.c
diff --git a/src/tests/knot/dthreads_tests.h b/src/tests/knot/dthreads_tests.h
index e41bdc5..e41bdc5 100644..100755
--- a/src/tests/knot/dthreads_tests.h
+++ b/src/tests/knot/dthreads_tests.h
diff --git a/src/tests/knot/journal_tests.c b/src/tests/knot/journal_tests.c
index 7c8125e..5c89de9 100644..100755
--- a/src/tests/knot/journal_tests.c
+++ b/src/tests/knot/journal_tests.c
@@ -34,7 +34,7 @@ unit_api journal_tests_api = {
/*
* Unit implementation.
*/
-static const int JOURNAL_TEST_COUNT = 20;
+static const int JOURNAL_TEST_COUNT = 21;
/*! \brief Generate random string with given length. */
static int randstr(char* dst, size_t len)
@@ -76,6 +76,7 @@ static int journal_tests_run(int argc, char *argv[])
int tmp_fd = mkstemp(jfn_buf);
ok(tmp_fd >= 0, "journal: create temporary file");
skip(tmp_fd < 0, JOURNAL_TEST_COUNT - 1);
+ close(tmp_fd);
/* Test 2: Create journal. */
const char *jfilename = jfn_buf;
@@ -139,7 +140,9 @@ static int journal_tests_run(int argc, char *argv[])
journal_close(journal);
/* Recreate journal = NORMAL mode. */
- remove(jfilename);
+ if (remove(jfilename) < 0) {
+ diag("journal: couldn't remove filename");
+ }
fsize = 8092;
jsize = 512;
ret = journal_create(jfilename, jsize);
@@ -199,7 +202,7 @@ static int journal_tests_run(int argc, char *argv[])
ok(j && ret == 0, "journal: mapped entry data integrity check");
endskip;
- /* Test 17: Make a transaction. */
+ /* Test 16: Make a transaction. */
uint64_t tskey = 0x75750000;
ret = journal_trans_begin(j);
ok(j && ret == 0, "journal: TRANS begin");
@@ -208,16 +211,16 @@ static int journal_tests_run(int argc, char *argv[])
journal_write(j, tskey + i, tmpbuf, sizeof(tmpbuf));
}
- /* Test 18: Check if uncommited node exists. */
+ /* Test 17: Check if uncommited node exists. */
ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret != 0, "journal: check for uncommited node");
- /* Test 19: Commit transaction. */
+ /* Test 18: Commit transaction. */
ret = journal_trans_commit(j);
int read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret == 0 && read_ret == 0, "journal: transaction commit");
- /* Test 20: Rollback transaction. */
+ /* Test 19: Rollback transaction. */
tskey = 0x6B6B0000;
journal_trans_begin(j);
for (int i = 0; i < 16; ++i) {
@@ -228,7 +231,7 @@ static int journal_tests_run(int argc, char *argv[])
read_ret = journal_read(j, tskey + rand() % 16, NULL, chk_buf);
ok(j && ret == 0 && read_ret != 0, "journal: transaction rollback");
- /* Test 16: Write random data. */
+ /* Test 20: Write random data. */
ret = 0;
for (int i = 0; i < 512; ++i) {
int key = i;
@@ -258,16 +261,17 @@ static int journal_tests_run(int argc, char *argv[])
break;
}
}
-
- /* Test 16: Check data integrity. */
ok(j && ret == 0, "journal: sustained mmap r/w");
+ /* Test 21: Open + create journal. */
+ journal_close(j);
+ remove(jfilename);
+ j = journal_open(jfilename, fsize, 0, 0);
+ ok(j != NULL, "journal: open+create from scratch");
+
/* Close journal. */
journal_close(j);
- /* Close temporary file fd. */
- close(tmp_fd);
-
/* Delete journal. */
remove(jfilename);
diff --git a/src/tests/knot/journal_tests.h b/src/tests/knot/journal_tests.h
index beec8ca..beec8ca 100644..100755
--- a/src/tests/knot/journal_tests.h
+++ b/src/tests/knot/journal_tests.h
diff --git a/src/tests/knot/server_tests.c b/src/tests/knot/server_tests.c
index 5ae04d8..5ae04d8 100644..100755
--- a/src/tests/knot/server_tests.c
+++ b/src/tests/knot/server_tests.c
diff --git a/src/tests/knot/server_tests.h b/src/tests/knot/server_tests.h
index 43ad0c1..43ad0c1 100644..100755
--- a/src/tests/knot/server_tests.h
+++ b/src/tests/knot/server_tests.h
diff --git a/src/tests/libknot/files/parsed_data b/src/tests/libknot/files/parsed_data
index 4027c92..4027c92 100644..100755
--- a/src/tests/libknot/files/parsed_data
+++ b/src/tests/libknot/files/parsed_data
Binary files differ
diff --git a/src/tests/libknot/files/parsed_data_queries b/src/tests/libknot/files/parsed_data_queries
index 5857c87..5857c87 100644..100755
--- a/src/tests/libknot/files/parsed_data_queries
+++ b/src/tests/libknot/files/parsed_data_queries
Binary files differ
diff --git a/src/tests/libknot/files/raw_data b/src/tests/libknot/files/raw_data
index f94236b..f94236b 100644..100755
--- a/src/tests/libknot/files/raw_data
+++ b/src/tests/libknot/files/raw_data
Binary files differ
diff --git a/src/tests/libknot/files/raw_data_queries b/src/tests/libknot/files/raw_data_queries
index 9062d5a..9062d5a 100644..100755
--- a/src/tests/libknot/files/raw_data_queries
+++ b/src/tests/libknot/files/raw_data_queries
Binary files differ
diff --git a/src/tests/libknot/libknot/cuckoo_tests.c b/src/tests/libknot/libknot/cuckoo_tests.c
index c1306a3..8d22e36 100644..100755
--- a/src/tests/libknot/libknot/cuckoo_tests.c
+++ b/src/tests/libknot/libknot/cuckoo_tests.c
@@ -15,6 +15,7 @@
*/
#include <time.h>
#include <assert.h>
+#include <string.h>
#include "tests/libknot/libknot/cuckoo_tests.h"
diff --git a/src/tests/libknot/libknot/cuckoo_tests.h b/src/tests/libknot/libknot/cuckoo_tests.h
index b6b0db8..b6b0db8 100644..100755
--- a/src/tests/libknot/libknot/cuckoo_tests.h
+++ b/src/tests/libknot/libknot/cuckoo_tests.h
diff --git a/src/tests/libknot/libknot/dname_table_tests.c b/src/tests/libknot/libknot/dname_table_tests.c
index 0d00a44..0d00a44 100644..100755
--- a/src/tests/libknot/libknot/dname_table_tests.c
+++ b/src/tests/libknot/libknot/dname_table_tests.c
diff --git a/src/tests/libknot/libknot/dname_table_tests.h b/src/tests/libknot/libknot/dname_table_tests.h
index f3088e9..f3088e9 100644..100755
--- a/src/tests/libknot/libknot/dname_table_tests.h
+++ b/src/tests/libknot/libknot/dname_table_tests.h
diff --git a/src/tests/libknot/libknot/dname_tests.c b/src/tests/libknot/libknot/dname_tests.c
index 35ac230..35ac230 100644..100755
--- a/src/tests/libknot/libknot/dname_tests.c
+++ b/src/tests/libknot/libknot/dname_tests.c
diff --git a/src/tests/libknot/libknot/dname_tests.h b/src/tests/libknot/libknot/dname_tests.h
index a7d75aa..a7d75aa 100644..100755
--- a/src/tests/libknot/libknot/dname_tests.h
+++ b/src/tests/libknot/libknot/dname_tests.h
diff --git a/src/tests/libknot/libknot/edns_tests.c b/src/tests/libknot/libknot/edns_tests.c
index ac5d130..4d1a37a 100644..100755
--- a/src/tests/libknot/libknot/edns_tests.c
+++ b/src/tests/libknot/libknot/edns_tests.c
@@ -495,12 +495,13 @@ static int test_edns_has_option()
*/
for (int i = 0; i < TEST_EDNS; i++) {
knot_opt_rr_t *edns = knot_edns_new();
- assert(edns->option_count == 0);
if (edns == NULL) {
ERR_ALLOC_FAILED;
return 0;
}
+
+ assert(edns->option_count == 0);
for (int j = 0; j < test_edns_data[i].option_count; j++) {
if (knot_edns_add_option(edns,
diff --git a/src/tests/libknot/libknot/edns_tests.h b/src/tests/libknot/libknot/edns_tests.h
index 4553234..4553234 100644..100755
--- a/src/tests/libknot/libknot/edns_tests.h
+++ b/src/tests/libknot/libknot/edns_tests.h
diff --git a/src/tests/libknot/libknot/node_tests.c b/src/tests/libknot/libknot/node_tests.c
index 10b01fa..b252982 100644..100755
--- a/src/tests/libknot/libknot/node_tests.c
+++ b/src/tests/libknot/libknot/node_tests.c
@@ -76,7 +76,7 @@ static int test_node_create()
errors++;
diag("Failed to create node structure");
}
- knot_node_free(&tmp, 0);
+ knot_node_free(&tmp);
}
return (errors == 0);
}
@@ -134,7 +134,7 @@ static int test_node_add_rrset()
diag("Values in found rrset are wrong");
}
- knot_node_free(&tmp, 0);
+ knot_node_free(&tmp);
}
return (errors == 0);
@@ -166,7 +166,7 @@ static int test_node_get_rrset()
diag("Failed to get proper rrset from node");
}
}
- knot_node_free(&nodes[i], 0);
+ knot_node_free(&nodes[i]);
}
return (errors == 0);
@@ -194,7 +194,7 @@ static int test_node_get_parent()
errors++;
diag("Failed to get proper parent from node");
}
- knot_node_free(&nodes[i], 0);
+ knot_node_free(&nodes[i]);
}
return (errors == 0);
}
@@ -230,7 +230,7 @@ static int test_node_sorting()
// last = *((uint16_t *)node->key);
// }
- knot_node_free(&tmp, 0);
+ knot_node_free(&tmp);
return (errors == 0);
}
@@ -244,7 +244,7 @@ static int test_node_delete()
tmp_node = knot_node_new(&test_nodes[i].owner,
test_nodes[i].parent, 0);
- knot_node_free(&tmp_node, 0);
+ knot_node_free(&tmp_node);
errors += (tmp_node != NULL);
}
@@ -269,9 +269,9 @@ static int test_node_set_parent()
diag("Parent node is wrongly set.");
errors++;
}
- knot_node_free(&tmp_node, 0);
+ knot_node_free(&tmp_node);
}
- knot_node_free(&tmp_parent, 0);
+ knot_node_free(&tmp_parent);
return (errors == 0);
}
@@ -289,7 +289,7 @@ static int test_node_free_rrsets()
// errors += (tmp_node->rrsets != NULL);
- knot_node_free(&tmp_node, 0);
+ knot_node_free(&tmp_node);
}
return (errors == 0);
}
diff --git a/src/tests/libknot/libknot/node_tests.h b/src/tests/libknot/libknot/node_tests.h
index a90179f..a90179f 100644..100755
--- a/src/tests/libknot/libknot/node_tests.h
+++ b/src/tests/libknot/libknot/node_tests.h
diff --git a/src/tests/libknot/libknot/nsec3_tests.c b/src/tests/libknot/libknot/nsec3_tests.c
index 7b95549..7b95549 100644..100755
--- a/src/tests/libknot/libknot/nsec3_tests.c
+++ b/src/tests/libknot/libknot/nsec3_tests.c
diff --git a/src/tests/libknot/libknot/nsec3_tests.h b/src/tests/libknot/libknot/nsec3_tests.h
index 10e7ed9..10e7ed9 100644..100755
--- a/src/tests/libknot/libknot/nsec3_tests.h
+++ b/src/tests/libknot/libknot/nsec3_tests.h
diff --git a/src/tests/libknot/libknot/packet_tests.c b/src/tests/libknot/libknot/packet_tests.c
index 916328d..916328d 100644..100755
--- a/src/tests/libknot/libknot/packet_tests.c
+++ b/src/tests/libknot/libknot/packet_tests.c
diff --git a/src/tests/libknot/libknot/packet_tests.h b/src/tests/libknot/libknot/packet_tests.h
index 5a8ce03..5a8ce03 100644..100755
--- a/src/tests/libknot/libknot/packet_tests.h
+++ b/src/tests/libknot/libknot/packet_tests.h
diff --git a/src/tests/libknot/libknot/query_tests.c b/src/tests/libknot/libknot/query_tests.c
index 1e4e081..1e4e081 100644..100755
--- a/src/tests/libknot/libknot/query_tests.c
+++ b/src/tests/libknot/libknot/query_tests.c
diff --git a/src/tests/libknot/libknot/query_tests.h b/src/tests/libknot/libknot/query_tests.h
index 037ecab..037ecab 100644..100755
--- a/src/tests/libknot/libknot/query_tests.h
+++ b/src/tests/libknot/libknot/query_tests.h
diff --git a/src/tests/libknot/libknot/rdata_tests.c b/src/tests/libknot/libknot/rdata_tests.c
index 0c53613..0c53613 100644..100755
--- a/src/tests/libknot/libknot/rdata_tests.c
+++ b/src/tests/libknot/libknot/rdata_tests.c
diff --git a/src/tests/libknot/libknot/rdata_tests.h b/src/tests/libknot/libknot/rdata_tests.h
index 1f43c91..1f43c91 100644..100755
--- a/src/tests/libknot/libknot/rdata_tests.h
+++ b/src/tests/libknot/libknot/rdata_tests.h
diff --git a/src/tests/libknot/libknot/response_tests.c b/src/tests/libknot/libknot/response_tests.c
index f40bb76..f40bb76 100644..100755
--- a/src/tests/libknot/libknot/response_tests.c
+++ b/src/tests/libknot/libknot/response_tests.c
diff --git a/src/tests/libknot/libknot/response_tests.h b/src/tests/libknot/libknot/response_tests.h
index c9a117b..c9a117b 100644..100755
--- a/src/tests/libknot/libknot/response_tests.h
+++ b/src/tests/libknot/libknot/response_tests.h
diff --git a/src/tests/libknot/libknot/rrset_tests.c b/src/tests/libknot/libknot/rrset_tests.c
index 41284df..41284df 100644..100755
--- a/src/tests/libknot/libknot/rrset_tests.c
+++ b/src/tests/libknot/libknot/rrset_tests.c
diff --git a/src/tests/libknot/libknot/rrset_tests.h b/src/tests/libknot/libknot/rrset_tests.h
index b0787d6..b0787d6 100644..100755
--- a/src/tests/libknot/libknot/rrset_tests.h
+++ b/src/tests/libknot/libknot/rrset_tests.h
diff --git a/src/tests/libknot/libknot/tsig_tests.c b/src/tests/libknot/libknot/tsig_tests.c
index e0a3d34..e0a3d34 100644..100755
--- a/src/tests/libknot/libknot/tsig_tests.c
+++ b/src/tests/libknot/libknot/tsig_tests.c
diff --git a/src/tests/libknot/libknot/tsig_tests.h b/src/tests/libknot/libknot/tsig_tests.h
index 8ea15f6..8ea15f6 100644..100755
--- a/src/tests/libknot/libknot/tsig_tests.h
+++ b/src/tests/libknot/libknot/tsig_tests.h
diff --git a/src/tests/libknot/libknot/zone_tests.c b/src/tests/libknot/libknot/zone_tests.c
index 72aae39..54cd38d 100644..100755
--- a/src/tests/libknot/libknot/zone_tests.c
+++ b/src/tests/libknot/libknot/zone_tests.c
@@ -95,13 +95,13 @@ static int test_zone_create(knot_zone_contents_t **zone)
if ((*zone) == NULL) {
diag("zone: Failed to create zone.");
- knot_node_free(&node, 0);
+ knot_node_free(&node);
return 0;
}
if ((*zone)->apex != node) {
diag("zone: Zone apex not set right.");
- knot_node_free(&node, 0);
+ knot_node_free(&node);
return 0;
}
@@ -133,7 +133,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3)
: knot_zone_contents_add_node(zone, node, 0, 0, 0))) != 0) {
diag("zone: Failed to insert node into zone (returned"
" %d).", res);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
++errors;
}
/* TODO check values in the node as well */
@@ -157,7 +157,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3)
KNOT_EBADZONE);
++errors;
}
- knot_node_free(&node, 0);
+ knot_node_free(&node);
}
//note("NULL zone");
@@ -179,7 +179,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3)
++errors;
}
- knot_node_free(&node, 0);
+ knot_node_free(&node);
//note("NULL node");
note("Inserting NULL node...\n");
@@ -211,7 +211,7 @@ static int test_zone_add_node(knot_zone_contents_t *zone, int nsec3)
++errors;
}
- knot_node_free(&node, 0);
+ knot_node_free(&node);
}
// check if all nodes are inserted
diff --git a/src/tests/libknot/libknot/zone_tests.h b/src/tests/libknot/libknot/zone_tests.h
index 5539709..5539709 100644..100755
--- a/src/tests/libknot/libknot/zone_tests.h
+++ b/src/tests/libknot/libknot/zone_tests.h
diff --git a/src/tests/libknot/libknot/zone_tree_tests.c b/src/tests/libknot/libknot/zone_tree_tests.c
index 207afea..207afea 100644..100755
--- a/src/tests/libknot/libknot/zone_tree_tests.c
+++ b/src/tests/libknot/libknot/zone_tree_tests.c
diff --git a/src/tests/libknot/libknot/zone_tree_tests.h b/src/tests/libknot/libknot/zone_tree_tests.h
index 4cea88c..4cea88c 100644..100755
--- a/src/tests/libknot/libknot/zone_tree_tests.h
+++ b/src/tests/libknot/libknot/zone_tree_tests.h
diff --git a/src/tests/libknot/libknot/zonedb_tests.c b/src/tests/libknot/libknot/zonedb_tests.c
index 7b45587..7b45587 100644..100755
--- a/src/tests/libknot/libknot/zonedb_tests.c
+++ b/src/tests/libknot/libknot/zonedb_tests.c
diff --git a/src/tests/libknot/libknot/zonedb_tests.h b/src/tests/libknot/libknot/zonedb_tests.h
index 0c4f8ef..0c4f8ef 100644..100755
--- a/src/tests/libknot/libknot/zonedb_tests.h
+++ b/src/tests/libknot/libknot/zonedb_tests.h
diff --git a/src/tests/libknot/realdata/files/parsed_data b/src/tests/libknot/realdata/files/parsed_data
index fe22b90..fe22b90 100644..100755
--- a/src/tests/libknot/realdata/files/parsed_data
+++ b/src/tests/libknot/realdata/files/parsed_data
Binary files differ
diff --git a/src/tests/libknot/realdata/files/parsed_data_queries b/src/tests/libknot/realdata/files/parsed_data_queries
index 5857c87..5857c87 100644..100755
--- a/src/tests/libknot/realdata/files/parsed_data_queries
+++ b/src/tests/libknot/realdata/files/parsed_data_queries
Binary files differ
diff --git a/src/tests/libknot/realdata/files/raw_data b/src/tests/libknot/realdata/files/raw_data
index 502005e..502005e 100644..100755
--- a/src/tests/libknot/realdata/files/raw_data
+++ b/src/tests/libknot/realdata/files/raw_data
Binary files differ
diff --git a/src/tests/libknot/realdata/files/raw_data_queries b/src/tests/libknot/realdata/files/raw_data_queries
index 9062d5a..9062d5a 100644..100755
--- a/src/tests/libknot/realdata/files/raw_data_queries
+++ b/src/tests/libknot/realdata/files/raw_data_queries
Binary files differ
diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.c b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c
index d0216c7..d0216c7 100644..100755
--- a/src/tests/libknot/realdata/libknot/dname_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/dname_tests_realdata.h b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h
index a7d75aa..a7d75aa 100644..100755
--- a/src/tests/libknot/realdata/libknot/dname_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/dname_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.c b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c
index 257d480..257d480 100644..100755
--- a/src/tests/libknot/realdata/libknot/edns_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/edns_tests_realdata.h b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h
index cfa64b0..cfa64b0 100644..100755
--- a/src/tests/libknot/realdata/libknot/edns_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/edns_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.c b/src/tests/libknot/realdata/libknot/node_tests_realdata.c
index b9415ef..91209c9 100644..100755
--- a/src/tests/libknot/realdata/libknot/node_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.c
@@ -94,7 +94,7 @@ static int test_node_create(const list *node_list)
errors++;
diag("Failed to create node structure");
}
- knot_node_free(&tmp, 0);
+ knot_node_free(&tmp);
}
return (errors == 0);
@@ -168,7 +168,7 @@ static int test_node_add_rrset(list *rrset_list)
diag("Values in found rrset are wrong");
}
- knot_node_free(&tmp, 0);
+ knot_node_free(&tmp);
}
return (errors == 0);
diff --git a/src/tests/libknot/realdata/libknot/node_tests_realdata.h b/src/tests/libknot/realdata/libknot/node_tests_realdata.h
index a90179f..a90179f 100644..100755
--- a/src/tests/libknot/realdata/libknot/node_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/node_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.c b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c
index 08c0882..08c0882 100644..100755
--- a/src/tests/libknot/realdata/libknot/packet_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/packet_tests_realdata.h b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h
index c0e0479..c0e0479 100644..100755
--- a/src/tests/libknot/realdata/libknot/packet_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/packet_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c
index f4ba64c..f4ba64c 100644..100755
--- a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h
index 570b2b1..570b2b1 100644..100755
--- a/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/rdata_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.c b/src/tests/libknot/realdata/libknot/response_tests_realdata.c
index 7dac341..7dac341 100644..100755
--- a/src/tests/libknot/realdata/libknot/response_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/response_tests_realdata.h b/src/tests/libknot/realdata/libknot/response_tests_realdata.h
index 731604b..731604b 100644..100755
--- a/src/tests/libknot/realdata/libknot/response_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/response_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c
index cb59f4c..cb59f4c 100644..100755
--- a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h
index cc3b705..cc3b705 100644..100755
--- a/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/rrset_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.c b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c
index 2738dab..4687978 100644..100755
--- a/src/tests/libknot/realdata/libknot/zone_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.c
@@ -77,7 +77,7 @@ static int test_zone_create(list node_list)
errors++;
}
knot_node_free_rrsets(node, 1);
- knot_node_free(&node, 0);
+ knot_node_free(&node);
}
return (errors == 0);
diff --git a/src/tests/libknot/realdata/libknot/zone_tests_realdata.h b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h
index 5539709..5539709 100644..100755
--- a/src/tests/libknot/realdata/libknot/zone_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/zone_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c
index 96d1517..96d1517 100644..100755
--- a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c
+++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.c
diff --git a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h
index 0c4f8ef..0c4f8ef 100644..100755
--- a/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h
+++ b/src/tests/libknot/realdata/libknot/zonedb_tests_realdata.h
diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
index e972855..e972855 100644..100755
--- a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
+++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.h b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h
index 8f57944..8f57944 100644..100755
--- a/src/tests/libknot/realdata/libknot_tests_loader_realdata.h
+++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.h
diff --git a/src/tests/libknot/realdata/unittests_libknot_realdata.c b/src/tests/libknot/realdata/unittests_libknot_realdata.c
index e557c43..e557c43 100644..100755
--- a/src/tests/libknot/realdata/unittests_libknot_realdata.c
+++ b/src/tests/libknot/realdata/unittests_libknot_realdata.c
diff --git a/src/tests/libknot/unittests_libknot.c b/src/tests/libknot/unittests_libknot.c
index d522e1d..d522e1d 100644..100755
--- a/src/tests/libknot/unittests_libknot.c
+++ b/src/tests/libknot/unittests_libknot.c
diff --git a/src/tests/unittests_main.c b/src/tests/unittests_main.c
index 1ec336a..21eae14 100644..100755
--- a/src/tests/unittests_main.c
+++ b/src/tests/unittests_main.c
@@ -22,7 +22,6 @@
#include "tests/common/slab_tests.h"
#include "tests/common/skiplist_tests.h"
#include "tests/common/events_tests.h"
-#include "tests/common/da_tests.h"
#include "tests/common/acl_tests.h"
#include "tests/common/fdset_tests.h"
#include "tests/knot/dthreads_tests.h"
@@ -47,7 +46,6 @@ int main(int argc, char *argv[])
&skiplist_tests_api, //! Skip list unit
&dthreads_tests_api, //! DThreads testing unit
&events_tests_api, //! Events testing unit
- &da_tests_api, //! Dynamic array unit
&acl_tests_api, //! ACLs
&fdset_tests_api, //! FDSET polling wrapper
diff --git a/src/tests/xfr_tests.c b/src/tests/xfr_tests.c
index 5017109..b78678b 100644..100755
--- a/src/tests/xfr_tests.c
+++ b/src/tests/xfr_tests.c
@@ -172,11 +172,11 @@ int main(int argc, char **argv)
server_t *server = server_create();
// Initialize configuration
- conf_read_lock();
+ rcu_read_lock();
conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
conf_add_hook(conf(), CONF_LOG, zones_ns_conf_hook, server->nameserver);
conf_add_hook(conf(), CONF_LOG, server_conf_hook, server);
- conf_read_unlock();
+ rcu_read_unlock();
// Find implicit configuration file
if (!config_fn) {
@@ -215,7 +215,7 @@ int main(int argc, char **argv)
log_server_error("Couldn't open configuration file "
"'%s'.\n", config_fn);
} else {
- log_server_error("Failed to parse configuration '%s'.\n",
+ log_server_error("Failed to load configuration '%s'.\n",
config_fn);
}
server_destroy(&server);
diff --git a/src/tests/xfr_tests.h b/src/tests/xfr_tests.h
index 29de11d..29de11d 100644..100755
--- a/src/tests/xfr_tests.h
+++ b/src/tests/xfr_tests.h
diff --git a/src/zcompile/LICENSE b/src/zcompile/LICENSE
index 55faacf..55faacf 100644..100755
--- a/src/zcompile/LICENSE
+++ b/src/zcompile/LICENSE
diff --git a/src/zcompile/parser-descriptor.c b/src/zcompile/parser-descriptor.c
index b876877..bc3ee16 100644..100755
--- a/src/zcompile/parser-descriptor.c
+++ b/src/zcompile/parser-descriptor.c
@@ -539,3 +539,4 @@ uint16_t parser_rrclass_from_string(const char *name)
return (uint16_t) rrclass;
}
+/*! @} */
diff --git a/src/zcompile/parser-descriptor.h b/src/zcompile/parser-descriptor.h
index 48c6f02..48c6f02 100644..100755
--- a/src/zcompile/parser-descriptor.h
+++ b/src/zcompile/parser-descriptor.h
diff --git a/src/zcompile/parser-util.c b/src/zcompile/parser-util.c
index cfbc624..955a7b0 100644..100755
--- a/src/zcompile/parser-util.c
+++ b/src/zcompile/parser-util.c
@@ -1128,25 +1128,32 @@ static ssize_t rdata_wireformat_to_rdata_atoms(const uint16_t *wireformat,
// break;
case KNOT_RDATA_WF_IPSECGATEWAY:
dbg_rdata("Parsed item is an IPSECGATEWAY address.\n");
- switch (rdata_atom_data(temp_rdatas[1])[0]) {
+ dbg_rdata("Gateway type: %d\n",
+ ((uint8_t *)rdata_atom_data(temp_rdatas[1]))[0]);
+ switch (((uint8_t *)rdata_atom_data(temp_rdatas[1]))[0]) {
/* gateway type */
- default:
case IPSECKEY_NOGATEWAY:
+ dbg_rdata("NOGATEWAY\n");
length = 0;
break;
case IPSECKEY_IP4:
+ dbg_rdata("IPv4\n");
length = 4;
break;
case IPSECKEY_IP6:
+ dbg_rdata("IPv6\n");
length = IP6ADDRLEN;
break;
case IPSECKEY_DNAME:
+ dbg_rdata("DNAME\n");
is_domain = 1;
is_wirestore = 1;
break;
- }
-
- break;
+ default:
+ dbg_rdata("Unknown IPSECKEY gateway!\n");
+ free(temp_rdatas);
+ return -1;
+ } // switch
}
if (is_domain) {
@@ -2473,3 +2480,4 @@ void set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
bits[window][bit / 8] |= (1 << (7 - bit % 8));
}
+/*! @} */
diff --git a/src/zcompile/parser-util.h b/src/zcompile/parser-util.h
index 57258dc..57258dc 100644..100755
--- a/src/zcompile/parser-util.h
+++ b/src/zcompile/parser-util.h
diff --git a/src/zcompile/tests/unittests_zp_main.c b/src/zcompile/tests/unittests_zp_main.c
index 5d8c5e9..5d8c5e9 100644..100755
--- a/src/zcompile/tests/unittests_zp_main.c
+++ b/src/zcompile/tests/unittests_zp_main.c
diff --git a/src/zcompile/tests/zcompile_tests.c b/src/zcompile/tests/zcompile_tests.c
index 5d3dce6..5d3dce6 100644..100755
--- a/src/zcompile/tests/zcompile_tests.c
+++ b/src/zcompile/tests/zcompile_tests.c
diff --git a/src/zcompile/zcompile-error.c b/src/zcompile/zcompile-error.c
index 9357cde..9357cde 100644..100755
--- a/src/zcompile/zcompile-error.c
+++ b/src/zcompile/zcompile-error.c
diff --git a/src/zcompile/zcompile-error.h b/src/zcompile/zcompile-error.h
index c6d999c..c6d999c 100644..100755
--- a/src/zcompile/zcompile-error.h
+++ b/src/zcompile/zcompile-error.h
diff --git a/src/zcompile/zcompile.c b/src/zcompile/zcompile.c
index c4415d4..e2f05e3 100644..100755
--- a/src/zcompile/zcompile.c
+++ b/src/zcompile/zcompile.c
@@ -42,6 +42,7 @@
#include "zcompile/parser-util.h"
#include "zcompile/zcompile-error.h"
#include "knot/zone/zone-dump.h"
+#include "libknot/zone/zone-diff.h"
#include "libknot/libknot.h"
#include "libknot/util/utils.h"
@@ -111,69 +112,6 @@ static void rrset_list_delete(rrset_list_t **head)
dbg_zp("zp: list_delete: List deleleted.\n");
}
-static int find_rrset_for_rrsig_in_zone(knot_zone_contents_t *zone,
- knot_rrset_t *rrsig)
-{
- assert(rrsig != NULL);
- assert(rrsig->rdata->items[0].raw_data);
-
- knot_node_t *tmp_node = NULL;
-
- if (knot_rdata_rrsig_type_covered(knot_rrset_rdata(rrsig))
- != KNOT_RRTYPE_NSEC3) {
- tmp_node = knot_zone_contents_get_node(zone, rrsig->owner);
- } else {
- tmp_node = knot_zone_contents_get_nsec3_node(zone,
- rrsig->owner);
- }
-
- dbg_zp_verb("zp: find_rr_for_sig: Found this node for RRSIG: %p.\n",
- tmp_node);
-
- if (tmp_node == NULL) {
- dbg_zp("zp: find_rr_for_sig: There is no node for this RR.\n");
- return KNOTDZCOMPILE_EINVAL;
- }
-
- knot_rrset_t *tmp_rrset =
- knot_node_get_rrset(tmp_node,
- knot_rdata_rrsig_type_covered(
- rrsig->rdata));
-
- dbg_zp_verb("zp: find_rr_for_sig: Found this RRSet for RRSIG: %p.\n",
- tmp_rrset);
-
- if (tmp_rrset == NULL) {
- dbg_zp("zp: find_rr_for_sig: There is no RRSet "
- "for this RRSIG.\n");
- return KNOTDZCOMPILE_EINVAL;
- }
-
-
- if (tmp_rrset->rrsigs != NULL) {
- int ret = knot_zone_contents_add_rrsigs(zone, rrsig,
- &tmp_rrset, &tmp_node,
- KNOT_RRSET_DUPL_MERGE, 1);
- if (ret != KNOT_EOK) {
- dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n");
- return ret;
- }
- knot_rrset_free(&rrsig);
- } else {
- int ret = knot_zone_contents_add_rrsigs(zone, rrsig,
- &tmp_rrset, &tmp_node,
- KNOT_RRSET_DUPL_SKIP, 1);
- if (ret != KNOT_EOK) {
- dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n");
- return ret;
- }
- }
-
- dbg_zp("zp: find_rr_for_sig: Found node: %p found rrset: %p.\n",
- tmo_node, tmp_rrset);
- return KNOTDZCOMPILE_EOK;
-}
-
static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
knot_node_t *node,
knot_rrset_t *rrsig)
@@ -187,28 +125,51 @@ static int find_rrset_for_rrsig_in_node(knot_zone_contents_t *zone,
knot_rrset_t *tmp_rrset =
knot_node_get_rrset(node, rrsig_type_covered(rrsig));
+ int ret;
+
if (tmp_rrset == NULL) {
dbg_zp("zp: find_rr_for_sig_in_node: Node does not contain "
"RRSet of type %s.\n",
knot_rrtype_to_string(rrsig_type_covered(rrsig)));
- return KNOTDZCOMPILE_EINVAL;
- }
+ tmp_rrset = knot_rrset_new(rrsig->owner,
+ rrsig_type_covered(rrsig),
+ rrsig->rclass,
+ rrsig->ttl);
+ if (tmp_rrset == NULL) {
+ dbg_zp("zp: find_rr_for_sig_in_node: Cannot create "
+ "dummy RRSet.\n");
+ return KNOT_ERROR;
+ }
- if (tmp_rrset->rrsigs != NULL) {
- if (knot_zone_contents_add_rrsigs(zone, rrsig,
- &tmp_rrset, &node,
- KNOT_RRSET_DUPL_MERGE, 1) < 0) {
- dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n");
- return KNOTDZCOMPILE_EINVAL;
+ ret = knot_zone_contents_add_rrset(zone, tmp_rrset, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ assert(ret <= 0);
+ if (ret < 0) {
+ dbg_zp("zp: Failed to add new dummy RRSet to the zone."
+ "\n");
+ return KNOT_ERROR;
}
+ }
+
+ assert(tmp_rrset);
+
+ if (tmp_rrset->ttl != rrsig->ttl) {
+ char *name = knot_dname_to_str(tmp_rrset->owner);
+ assert(name);
+ log_zone_warning("RRSIG owned by: %s cannot be added to "
+ "its RRSet, because their TTLs differ. "
+ "Changing TTL to value=%d.\n",
+ name, tmp_rrset->ttl);
+ free(name);
+ }
+
+ ret = knot_zone_contents_add_rrsigs(zone, rrsig, &tmp_rrset, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret < 0) {
+ dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n");
+ return KNOTDZCOMPILE_EINVAL;
+ } else if (ret > 0) {
knot_rrset_free(&rrsig);
- } else {
- if (knot_zone_contents_add_rrsigs(zone, rrsig,
- &tmp_rrset, &node,
- KNOT_RRSET_DUPL_SKIP, 1) < 0) {
- dbg_zp("zp: find_rr_for_sig: Cannot add RRSIG.\n");
- return KNOTDZCOMPILE_EINVAL;
- }
}
assert(tmp_rrset->rrsigs != NULL);
@@ -244,10 +205,9 @@ static void process_rrsigs_in_node(knot_zone_contents_t *zone,
rrset_list_t *tmp = parser->node_rrsigs;
while (tmp != NULL) {
if (find_rrset_for_rrsig_in_node(zone, node,
- tmp->data) != 0) {
- rrset_list_add(&parser->rrsig_orphans,
- tmp->data);
- parser->rrsig_orphan_count++;
+ tmp->data) != KNOT_EOK) {
+ zc_error_prev_line("Could not add RRSIG to zone!\n");
+ return;
}
tmp = tmp->next;
}
@@ -279,6 +239,7 @@ dbg_zp_exec_detail(
assert(current_rrset->rdata->count == descriptor->length);
}
+
assert(current_rrset->rdata->count > 0);
assert(knot_dname_is_fqdn(current_rrset->owner));
@@ -336,6 +297,7 @@ dbg_zp_exec_detail(
}
if (current_rrset->type == KNOT_RRTYPE_RRSIG) {
+ /*!< \todo Use deep copy function here! */
knot_rrset_t *tmp_rrsig =
knot_rrset_new(current_rrset->owner,
KNOT_RRTYPE_RRSIG,
@@ -458,11 +420,10 @@ dbg_zp_exec_detail(
"add RDATA to RRSet.\n");
return KNOTDZCOMPILE_EBRDATA;
}
-
- /* I chose skip, but there should not really be
- * any rrset to skip */
+
+ /* Selected merge option does not really matter here. */
if (knot_zone_contents_add_rrset(contents, rrset, &node,
- KNOT_RRSET_DUPL_SKIP, 1) < 0) {
+ KNOT_RRSET_DUPL_MERGE, 1) < 0) {
knot_rrset_free(&rrset);
dbg_zp("zp: process_rr: Cannot "
"add RDATA to RRSet.\n");
@@ -472,8 +433,9 @@ dbg_zp_exec_detail(
if (current_rrset->type !=
KNOT_RRTYPE_RRSIG && rrset->ttl !=
current_rrset->ttl) {
- zc_error_prev_line(
- "TTL does not match the TTL of the RRSet");
+ zc_warning_prev_line(
+ "TTL does not match the TTL of the RRSet. "
+ "Changing to %lu.\n", rrset->ttl);
}
if (knot_zone_contents_add_rrset(contents, current_rrset,
@@ -497,31 +459,6 @@ dbg_zp_exec_detail(
return KNOTDZCOMPILE_EOK;
}
-static uint find_rrsets_orphans(knot_zone_contents_t *zone, rrset_list_t
- *head)
-{
- uint found_rrsets = 0;
- while (head != NULL) {
- if (find_rrset_for_rrsig_in_zone(zone, head->data) == 0) {
- found_rrsets += 1;
- dbg_zp("zp: find_orphans: "
- "RRSet succesfully found: owner %s type %s\n",
- knot_dname_to_str(head->data->owner),
- knot_rrtype_to_string(head->data->type));
- }
- else { /* we can throw it away now */
- dbg_zp("zp: find_orphans: "
- "RRSet not found for RRSIG: %s (%s)\n",
- knot_dname_to_str(head->data->owner),
- knot_rrtype_to_string(
- knot_rdata_rrsig_type_covered(head->data->rdata)));
- knot_rrset_free(&head->data);
- }
- head = head->next;
- }
- return found_rrsets;
-}
-
static int zone_open(const char *filename, uint32_t ttl, uint16_t rclass,
knot_node_t *origin, void *scanner, knot_dname_t *origin_from_config)
{
@@ -549,19 +486,23 @@ static int zone_open(const char *filename, uint32_t ttl, uint16_t rclass,
int zone_read(const char *name, const char *zonefile, const char *outfile,
int semantic_checks)
-{
+{
+ if (!outfile) {
+ zc_error_prev_line("Missing output file for '%s'\n",
+ zonefile);
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
dbg_zp("zp: zone_read: Reading zone: %s.\n", zonefile);
/* Check that we can write to outfile. */
- if (outfile != NULL) {
- FILE *f = fopen(outfile, "wb");
- if (f == NULL) {
- fprintf(stderr, "Cannot write zone db to file '%s' (%s).\n",
- outfile, strerror(errno));
- return KNOTDZCOMPILE_EINVAL;
- }
- fclose(f);
+ FILE *f = fopen(outfile, "wb");
+ if (f == NULL) {
+ log_zone_error("Cannot write zone db to file '%s' (%s).\n",
+ outfile, strerror(errno));
+ return KNOTDZCOMPILE_EINVAL;
}
+ fclose(f);
knot_dname_t *dname =
knot_dname_new_from_str(name, strlen(name), NULL);
@@ -570,7 +511,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
}
if (!knot_dname_is_fqdn(dname)) {
- fprintf(stderr, "Error: given zone origin is not FQDN.\n");
+ log_zone_error("Error: given zone origin is not FQDN.\n");
knot_dname_release(dname);
return KNOTDZCOMPILE_EINVAL;
}
@@ -587,7 +528,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
knot_dname_t *origin_from_config =
knot_dname_new_from_str(name, strlen(name), NULL);
if (origin_from_config == NULL) {
- knot_node_free(&origin_node, 0);
+ knot_node_free(&origin_node);
return KNOTDZCOMPILE_ENOMEM;
}
@@ -595,7 +536,7 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
zp_lex_init(&scanner);
if (scanner == NULL) {
knot_dname_release(origin_from_config);
- knot_node_free(&origin_node, 0);
+ knot_node_free(&origin_node);
return KNOTDZCOMPILE_ENOMEM;
}
@@ -608,25 +549,55 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
// knot_node_free(&origin_node, 0);
return KNOTDZCOMPILE_EZONEINVAL;
}
+
+ /* Lock zone file. There should not be any modifications. */
+ struct flock lock;
+ lock.l_type = F_RDLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ lock.l_pid = getpid();
+ if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) {
+ log_zone_error("Cannot obtain zone source file lock (%d).\n",
+ errno);
+ FILE *in_file = (FILE *)zp_get_in(scanner);
+ fclose(in_file);
+ zp_lex_destroy(scanner);
+ knot_dname_release(origin_from_config);
+ return KNOTDZCOMPILE_EINVAL;
+ }
+
+ /* Change lock type to unlock rigth away. */
+ lock.l_type = F_UNLCK;
if (zp_parse(scanner) != 0) {
- /*!< \todo #1676 Implement proper locking. */
+ log_zone_error("Parse failed.\n");
FILE *in_file = (FILE *)zp_get_in(scanner);
fclose(in_file);
- zp_lex_destroy(scanner);
knot_dname_release(origin_from_config);
// knot_node_free(&origin_node, 0);
+ /* Release file lock. */
+ if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) {
+ log_zone_error("Cannot release zone source file "
+ "lock (%d).\n",
+ errno);
+ }
+ zp_lex_destroy(scanner);
return KNOTDZCOMPILE_ESYNT;
}
knot_zone_contents_t *contents =
knot_zone_get_contents(parser->current_zone);
+
+ /* Release file lock. */
+ if (fcntl(fileno(zp_get_in(scanner)), F_SETLK, &lock) == -1) {
+ log_zone_error("Cannot release zone source file lock (%d).\n",
+ errno);
+ }
FILE *in_file = (FILE *)zp_get_in(scanner);
fclose(in_file);
zp_lex_destroy(scanner);
-
- /*!< \todo #1676 Implement proper locking. */
dbg_zp("zp: zone_read: Parse complete for %s.\n",
zonefile);
@@ -650,31 +621,20 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
return KNOTDZCOMPILE_EZONEINVAL;
}
- uint found_orphans;
- found_orphans = find_rrsets_orphans(contents,
- parser->rrsig_orphans);
-
- dbg_zp("zp: zone_read: %u RRSIG orphans found.\n", found_orphans);
-
- rrset_list_delete(&parser->rrsig_orphans);
-
- if (found_orphans != parser->rrsig_orphan_count) {
- /*! \todo This might be desired behaviour. */
- fprintf(stderr,
- "There are unassigned RRSIGs in the zone!\n");
+ int ret = knot_zone_contents_adjust(contents);
+ if (ret != KNOT_EOK) {
+ fprintf(stderr, "Zone could not be adjusted, error: %s.\n",
+ knot_strerror(ret));
parser->errors++;
}
-
- /*! \todo Check return value. */
- knot_zone_contents_adjust(contents);
-
+
dbg_zp("zp: zone_read: Zone adjusted.\n");
if (parser->errors != 0) {
- log_zone_error("Parser finished with %d error(s)%s\n",
- parser->errors, outfile == NULL ?
- "." : ", not dumping the zone!");
- } else if (outfile != NULL) {
+ log_zone_error("Parser finished with %d error(s), "
+ "not dumping the zone!\n",
+ parser->errors);
+ } else {
int fd = open(outfile, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG);
if (fd < 0) {
log_zone_error("Could not open destination file for db: %s.\n",
@@ -688,7 +648,10 @@ int zone_read(const char *name, const char *zonefile, const char *outfile,
if (ret != KNOT_EOK) {
log_zone_error("Could not dump zone, reason: "
"%s.\n", knot_strerror(ret));
- remove(outfile);
+ if (remove(outfile) != 0) {
+ log_zone_error("Could not remove "
+ "db file!\n");
+ }
totalerrors++;
} else {
/* Write CRC file. */
diff --git a/src/zcompile/zcompile.h b/src/zcompile/zcompile.h
index 37b3791..d19ef4c 100644..100755
--- a/src/zcompile/zcompile.h
+++ b/src/zcompile/zcompile.h
@@ -131,18 +131,11 @@ struct zparser {
knot_rrset_t *current_rrset; /*!< Current RRSet. */
knot_rdata_item_t *temporary_items; /*!< Temporary rdata items. */
- /*!
- * \brief list of RRSIGs that were not inside their nodes in zone file
- */
- rrset_list_t *rrsig_orphans;
- unsigned long rrsig_orphan_count; /*!< RRSIG orphan count */
-
knot_dname_t *root_domain; /*!< Root domain name. */
slab_cache_t *parser_slab; /*!< Slab for parser. */
rrset_list_t *node_rrsigs; /*!< List of RRSIGs in current node. */
int rdata_count; /*!< Count of parsed rdata. */
- int origin_directive; /*!< Set to 1 if $ORIGIN directive is present. */
};
typedef struct zparser zparser_type;
diff --git a/src/zcompile/zcompile_main.c b/src/zcompile/zcompile_main.c
index c84515e..983376a 100644..100755
--- a/src/zcompile/zcompile_main.c
+++ b/src/zcompile/zcompile_main.c
@@ -26,7 +26,7 @@ static void help(int argc, char **argv)
{
printf("Usage: %s [parameters] origin zonefile\n",
argv[0]);
- printf("Parameters:\n"
+ printf("\n:Parameters:\n"
" -o <outfile> Override output file.\n"
" -v Verbose mode - additional runtime information.\n"
" -s Enable semantic checks.\n"
@@ -114,7 +114,6 @@ int main(int argc, char **argv)
} else {
log_zone_info("Zone file for '%s' is OK.\n", origin);
}
- //log_close();
return 0;
}
diff --git a/src/zcompile/zlexer.l b/src/zcompile/zlexer.l
index 58e6439..58e6439 100644..100755
--- a/src/zcompile/zlexer.l
+++ b/src/zcompile/zlexer.l
diff --git a/src/zcompile/zparser.y b/src/zcompile/zparser.y
index 89af437..c35060e 100644..100755
--- a/src/zcompile/zparser.y
+++ b/src/zcompile/zparser.y
@@ -314,7 +314,6 @@ origin_directive: DOLLAR_ORIGIN sp abs_dname trail
// knot_node_free(&parser->origin, 1);
}
parser->origin = origin_node;
- parser->origin_directive = 1;
}
| DOLLAR_ORIGIN sp rel_dname trail
{
@@ -406,12 +405,8 @@ abs_dname: '.'
}
| '@'
{
- if (parser->origin_directive) {
- $$ = parser->origin->owner;
- } else {
- zc_error("@ used, but no $ORIGIN specified.\n");
- $$ = parser->origin->owner;
- }
+ assert(parser->origin != NULL);
+ $$ = parser->origin->owner;
}
| rel_dname '.'
{
@@ -1267,13 +1262,13 @@ rdata_apl: rdata_apl_seq trail
rdata_apl_seq: dotted_str
{
- zadd_rdata_wireformat(zparser_conv_apl_rdata($1.str));
+ zadd_rdata_txt_wireformat(zparser_conv_apl_rdata($1.str), 1);
free($1.str);
}
| rdata_apl_seq sp dotted_str
{
- zadd_rdata_wireformat(zparser_conv_apl_rdata($3.str));
+ zadd_rdata_txt_wireformat(zparser_conv_apl_rdata($3.str), 0);
free($3.str);
}
@@ -1637,13 +1632,9 @@ zparser_init(const char *filename, uint32_t ttl, uint16_t rclass,
parser->current_zone = knot_zone_new(origin, 0, 1);
parser->node_rrsigs = NULL;
- parser->rrsig_orphans = NULL;
- parser->rrsig_orphan_count = 0;
parser->current_rrset->rclass = parser->default_class;
parser->current_rrset->rdata = NULL;
-
- parser->origin_directive = 0;
}
diff --git a/tests/querytcp.c b/tests/querytcp.c
index 7e1418f..7e1418f 100644..100755
--- a/tests/querytcp.c
+++ b/tests/querytcp.c