diff --git a/doc/markdown/.gitignore b/doc/markdown/.gitignore new file mode 100644 index 0000000000..2d19fc766d --- /dev/null +++ b/doc/markdown/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/doc/markdown/Makefile b/doc/markdown/Makefile new file mode 100644 index 0000000000..0ac83ae7c2 --- /dev/null +++ b/doc/markdown/Makefile @@ -0,0 +1,21 @@ +# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +all: style.html howto.html dev.html + +%.html: %.mkd + markdown $< > $@ + +clean: + rm style.html howto.html dev.html diff --git a/doc/markdown/dev.mkd b/doc/markdown/dev.mkd new file mode 100644 index 0000000000..4b0f9cd5f1 --- /dev/null +++ b/doc/markdown/dev.mkd @@ -0,0 +1,1490 @@ + +## BIND Developer Information + +### Contents + +1. [The code review process](#reviews) +1. [Testing](#testing) + * [System tests](#systest) + * [Unit tests](#unittest) +1. [BIND system architecture](#arch) + * [Source tree layout](#layout) + * [Design by contract](#dbc) + * [Magic numbers](#magic) + * [Result codes](#results) + * [Memory management](#mem) + * [Lists](#lists) + * [Buffers and regions](#buffers) + * [Names](#names) + * [Iterators](#iterators) + * [Logging](#logging) + * [Adding a new RR type](#rrtype) + * [Task and timer model](#tasks) + +### The code review process + +Every line of code comitted to BIND has been reviewed by ISC engineers +first. + +The code review process is a dialog between the original author and the +reviewer. Code inspection, including documentation and tests, is part of +this. Compiling and running the resulting code should be done in most +cases, even for trivial changes, to ensure that it works as intended. In +particular, a full regression test (`make` `check`) must be run for every +modification so that unexpected side-effects are identified. + +When a problem or concern is found by the reviewer, these comments are +placed on the RT ticket so the author can respond. + +#### What is reviewed: + +First, consideration is given to whether contributed code would +be useful to a significant user base (we can't take on the additional +maintenance and support burden for changes that would only be useful +to a tiny niche). Second, whether the approach taken is consistent +with ISC's open-internet goals, BIND architecture, and DNS best +practices. Third, the contribution is checked for correctness and +completness. + +Obvious bottlenecks and places where performance or reliability may suffer +are noted as part of the review. Tricky code needs to be commented well +enough. A patch is much more likely to be accepted quickly if it includes +tests providing good coverage of the new code. Tests for bugfix code +should fail when run against the unmodified code; tests for new feature +code should have good code coverage and address corner cases and error +cases. (See [testing](#testing).) + +Documentation is also reviewed. This includes all user-facing text, +including log messages, manual pages, user manuals and sometimes even +comments; they must be clearly written and consistent with existing style. + +#### Steps in code review: + +* Read the diff +* Read accompanying notes in the ticket +* Apply the diff to the appropriate branch +* Run `configure` (using at least `--enable-developer --enable-threads --with-atf`) +* Build +* Read the documentation, if any +* Read the tests +* Run the tests +
(In some cases it may be appropriate to run tests against code + from before the change to ensure that they fail as expected.) + +#### Things we look for + +* General correctness of approach +* Style errors +* Simple coding errors +* Files inadvertently omitted +* Unnecessarily complex code +* Complex code with insufficient comments +* Lack of boundary checking +* Memory and resource leaks (deallocations must match allocations) +* Places that need `REQUIRE` or `INSIST` +* Thread safety +* Bad function names/variable names +* Overly long functions +* Copies of code that could be unified in a helper function +* Premature optimizations +* Compiler warnings introduced +* Portability issues: + * Use of non-POSIX library calls or options + * API changes correctly reflected in Windows `*.def` files +* DNS/protocol problems +* Cut/pasted code that may have been modified in one place but needs to be modified in other places as well +* No tests or inadequate tests +* Testability problems +* No documentation or inadequate documentation +* Grammar, spelling and clarity problems in documentation +* Usability problems + +When a patch is contributed which is a good idea but doesn't meet our code +quality requirements, we will often keep the ticket open so that we can +address the issue ourselves later. + +Sometimes contributed code is fine, but ISC staff still have to add +documentation and/or tests -- that's okay, but it may take a long time to +get to the top of our priority list. Ensuring that your patch includes +tests and documentation will reduce delay. + +###
Testing + +#### Running system tests + +To enable system tests to work, we first need to create the test loopback +interfaces (as root): + + $ cd bin/tests/system + $ sudo sh ifconfig.sh up + $ cd ../../.. + +To run the tests, build BIND (be sure to use --with-atf to run unit +tests), then run `make` `check`. An easy way to check the results: + + $ make check 2>&1 | tee /tmp/check.out + $ grep '^R:' /tmp/check.out | sort | uniq -c + +This will show all of the test results. One or two "R:SKIPPED" is okay; if +there are a lot of them, then you probably forgot to create the loopback +interfaces in the previous step. (NOTE: the summary of tests that appears at +the end of `make` `check` only summarizes the system test results, not the +unit tests, so you can't rely on it to catch everything.) + +To run only the system tests, omitting unit tests: + + $ cd bin/tests/system + $ sh runall.sh + +Or, to run an individual system test: + + $ cd bin/tests/system + $ sh run.sh + +System tests are in separate directories under `bin/tests/system`. +For example, the "dnssec" test is in `bin/tests/system/dnssec`. + +#### Writing system tests + +The following standard files are found in system test directories: + +- `prereq.sh`: run at the beginning to determine whether the test can be run at all; if not, we see R:SKIPPED + +- `setup.sh`: sets up the preconditions for the tests + +- `tests.sh`: runs all the test cases. A non-zero return value results in R:FAIL + +- `clean.sh`: run at the end to clean up temporary files, but only if the + test was completed successfully; otherwise the temporary files are left + in place for inspection. + +- `ns[X]`: these subdirectories contain test name servers that can be + queried or can interact with each other. (For example, `ns1` might be + running as a root server, `ns2` as a TLD server, and `ns3` as a recursive + resolver.) The value of X indicates the address the server listens on: + for example, `ns2` listens on 10.53.0.2, and ns4 on 10.53.0.4. All test + servers use port 5300 so they don't need to run as root. All servers + log at the highest debug level, and the logs are captured in the file + `nsX/named.run`. + +- `ans[X]`: like `ns[X]`, but these are simple mock name servers + implemented in perl; they are generally programmed to misbehave in ways + `named` wouldn't, so as to exercise `named`'s ability to interoperate with + badly behaved name servers. Logs, if any, are captured in `ansX/ans.run`. + +All test scripts source the file `bin/tests/system/conf.sh` (which is +generated by `configure` from `conf.sh.in`). This script provides +functions and variables pointing to the binaries under test; for example, +`DIG` contains the path to `dig` in the build tree being tested, `RNDC` +points to `rndc`, `SIGNZONE` to `dnssec-signzone`, etc. + +#### Building unit tests + +BIND uses the Automated Testing Framework (ATF), originally from the NetBSD +project, as its unit testing framework. (Note: ATF has been supplanted by +a newer version called Kyua, but BIND is still using the older system.) + +To build BIND with unit tests, run `configure` with the `--with-atf` +option. This causes the ATF source code in the `unit/atf-src` +subdirectory to be built. + +To save time on repeated builds, you can build and install ATF +in another directory, and configure BIND to use the pre-built +version. (Be sure to disable shared libraries in the ATF build, +and to build the ATF tools; libraries alone are not sufficient). + + $ cd atf-src + $ configure --prefix= --enable-tools --disable-shared + $ make + $ make install + +After this has been done, specify the ATF prefix when building BIND: + + $ configure --with-atf= + +#### Running unit tests + +Unit tests are stored in `/tests` subdirectories under the libraries +they test. For example, the unit tests for libisc are in `lib/isc/tests`. +Particular test sets are called `{module}_test.c`, where {module} is +usually the name of the module being tested; `rbt_test.c` tests functions +in `rbt.c`. (There are exceptions to this rule, though; for instance, +`hash_test.c` tests hash functions that are implemented in several +different files in `lib/isc`.) + +When BIND is built with unit tests, they will be run as part of +`make` `check`. But if you want to run *only* the ATF unit tests: + + $ sh unit/unittest.sh + +You can also run the unit tests for only one library: + + $ cd lib/isc/tests (or lib/dns/tests) + $ make unit + +Or run a particular test case (in the following example, the isc_sha512 +test case in the hash unit test). This has the advantage that you can see +whatever output the unit test emits, whereas in the other modes, output is +redirected: + + $ cd lib/isc/tests + $ hash_test isc_sha512 + +#### Writing unit tests + +Information on writing ATF tests can be found at the +[NetBSD site](http://wiki.netbsd.org/tutorials/atf/). + +Each unit test file contains at least one unit test case, and +a list of test cases to execute when the test is run. These +will look like the following: + + ATF_TC(test_case); + ATF_TC_HEAD(test_case, tc) { + atf_tc_set_md_var(tc, "descr", + "Describe the test case here."); + } + ATF_TC_BODY(serialize_align, tc) { + UNUSED(tc); + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK_EQ(value1, value2); + ATF_CHECK(value1 + value2 < 100); + + isc_test_end(); + } + + /* + * Main + */ + ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, test_case); + + return (atf_no_error()); + } + +If the conditions specified in `ATF_CHECK` and `ATF_CHECK_EQ` +directives are found to be false, then the test case will fail, but it +will continue running to see if there are any more failures. + +If the conditions specified in `ATF_REQUIRE` and `ATF_REQUIRE_EQ` are +found to be false, the test case cannot continue running and will stop + +`isc_test_begin()` and `isc_test_end()` set up necessary preconditions +for checking libisc functions, such as starting a task manger and +creating a memory context. Similar functions `dns_test_begin()` and +`dns_test_end()` are available for testing libdns functions. + +### BIND system architecture + +#### Source tree layout + +* `bind9/bin`: binaries + * `bind9/bin/named`: source code for `named` and `lwresd` binaries; includes server configuration, interface manager, client manger, and high-level processing logic for query, update, and xfer. + * `bind9/bin/dnssec`: DNSSEC-related tools written in C: + `dnssec-keygen`, `dnssec-signzone`, `dnssec-settime`, + `dnssec-revoke`, `dnssec-keyfromlabel`, `dnssec-dsfromkey`, + `dnssec-verify` (BIND 9.9+) + * `bind9/bin/python` (BIND 9.9+): tools written in python. Currently + has `dnssec-checkds` and `dnssec-coverage` + * `bind9/bin/rndc`: `rndc` binary + * `bind9/bin/dig`: `dig`, `host`, and `nslookup` + * `bind9/bin/delv`: `delv` + * `bind9/bin/check`: `named-checkconf` and `named-checkzone` + * `bind9/bin/confgen`: `rndc-confgen`, `ddns-confgen`, and + `tsig-keygen` (BIND 9.9+) + * `bind9/bin/tools`: assorted useful tools: `named-journalprint`, + `nsec3hash`, etc +* `bind9/lib`: libraries + * `bind9/lib/isc`: implements basic functionality such as threads, + tasks, timers, sockets, memory manager, buffers, and basic data types. + * `bind9/lib/isc/tests`: unit tests for libisc + * `bind9/lib/dns`: implements higher-level DNS functionality: + red-black trees, rdatasets, views, zones, ACLs, resolver, validator, etc + * `bind9/lib/dns/tests`: unit tests for libdns + * `bind9/lib/bind9`: library implementing bind9-specific functionality, + principally configuration validity checking (used in `named` and + `named-checkconf` when reading `named.conf`). + * `bind9/lib/isccfg`: library implementing the `named.conf` + configuration parser. + * `bind9/lib/isccc`: library implementing the control channel used + by `rndc` + * `bind9/lib/lwres`: lightweight resolver library. Used very little + in BIND + * `bind9/lib/irs`: provides mechanisms for reading `/etc/resolv.conf` + and other configuration files. Used very little in BIND + +#### Namespace + +See the [namespace](style.html#public_namespace) discussion in the +[BIND coding style](style.html) document. + +#### Design by contract + +BIND uses the "Design by Contract" pattern for most function calls. + +A quick summary of the idea is that a function and its caller make a +contract. If the caller meets certain preconditions, then the function +promises to either fulfill its contract (i.e. guarantee a set of +postconditions), or to clearly fail. + +"Clearly fail" means that if the function cannot succeed, then it will +not silently fail and return a value which the caller might interpret as +success. + +If a caller doesn't meet the preconditions, then "further execution is +undefined". The function can crash, compute a garbage result, fail +silently, etc. Allowing the function to define preconditions greatly +simplifies many APIs, because the API need not specify a way of saying +"hey caller, the values you passed in are garbage". + +Typically, preconditions are specified in the functions .h file, and +encoded in its body with `REQUIRE` statements. The `REQUIRE` statements +cause the program to dump core if they are not true, and can be used to +identify callers that are not meeting their preconditions. + +Postconditions can be encoded with `ENSURE` statements. Within the body of +a function, `INSIST` is used to assert that a particular expression must be +true. + +Assertions must not have side effects that the function relies upon, +because assertion checking may be turned off in some environments. +(This is *not* recommended, however: assertion failures serve the +useful function of ensuring that `named` does not continue running +in an insane state. The surfeit of assertions in BIND 9 have made +it vulnerable over the years to "packets of death" and other +denial-of-service exploits, but as of this writing - more than 14 +years since the initial release - BIND 9 has never had an arbitrary +code execution vulnerability.) + +#### Magic numbers + +A number of data structures in the ISC and DNS libraries have an +`unsigned int magic` value as the first field. The purpose of the +magic number is principally to validate that a pointer that's been +passed to a subroutine really points to the type it claims to be. This +helps detect problems caused by resources being freed prematurely, that +have been corrupted, or that have not been properly initialized. It can +also be handy in debugging. + +Magic numbers should always be the first field in a structure. They never +require locking to access. As to the actual value to be used, something +mnemonic is good: + + #define TASK_MAGIC 0x5441534BU /* TASK. */ + #define VALID_TASK(t) ((t) != NULL && \ + (t)->magic == TASK_MAGIC) + + #define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */ + #define VALID_MANAGER(m) ((m) != NULL && \ + (m)->magic == + TASK_MANAGER_MAGIC) + +Unless the memory cost is critical, most objects should have a magic number. + +The magic number should be the last field set in a creation routine, so that +an object will never be stamped with a magic number until it is valid. + +The magic number should be set to zero immediately before the object is +freed. + +Magic values are usually private to the implementation of the type; +i.e. they are defined in the .c file, not the .h file. There are some +exceptions to this. + +Validation of magic numbers is done by routines that manipulate the type, +not by users of the type. (Indeed, user validation is usually not possible +because the magic number is not public.) + +#### Result codes + +The `isc_result_t` type is provided for function result codes, +and is used throughout BIND. For example: + + isc_result_t result; + FILE *fp = NULL; + + result = isc_stdio_open("file", "r", &fp); + +Note that an explicit result code is used, instead of mixing the error +result type with the normal result type. In contrast to the +C library routine `fopen()` which returns a file pointer or `NULL` +on failure (setting `errno` to indicate what the nature of the problem +was), BIND style always keeps indication of the function's success or +failure separate from its returned data. Similarly, the C library +function `fread()` returns the number of characters read and then +depends on `feof()` and `ferror()` to determine whether an error occured +or the end of file was reached, but BIND's version uses result codes: + + char buffer[BUFSIZ]; + size_t n; + + result = isc_stdio_read(buffer, 1, sizeof(bufer), fp, &n); + if (result == ISC_R_SUCCESS) { + /* Do something with 'buffer'. */ + } else if (result == ISC_R_EOF) { + /* EOF. */ + result = ISC_R_SUCCESS; + } else { + /* Some other error occurred. */ + } + +Only functions which cannot fail (assuming the caller has provided valid +arguments) should return data directly instead of a result code. For +example, `dns_name_issubdomain()` returns an `isc_boolean_t`, because it +has no failure mode. + +A result code can be converted to a human-readable error message by +calling `isc_result_totext(result)`. + +Many result codes have been defined and can be found in the source tree +in header files called `result.h` (for example, the result codes defined +for the ISC library are in `lib/isc/include/isc/result.h`. + +ISC library result codes (many of which are generically useful elsewhere) +begin with `ISC_R`: examples inclue `ISC_R_SUCCESS`, `ISC_R_FAILURE`, +`ISC_R_NOMEMORY`, etc. + +DNS library result codes begin with `DNS_R`: `DNS_R_SERVFAIL`, `DNS_R_NXRRSET`, +etc). Other sets of result codes are defined for crypto functions (`DST_R` +and `PKCS_R`). + +For portability, ISC result codes are used instead of codes provided +by the operating system; for example, `ISC_R_NOMEMORY` instead of +`ENOMEM`. In some cases, but not all, POSIX-defined error codes can be +converted to an ISC result code by calling `isc__errno2result(errno)`. +This can't be relied on; there are too many OS-specific error codes to +provide meaningful translations for all of them. Unknown `errno` values +are converted to `ISC_R_UNEXPECTED`. + +#### Buffers and regions + +A useful set of functions is provided for manipulating memory +buffers: the `isc_buffer` API. Buffers can be used for parsing +and constructing messages in both text and binary formats. + +A buffer is associated with a region of memory, which is subdivided +into 'used' and 'available'. The 'used' subregion is further subdivided +into 'consumed' and 'remaining'. + +When parsing a message, the message to be parsed in in the 'used' +part of the buffer. As the message is parsed, the 'consumed' +subregion grows and the 'remaining' subregion shrinks. + +When creating a message, data is written into the 'available' +subregion, which then becomes part of 'used'. + +The current sizes of these subregions can be determined by calling +`isc_buffer_usedlength()`, `isc_buffer_consumedlength()`, +`isc_buffer_remaininglength()`, and `isc_buffer_availablelength()`. + +The memory associated with a buffer may be dynamically allocated +from a memory context using `isc_buffer_allocate()` and freed by +`isc_buffer_free()`, or it may be a static region of memory +with which we want to use buffer semantics. In that case, we +associate a new buffer object with the desired block of memory +by running `isc_buffer_init()`. If the intention is to write +to the memory, nothing further is necessary; if it is to read +the memory using buffer sementaics, then we must mark the memory +as part of the 'used' subregion: + + isc_buffer_t b; + char text[BUFSIZ]; + unsigned int n; + + result = isc_stdio_read(buf, 1, BUFSIZ, fp, &n); + if (result == ISC_R_SUCCESS && n > 0U) { + isc_buffer_init(&b, text, sizeof(text)); + isc_buffer_add(&b, n); + /* now we can read the buffer */ + } + +Several functions are provided for both reading and writing +to the buffer: + +* `isc_buffer_getuint8`: Read and return an 8-bit unsigned integer +* `isc_buffer_putuint8`: Write an 8-bit unsigned integer to a buffer + +* `isc_buffer_getuint16`: Read a 16-bit unsigned integer in + network byte order, convert to host byte order, and return it +* `isc_buffer_putuint16`: Convert an unsigned 16-bit integer from + host to network byte order and write it to a buffer. + +* `isc_buffer_getuint32`: Read a 32-bit unsigned integer in + network byte order, convert to host byte order, and return it +* `isc_buffer_putuint32`: Convert an unsigned 32-bit integer from + host to network byte order and write it to a buffer. + +* `isc_buffer_putstr()`: Copy a null-terminated string into a buffer +* `isc_buffer_putmem()`: Copy a fixed-length region of memory into a + buffer. + +A simpler set of functions have also been provided for handling +memory regions: the `isc_region` API. A region is a simple structure +that only contains a base pointer (to the beginning of the associated +memory) and a length. Buffers and buffer subregions can be converted to +regions using `isc_buffer_region()`, `isc_buffer_usedregion()`, etc. +Regions can be copied to buffers by using `isc_buffer_copyregion()`, +or simply by running `isc_buffer_init()` on the region's base pointer. + +#### Memory management + +BIND manages its own memory internally via "memory contexts". Multiple +separate memory contexts can be created for the use of different modules or +subcomponents, and each can have its own size limits and tuning parameters +and maintain its own statistics, allocations and free lists. + +The memory system helps with diagnosis of common coding errors such as +memory leaks and use after free. Newly allocated memory is populated with +the repeating value 0xbe, and freed memory with 0xde. BIND tracks every +memory allocation, and will complain (via an assertion failure) if any +memory has not been freed when BIND shuts down. + +To create a basic memory context, use: + + isc_mem_t *mctx = NULL; + result = isc_mem_create(0, 0, &mctx); + +(The zeroes are tuning parameters, `max_size` and `target_size`: Any +allocations smaller than `max_size` will be satisfied by getting +blocks of size `target_size` from the operating system's memory +allocator and breaking them up into pieces, while larger allocations +will call the system allocator directly. These parameters are rarely +used.) + +When holding a persistent reference to a memory context it is advisable to +increment its reference counter using `isc_mem_attach()`. Do not just +copy an `mctx` pointer; this may lead to a shutdown race in which the +memory context is freed before all references have been cleaned up. + + /* + * Function to create an 'isc_foo' object. + */ + isc_result_t + isc_foo_create(isc_mem_t *mctx, isc_foo_t **foop) { + isc_foo_t *foo; + + REQUIRE(mctx != NULL); + REQUIRE(foop != NULL && *foop == NULL); + + foo = isc_mem_get(mctx, sizeof(isc_foo_t)) + if (foo == NULL) + return (ISC_R_NOMEMORY); + + /* Attach to memory context */ + isc_mem_attach(mctx, &foo->mctx); + + /* Populate other isc_foo members here */ + + foo->magic = ISC_FOO_MAGIC; + + *foop = foo; + return (ISC_R_SUCCESS); + } + +When finished with a memory context, detach it with `isc_mem_detach()`. +If freeing an object that contains a reference to a memory context, +you free it and detach its reference at the same time using +`isc_mem_putanddetach()`. + + void + isc_foo_destroy(isc_foo_t **foop) { + isc_foo_t *foo = *foop; + + /* clean up various isc_foo members */ + foo->magic = 0; + + isc_mem_putanddetach(&foo->mctx, foo, sizeof(isc_foo_t)); + + *foop = NULL; + } + +Two sets of allocation and deallocation functions are provided: +`isc_mem_get()` and `isc_mem_put()`; and `isc_mem_allocate()` and +`isc_mem_free()`. + +The call to `isc_mem_put()` must specify the number of bytes being freed, +so use `isc_mem_get()` when the caller can easily keep track of the size of +the allocation. + +A call to `isc_mem_free()` does not need to specify the size of the +allocation, it simply frees whatever was allocated at that address, so use +`isc_mem_allocate()` when use variable size blocks of memory. + +The function `isc_mem_strdup()` -- a version of `strdup()` that uses memory +contexts -- will also return memory that can be freed with +`isc_mem_free()`. + +Every allocation and deallocation requires a memory context lock to be +acquired. This will cause performance problems if you write code that +allocates and deallocates memory frequently. Whenever possible, +inner loop functions should be passed static buffers rather than allocating +memory. + +In cases where small fixed-size blocks of memory may be needed frequently, +the `isc_mempool` API can be used. This creates a standing pool of blocks +of a specified size which can be passed out and returned without the need +for locking the entire memory context. + +#### Lists + +A set of macros are provided for creating, modifying and iterating +doubly-linked lists. These are defined in ``. + +To create a structure that will be part of a linked list, specify +an `ISC_LINK` as one of its members: + + typedef struct isc_foo isc_foo_t; + struct isc_foo { + unsigned int magic; + + /* other contents */ + + ISC_LINK(isc_foo_t) link; + }; + +(Note the `typedef` of `isc_foo_t` prior to the structure declaration.) + +When creating an instance of this structure, initialize the link: + + isc_result_t + isc_foo_create(isc_mem_t mctx, isc_foo_t **foop) { + isc_foo_t *foo; + + REQUIRE(foop != NULL && *foop == NULL); + + foo = isc_mem_get(mctx, sizeof(isc_foo_t)); + if (foo == NULL) + return (ISC_R_NOMEMORY); + + ISC_LINK_INIT(foo, link); + + /* initialize other members */ + + foo->magic = ISC_FOO_MAGIC; + *foop = foo; + return (ISC_R_SUCCESS); + } + +To make a list of these elements, first create a list variable +by declaring it using the `ISC_LIST` macro, then initialize it +with `ISC_LIST_INIT`: + + ISC_LIST(isc_foo_t) foolist; + ISC_LIST_INIT(foolist); + +The list can then be modified: + + ISC_LIST_APPEND(foolist, foo1, link); + +Several macros are provided for this purpose, including `ISC_LIST_PREPEND`, +`ISC_LIST_INSERTBEFORE`, and `ISC_LIST_INSERTAFTER`. + +More macros are provided for iterating the list: + + isc_foo_t *foo; + for (foo = ISC_LIST_HEAD(foolist); + foo != NULL; + foo = ISC_LIST_NEXT(foo, link)) + { + /* do things */ + } + +There are also `ISC_LIST_TAIL` and `ISC_LIST_PREV` macros for walking the +list in reverse order. + +Items can be removed from the list using `ISC_LIST_UNLINK`: + + ISC_LIST_UNLINK(foolist, foo, link); + +A similar but smaller set of `ISC_QUEUE` macros, including `ISC_QUEUE_PUSH` +and `ISC_QUEUE_POP`, are provided to implement strict FIFO lists, with +built-in fine-grained locking. + +#### Names + +The `dns_name` API has facilities for processing DNS names and labels, +both dynamically and statically allocated, relative and absolute, +compressed and not, with straightforward conversions from text to +wire format and vice versa. + +##### Initializing + +When a name object is initialized, a pointer to an "offset table" +(`dns_offsets_t`) may optionally be supplied; this will improve +performance of most name operations if the name is used more than +once. + + dns_name_t name1, name2; + dns_offsets_t offsets1; + + dns_name_init(&name1, &offsets1); + dns_name_init(&name2, NULL); + +##### Copying + +There are two methods for copying name objects: `dns_name_clone()` +makes a target refer to the same data as the source without copying +the data, so the source must not be changed while the target is still in +use. `dns_name_dup()` and `dns_name_dupwithoffsets()` create a true +copy of the name, dynamically allocating memory as needed; targets +created by these must be freed by calling `dns_name_free()`. + +##### Wire format + +To create a name object from a wire format message such as a DNS +query or response, use `dns_name_fromwire()`. Generally this is +done with names in a DNS message object (`dns_message_t`), and some +names may be compressed; the ongoing decompression state for a message +is maintained in a "decompression context" object (`dns_decompress_t`) +which must be initialized before the first call to `dns_name_fromwire()` +for a given message, and passed to each additional call until all +the names have been extracted. + +Similarly, `dns_name_towire()` converts name objects into DNS wire +format, using an ongoing "compression context" object (`dns_compress_t`). + +##### Text format + +Converting text representations of names to name objects is +usually done by calling `dns_name_fromtext()`, which converts a name +found in a source [buffer object](#buffers) + +When using `dns_name_fromtext()`, the target name must have a buffer +associated with it, or else a buffer must be passed in separately which +will be used to store name data. An `origin` parameter indicates a zone origin +name, which is appended to the converted name; for absolute names, the root +zone name, `dns_rootname`, should be used as origin. If the +`DNS_NAME_DOWNCASE` flag is set in the `options` parameter, then the target +name will be converted to lower case, regardless of the case of the source +name. + + char *text = "foo.com"; + unsigned char namedata[DNS_NAME_MAXWIRE]; + isc_buffer_t buf; + dns_name_t name; + + dns_name_init(&name, NULL); + isc_buffer_init(&buf, namedata, sizeof(namedata)); + isc_buffer_add(&buf, strlen(text)); + result = dns_name_fromtext(&name, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + /* something went wrong */ + } + +An alternate mechanism `dns_name_fromstring()` converts a standard +null-terminated string to a name object. When using this function, +if the target name has a buffer associated with it, then that buffer +is used for the resulting name data; otherwise, memory is allocated for +the purpose and the name will need to be freed with `dns_name_free()` +later. + +There are also multiple functions for converting name objects to text. +`dns_name_tostring()` writes the name into a buffer object, which must +have at least `DNS_NAME_MAXTEXT` bytes of available space. +`dns_name_format()` writes the name into a null-terminated +string, which must have space for at least `DNS_NAME_FORMATSIZE` +bytes. `dns_name_tostring()` allocates memory for the text, which +must later be freed with `isc_mem_free()`. + +##### Manipulating names + +Several functions are provided for inspecting and modifying name objects. +These include: + +* `dns_name_countlabels()` returns the number of labels in a name. +* `dns_name_getlabel()` locates a specified label in a name + and references it in a [region object](#buffers). In the name + "www.example.com", label 0 is "www", label 1 is "example", + label 2 is "com", and label 3 is the root zone. +* `dns_name_getlabelsequence` copies a specified label and a + specified number of labels after it into a new name object. +* `dns_name_split()` separates a name into prefix and a suffix + on a specified label boundary. For example, "www.example.com" + can be split into "www" and "example.com". +* `dns_name_concatenate()` concatenates a prefix and a suffix into + a single name. + +##### Comparisons + +DNS name comparisons are more complex than simple string comparisons. When +sorting names, labels at the end of the name are more significant than +labels at the beginning ("zzz.com" is less than "aaa.zzz.com"). +Furthermore, it's necessary to determine relationships between names other +than simple ordering: Whether one name is the ancestor of another, or +whether they share a common ancestor, and if so how many labels they +have in common. The `dns_name_fullcompare()` function determines these +things. Its return value is the relationship between two names: + + dns_namereln_t rel; + unsigned int common; + int order; + + /* + * Get relationship between two names; store the sort + * order in 'order' and the number of common labels in + * 'common' + */ + rel = dns_name_fullcompare(name1, name2, &order, &common); + +The return value may be: + +* `dns_namereln_contains`: name1 contains name2 +* `dns_namereln_subdomain`: name2 contains name1 +* `dns_name_commonancestor`: name1 and name2 share some labels +* `dns_name_equal`: name1 and name2 are the same + +Some simpler comparison functions are provided for convenience when +not all of this information is required: + +* `dns_name_compare()`: returns the sort order of two names but + not their relationship +* `dns_name_equal()`: returns `true` when names are equivalent +* `dns_name_caseequal()`: same as `dns_name_equal()`, but case-sensitive +* `dns_name_issubdomain()`: returns `true` if one name contains another + +##### Fixed names + +`dns_fixedname_t` is a convenience type containing a name, an offsets +table, and a dedicated buffer big enough for the longest possible DNS +name. This allows names to be stack-allocated with minimal initialization: + + dns_fixedname_t fn; + dns_name_t *name; + + dns_fixedname_init(&fn) + name = dns_fixedname_name(&fn); + +`name` is now a pointer to a `dns_name` object in which a name can be +stored for the duration of this function; there is no need to initialize, +allocate, or free memory. + +#### Iterators + +Retrieving data from BIND databases involves the use of iterator +functions to walk from entry to entry. Several iterator function +sets have been defined: + +* `dns_dbiterator`: Walks the nodes in a database +* `dns_rdatasetiter`: Walks the RRsets in a node +* `dns_rdataset`: Walks the resource records in an RRset +* `dns_rriterator`: A combination of the previous three; walks all + the RRs or RRsets in a database +* `dns_rbtnodechain`: Walks the nodes in a red-black tree + +Each of these has a `first()`, `next()` and `current()` function; for +example, `dns_rdataset_first()`, `dns_rdataset_next()`, and +`dns_rdataset_current()`. + +The `first()` and `next()` functions move the iterator's cursor and so that +the data at a new location can be retrieved. (Most of these can only step +by one item at a time, but `dns_rriterator` provides both `next()` and +`nextrrset()`, enabling it to step by RR or RRset.) These functions return +`isc_result_t`, with `ISC_R_SUCCESS` indicating that there is data to +retrieve and `ISC_R_NOMORE` indicating that the iterator is finished. + +The `current()` function has no return value; it simply retrieves the +data at the current cursor location. + +To use an iterator, call the `first()` function, then the `current()` +function, then loop over the `next()` function until it no longer returns +success: + + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(rdataset, &rdata); + /* rdata is now populated with an RR */ + } + +In some cases, calling an iterator function causes the acquisition of +database and/or node locks. Rather than reaquire these locks every time +one of these functions is called, they are often simply held until the +iterator is destroyed. If a caller wishes to hold an iterator open but not +use it for a while, it should call the iterator's `pause()` function (such +as `dns_dbiterator_pause()`); this will release all the locks that are +currently held by the iterator so that other threads may proceed. + +#### Logging + +The ISC logging system is designed to provide a flexible, extensible +method of writing messages, either to the system's logging facility, +directly to a file, or into the bitbucket -- usually configured per +the desires of the user of the program. + +Each log message is associated with a particular category (eg, "security" +or "database") that reflects its nature, and a particular module (such as +the library's source file) that reflects its origin. Messages are also +assigned a priority level which states how remarkable the message is; +the program's user may use this to decide how much detail is desired. + +Libraries which use the ISC logging system can be linked against each +other without fear of conflict. A program is able to select which, if +any, libraries will write log messages. + +##### Fundamentals + +Log messages are associated with three pieces of information that are +used to determine their disposition: a category, a module, and a +level (aka "priority"). + +A category describes the conceptual nature of the message, that is, +what general aspect of the code it is concerned with. For example, +the DNS library defines categories that include the workings of the +database as well security issues. Macros for naming categories are +typically provided in the library's log header file, such as +`DNS_LOGCATEGORY_DATABASE` and `DNS_LOGCATEGORY_SECURITY` in ``. +The special category `ISC_LOGCATEGORY_DEFAULT` is associated with +any message that does not match a particular category (or matches a +category but not a module, as seen in the next paragraph). + +A module is loosely the origin of a message. There may not be a +one-to-one correspondence of source files with modules, but it is typical +that a module's name reflect the source file in which it is used. So, for +example, the module identifier `DNS_LOGMODULE_RBT` would be used by +messages coming from within the `lib/dns/rbt.c` source file. + +The specification of the combination of a category and a module for a +message are called the message's "category/module pair". + +The level of a message is an indication of its severity. There are +six standard logging levels, in order here from most to least severe +(least to most common): + +* `ISC_LOG_CRITICAL`: An error so severe it causes the program to exit. +* `ISC_LOG_ERROR`: A very notable error, but the program can go on. +* `ISC_LOG_WARNING`: Something is probably not as it should be. +* `ISC_LOG_NOTICE`: Notable events that occur while the program runs. +* `ISC_LOG_INFO`: Statistics and routine announcements. +* `ISC_LOG_DEBUG(unsigned int level)`: Detailed debugging messages. + +`ISC_LOG_DEBUG` is not quite like the others in that it takes an +argument the defines roughly how detailed the message is; a higher +level means more copious detail, so that values near 0 would be used +at places like the entry to major sections of code, while greater +numbers would be used inside loops. + +The next building block of the logging system is a channel. A channel +specifies where a message of a particular priority level should go, as +well as any special options for that destination. There are four +basic destinations, as follows: + +* `ISC_LOG_TOSYSLOG`: Send it to syslog. +* `ISC_LOG_TOFILE`: Write to a file. +* `ISC_LOG_TOFILEDESC`: Write to a (previously opened) file descriptor. +* `ISC_LOG_TONULL`: Do not write the message when selected. + +A file destination names a path to a log file. It also specifies the +maximum allowable byte size of the file before it is closed (where 0 +means no limit) and the number of versions of a file to keep (where +`ISC_LOG_ROLLNEVER` means the logging system never renames the log file, +and `ISC_LOG_ROLLINFINITE` means no cap, other than integer size, on the +number of versions). Version control is done just before a file is opened, +so a program that used it would start with a fresh log file (unless using +`ISC_LOG_ROLLNEVER`) each time it ran. If you want to use an external +rolling method, use `ISC_LOG_ROLLNEVER` and ensure that your program has a +mechanism for calling `isc_log_closefilelogs()`. + +A file descriptor destination is simply associated with a previously +opened stdio file descriptor. This is mostly used for associating +stdout or stderr with log messages, but could also be used, for +example, to send logging messages down a pipe that has been opened by +the program. File descriptor destinations are never closed, have no +maximum size limit, and do not do version control. + +Syslog destinations are associated with the standard syslog facilities +available on your system: generally `syslogd` on UNIX and Linux systems +and the Application log in the Event Viewer on Windows systems. They too +have no maximum size limit and do no version control. + +Since null channels go nowhere, no additional destination +specification is necessary. + +Channels have string names that are their primary external reference. +There are four predefined logging channels (five, as of BIND 9.11): + +* `"default_stderr"`: Descriptor channel to stderr at priority `ISC_LOG_INFO` +* `"default_logfile"`: File channel created if the user specifies a logfile using `named -L` at priority `ISC_LOG_DYNAMIC` (9.11 and higher only) +* `"default_debug"`: Descriptor channel to stderr at priority `ISC_LOG_DYNAMIC` +* `"default_syslog"` -- Syslog channel to `LOG_DAEMON` at priority `ISC_LOG_INFO` +* `"null"` -- Null channel + +Other channels may be configured by the user via `named.conf`. + +`ISC_LOG_DYNAMIC` indicates to the logging system that +debugging messages are desired, but only at the current debugging level +of the program. The debugging level can be modifid dynamically at +runtime; in `named` this can be done by the `"rndc trace"` command. +When the debugging level is 0 (turned off), then no debugging messages are +written to the channel. If the debugging level is raised, only debugging +messages up to the current debugging level are written to the channel. + +These objects -- the category, module, and channel -- direct hessages +to desired destinations. Each category/module pair can be associated +with a specific channel, and the correct destination will be used +when a message is logged by `isc_log_write()`. + +In `isc_log_write()`, the logging system first looks up a list that +consists of all of the channels associated with a particular category. +It walks down the list looking for each channel that also has the +indicated module associated with it, and writes the message to each +channel it encounters. If no match is found in the list for the +module, the default channel (associated with `ISC_LOGCATEGORY_DEFAULT`) +is used. The default is also used if no channels have been specified +for the category at all. + +##### Externally visible structure + +The types used by programs for configuring log message destinations are +`isc_log_t` and `isc_logconfig_t`. The `isc_log_t` type is normally +created only once by a program, to hold static information about what +categories and modules exist in the program and some other housekeeping +information. `isc_logconfig_t` is used to store the configurable +specification of message destinations, which can be changed during the +course of the program. + +A starting configuration (`isc_logconfig_t`) is created implicitly when +the context (`isc_log_t`) is created. The pointer to this configuration +is returned via a parameter to `isc_log_create()` so that it can then be +configured. A new log configuration can be established by creating +it with `isc_logconfig_create()`, configuring it, then installing it as +the active configuration with `isc_logconfig_use()`. + +##### Logging in multithreaded programs + +The entire logging context is thread-locked for most of the duration of the +`isc_log_write()`. However, `isc_log_write()` avoids the delays caused by +locking when it is clear that there are no possible outputs for a message +based on its debugging level --- this is so that a program can have +debugging messages sprinkled liberally throughout it but not incur any +locking penalty when debugging is not enabled. + +##### Using libraries that use the logging system + +To enable the messages from a library that uses the logging system, +the following steps need to be taken to initialize it. + +1. Include the main logging header file as well as the logging header + file for any additional library you are using. For example, when using + the DNS library, include the following: + + #include + #include + +1. Initialize a logging context. A logging context needs a valid + memory context in order to work, so the following code snippet shows a + rudimentary initialization of both. + + isc_mem_t *mctx; + isc_log_t *lctx; + isc_logconfig_t *lcfg; + + if (isc_mem_create(0, 0, &mctx) != ISC_R_SUCCESS) || + isc_log_create(mctx, &lctx, &lcfg) != ISC_R_SUCCESS)) + oops_it_didnt_work(); + +1. Initalize any additional libraries. The convention for the name of + the initialization function is `{library}_log_init()`, with a pointer to + the logging context as an argument. The function can only be called + once in a program or it will generate an assertion. + + `dns_log_init(lctx);` + + If you do not want a library to write any log messages, simply do not + call its the initialization function. + +1. Create any channels you want in addition to the internal channels + of `default_syslog`, `default_stderr`, `default_debug` and null. A + destination structure needs to be filled for any destination other than + null. The following examples show use of a file log, a file descriptor + log, and syslog. + + isc_logdestination_t destination; + + destination.file.name = "/var/log/example"; + destination.file.maximum_size = 0; /* No byte limit. */ + destination.file.versions = ISC_LOG_ROLLNEVER; /* External rolling. */ + result = isc_log_createchannel(lcfg, "sample1", ISC_LOG_TOFILE, + ISC_LOG_DYNAMIC, &destination, + ISC_LOG_PRINTTIME); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + destination.file.stream = stdout; + result = isc_log_createchannel(lcfg, "sample2", ISC_LOG_TOFILEDESC, + ISC_LOG_INFO, &destination, + ISC_LOG_PRINTTIME); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + destination.facility = LOG_ERR; + result = isc_log_createchannel(lcfg, "sample3", ISC_LOG_SYSLOG, + ISC_LOG_ERROR, &destination, 0); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + `ISC_LOG_DYNAMIC` is used to define a channel that wants any of the + messages up to the current debugging level of the program. + `ISC_LOG_DEBUG(level)` can define a channel that *always* gets messages + up to the debug level specified, regardless of the debugging state of + the server. + +1. Direct the various log categories and modules to the desired + destination. This step is not necessary if the normal behavior of + sending all messages to `default_stderr` is acceptable. The following + examples sends DNS security messages to stderr, DNS database messages to + null, and all other messages to syslog. + + result = isc_log_usechannel(lcfg, "default_stderr", + DNS_LOGCATEGORY_SECURITY, NULL); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + result = isc_log_usechannel(lcfg, "null", + DNS_LOGCATEGORY_DATABASE, NULL); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + result = isc_log_usechannel(lcfg, "default_syslog", + ISC_LOGCATEGORY_DEFAULT, NULL); + if (result != ISC_R_SUCCESS) + oops_it_didnt_work(); + + Providing a NULL argument for the category means "associate the channel + with the indicated module in all known categories": + `ISC_CATEGORY_DEFAULT`. + + Providing a NULL argument for the module means "associate the channel + with all modules that use this category." + +There are three additional functions you might find useful in your program +to control logging behavior, two to work with the debugging level and one +to control the closing of log files. + + void isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level); + unsigned int isc_log_getdebuglevel(isc_log_t *lctx); + +These set and retrieve the current debugging level of the program. +`isc_log_getdebuglevel()` can be used so that you need not keep track of +the level yourself in another variable. + + void isc_log_closefilelogs(isc_log_t *lcxt); + +This function closes any open log files. This is useful for programs that +do not want to do file rotation as with the internal rolling mechanism. +For example, a program that wanted to keep daily logs would define a +channel which used `ISC_LOG_ROLLNEVER`, then once a day would rename the +log file and call `isc_log_closefilelogs()`. The next time a message needs +to be written a file that has been closed, it is reopened. + +#### Adding a new RR type + +##### Overview + +BIND 9 was designed to make it relatively easy for anyone with sufficient +knowledge of C to add user defined resource record (RR) types. + +The descriptions of all the record types known to BIND are in a directory +structure under lib/dns/rdata in the source tree. This directory is +structured at the first level by the DNS CLASS the record type belongs to. +The name of the directory is the `{class}_{code}` (for example, IN is +`in_1`). + +The currently existing classes are `in_1`, `ch_3`, `hs_4`, `any_255` and +`generic` -- the first four hold RR types that are specific to a particular +class, and "generic" holds RR types that are the same across all classes. +Within each of these directories there are pairs of files which describe +the actual types. These files are named `{type}_{code}.c` and +`{type}_{code}.h`: for examle, the description of the MX record, which has +the RR type code 15, is in `mx_15.c` and `mx_15.h`. + +Within each of these files there are method functions for various +operations that apply to types, such as how to print out a type, how to +read a type from a text file, how to read a record from a DNS message in +wire format, etc. These methods have names constructed from the type, +class (if the record is class specific) and operation to be performed. +These methods are called from the `dns_rdata_{method}` functions which are +declared in ``. + +Once the two files containing the method and type definitions for the +structures have been written you need to run `"make clean"` then `"make"` to +incorporate the new record type. This will cause the `lib/dns/rdata` +directory structure to be scanned and header files to be rebuilt which will +include the new files. All the tools that are part of BIND will know about +the new type. + +You can also define auxiliary functions to help walk the structure returned +by `dns_rdata_tostruct()`, such as `dns_rdata_txt_first()` and +`dns_rdata_txt_next()`, which are used to walk the text strings in a TXT +record. The code goes into the .c file and the function prototype into the +.h file the contents of which are included in ``. + +`lib/dns/rdata/generic/proforma.c` and `lib/dns/rdata/generic/proforma.h` +can be copied and used as starting points when defining a adding a new type. +Please also look as the existing record types for examples of how to +implement a method. + +##### Type value selection + +Type values range from 0 to 65536. These have been further divided into +reserved values, values that have global definition and values that have +local definition as defined in [RFC 6895](http://tools.ietf.org/html/rfc6895). +Please use an appropriate value. You can use a private value +(65280 - 65534) while waiting for a type assignment to be made, then +rename the file and update the type values when the assignment has been +made. + +##### Methods + +"fromtext" reads a series of tokens from `lexer` and constructs +a DNS record in wire format, which it stores in `target`. It performs +sanity checks on the entered content, rejecting any invalid records. + + static isc_result_t + fromtext[_]_(int rdclass, dns_rdatatype_t type, + isc_lex_t *lexer, dns_name_t *origin, + unsigned int options, isc_buffer_t *target, + dns_rdatacallbacks_t *callbacks); + + + static isc_result_t + totext[_]_(dns_rdata_t *rdata, + dns_rdata_textctx_t *tctx, + isc_buffer_t *target); + +"totext" takes a record in wire format, converts it to +presentation format, and stores it in a buffer for later printing. + + static isc_result_t + fromwire[_]_(int rdclass, dns_rdatatype_t type, + isc_buffer_t *source, + dns_decompress_t *dctx, + unsigned int options, + isc_buffer_t *target_t); + +"fromwire" copies in a record received in a DNS message. +It performs sanity checks to ensure that the record conforms to the +specification for the RR type. It expands any compressed domain names, +and copies out the expanded record to a buffer. +(NOTE: It is critical to the security of the name server that only valid +records are accepted by this function, as other parts of the name server +do not verify the contents of incoming records.) + + static isc_result_t + towire[_]_(dns_rdata_t *rdata, dns_compress_t *cctx, + isc_buffer_t *target); + +"towire" takes a record in wire format and adds it to a DNS +message, optionally compressing domain names if that is allowed by the +type's definition. (NOTE: Compression is no longer allowed in new +RR types, so this is effectively a wrapper around `memmove()`.) + + static int + compare[_]_(const dns_rdata_t *rdata1, + const dns_rdata_t *rdata2); + +"compare" takes two records and compares them according to the +DNSSEC ordering rules. For all new record types, this is effectively a +wrapper around `memcmp()`. + + static isc_result_t + fromstruct[_]_(int rdclass, dns_rdatatype_t type, + void *source, isc_buffer_t *target); + +"fromstruct" takes a C structure (as described in +`tostruct()`, below) and turns it into a record in wire format. + + static isc_result_t + tostruct[_]_(dns_rdata_t *rdata, void *target, + isc_mem_t *mctx); + +"tostruct" take a record in wire format and breaks it down into +a type-specific C structure defined in the header file. The name of this +structure is `dns_rdata_[_]_t`; the first element of +the structure must be `"dns_rdatacommon_t common;"`. +If no memory context is passed in, then the caller will preserve the +contents of the record in wire form until the structure is freed or no +longer in use. If a memory context is passed, in then memory should +be allocated for anything not directly part of the structure. + + static void + freestruct[_]_(void *source); + +"freestruct" frees any memory allocated by `tostruct()`. + + static isc_result_t + additional[_]_(dns_rdata_t *rdata, + dns_additionaldatafunc_t add, + void *arg); + +"additional" provides the ability to add related records +to the additional section of a message when this record is added to +a message. An empty method is usual here. + + static isc_result_t + digest[_]_(dns_rdata_t *rdata, + dns_digestfunc_t digest, + void *arg); + +"digest" passes the record contents to the `digest` function, +performing any needed DNSSEC canonicalisation. For all new record types, +this simply involves adding the entire record to a region and passing that +to `digest`, because new record types are treated as opaque blobs of data +by DNSSEC. + + static isc_boolean_t + checkowner[_]_(dns_name_t *name, + dns_rdataclass_t rdclass, + dns_rdatatype_t type, + isc_boolean_t wildcard); + +"checkowner" takes the owner name of the record and checks +that it meets appropriate rules that are defined external to the DNS. +In most cases this can just be a function that returns `ISC_TRUE`. + + static isc_boolean_t + checknames[_]_(dns_rdata_t *rdata, + dns_name_t *owner, + dns_name_t *bad); + +"checknames" checks the contents of the rdata with the given +owner name to ensure that it meets externally defined syntax rules. +If `ISC_FALSE` is returned, then `bad` will point to the name that +caused the probelm. + + static int + casecompare[_]_(const dns_rdata_t *rdata1, + const dns_rdata_t *rdata2); + +"casecompare" compares two rdatas case-insensitively. +In nearly all cases, this is simply a wrapper around the `compare()` +function, except where DNSSEC comparisons are specified as +case-sensitive. Unknown RR types are always compared case-sensitively. + +#### Task and timer model + +The BIND task/timer management system can be thought of as comparable to a +simple non-preemptive multitasking operating system. + +In this model, what BIND calls a "task" (or an `isc_task_t` object) is the +equivalent of what is usually called a "process" in an operating system: +a persistent execution context in which functions run when action is +required. When the action is complete, the task goes to sleep, and +wakes up again when more work arrives. A "worker thread" is comparable +to an operating system's CPU; when a task is ready to run, a worker +thread will run it. By default, BIND creates one worker thread for each +system CPU. + +An "event" object can be associated with a task, and triggered when a a +specific condition occurs. Each event object contains a pointer to a +specific function and arguments to be passed to it. When the event +triggers, the task is placed onto a "ready queue" in the task manager. As +each running task finishes, the worker threads pull new tasks off the ready +queue. When the task associated with a given event reaches the head of the +queue, the specified function will be called. + +Examples: + +`isc_socket_recv()` calls the `recv()` system call asynchronously: rather +than waiting for data, it returns immediately, but it sets up an event to +be triggered when the `recv()` call completes; BIND can now do other work +instead of waiting for I/O. Once the `recv()` is finished, the +associated event is triggered. + + + /* + * Function to handle a completed recv() + */ + static void + recvdone(isc_task_t *task, isc_event_t *event) { + /* Arguments are in event->ev_arg. */ + } + + ... + + /* + * Call recv() on socket 'sock', put results into 'region', + * minimum read size 1, and call recvdone() with NULL as + * argument. (Note: 'sock' is already associated with a + * particular task, so that doesn't need to be specified + * here.) + */ + isc_socket_recv(sock, ®ion, 1, recvdone, NULL); + +A timer is set for a specifed time in the future, and the event will +be triggered at that time. + + /* + * Function to handle a timeout + */ + static void + timeout(isc_task_t *task, isc_event_t *event) { + /* do things */ + } + + ... + + /* + * Set up a timer in timer manager 'timermgr', to run + * once, with a NULL expiration time, after 'interval' + * has passed; it will run the function 'timeout' with + * 'arg' as its argument in task 'task'. + */ + isc_timer_t *timer = NULL; + result = isc_timer_create(timermgr, isc_timertype_once, NULL, + interval, task, timeout, arg, &timer); + +An event can also be explicitly triggered via `isc_task_send()`. + + static void + do_things(isc_task_t *task, isc_event_t *event) { + /* this function does things */ + } + + ... + + /* + * Allocate an event that calls 'do_things' with a + * NULL argument, using 'myself' as ev_sender. + * + * DNS_EVENT_DOTHINGS must be defined in . + * + * (Note that 'size' must be specified because there are + * event objects that inherit from isc_event_t, incorporating + * common members via the ISC_EVENT_COMMON member and then + * following them with other members.) + */ + isc_event_t *event; + event = isc_event_allocate(mctx, myself, DNS_EVENT_DOTHINGS, + do_things, NULL, sizeof(isc_event_t)); + if (event == NULL) + return (ISC_R_NOMEMORY); + + ... + + /* + * Send the allocated event to task 'task' + */ + isc_task_send(task, event); + +#### More... + +Further architectural details on BIND to be added here in the future. diff --git a/doc/markdown/howto.mkd b/doc/markdown/howto.mkd new file mode 100644 index 0000000000..ba74ebd165 --- /dev/null +++ b/doc/markdown/howto.mkd @@ -0,0 +1,207 @@ + +## BIND Source Access and Contributor Guidelines +*May 8, 2014* + +### Contents + +1. [Access to source code](#access) +1. [Reporting bugs](#bugs) +1. [Contributing code](#contrib) + +### Introduction + +Thank you for using BIND! + +BIND is open source software that implements the Domain Name System (DNS) +protocols for the Internet. It is a reference implementation of those +protocols, but it is also production-grade software, suitable for use in +high-volume and high-reliability applications. It is by far the most +widely used DNS software, providing a robust and stable platform on top of +which organizations can build distributed computing systems with the +knowledge that those systems are fully compliant with published DNS +standards. + +BIND is and will always remain free and openly available. It can be +used and modified in any way by anyone. + +BIND is maintained by the [Internet Systems Consortium](https://www.isc.org), +a public-benefit 501(c)(3) nonprofit, using a "managed open source" approach: +anyone can see the source, but only ISC employees have commit access. +Until recently, the source could only be seen once ISC had published +a release: read access to the source repository was restricted just +as commit access was. That's now changing, with the opening of a +public git mirror to the BIND source tree (see below). + +### Access to source code + +Public BIND releases are always available from the +[ISC FTP site](ftp://ftp.isc.org/isc/bind9). + +A public-access GIT repository is also available at +[https://bindmember.isc.org](https://bindmember.isc.org). +This repository is a mirror, updated several times per day, of the +source repository maintained by ISC. It contains all the public release +branches; upcoming releases can be viewed in their current state at any +time. It does *not* contain development branches or unreviewed work in +progress. Commits which address security vulnerablilities are withheld +until after public disclosure. + +You can browse the source online via +[https://bindmember.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=summary](https://bindmember.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=summary) + +To clone the repository, use: + +> $ git clone https://bindmember.isc.org/git/bind9.git + +Branch names are of the form `v9_X`, where X represents the second number in the BIND 9 version number. So, to check out the BIND 9.10 branch, use: + +> $ git checkout v9_10 + +Whenever a branch is ready for publication, a tag will be placed of the +form `v9_X_Y`. The 9.9.5 release, for instance, is tagged as `v9_9_5`. + +The branch in which the next major release is being developed is called +`master`. + +### Reporting bugs + +Reports of flaws in the BIND package, including software bugs, errors in +the documentation, missing files in the tarball, etc, can be emailed to +`bind9-bugs@isc.org`, or reported via the +[bug submission form](http://www.isc.org/community/report-bug) at +[http://www.isc.org/community/report-bug](http://www.isc.org/community/report-bug). + +Suggested changes or requests for new features can be emailed to +`bind-suggest@isc.org`. Both bugs and suggestions are stored in the +ticketing system used by the software engineering team at ISC. + +All submissions to the ticketing system receive an automatic response. +Any followup email sent to the ticketing system should use the same subject +header, so that it will be routed to the same ticket. + +Due to a large ticket backlog and an even larger quantity of incoming spam, +we are sometimes slow to respond, especially if a bug is cosmetic or if a +feature request is vague or low in priority, but we will try at least to +acknowledge legitimate bug reports within a week. + +The bug database is not publicly readable. Information about your +system that you submit in bug reports will not be divulged outside ISC. + +### Contributing code + +BIND's [open source +license](http://www.isc.org/downloads/software-support-policy/isc-license/) +does not require changes to be contributed back to ISC, but this page +includes some guidelines for those who would like to do so. + +We accept two different types of code contribution: Code intended for +inclusion in [BIND](#bind) itself, and code intended for the +[`contrib`](#contrib) directory. + +#### BIND code + +Patches for BIND itself may be submitted using the same methods as bug +reports or suggestions. When submitting a patch, please prepend the +subject header with "`[PATCH]`" so it will be easier for us to find. If +your patch introduces a new feature in BIND, please submit it to +`bind-suggest@isc.org`; if it fixes a bug, please submit it to +`bind9-bugs@isc.org`. + +ISC does not require an explicit copyright assignment for patch +contributions. However, by submitting a patch to ISC, you implicitly +certify that you are the author of the code, that you intend to reliquish +exclusive copyright, and that you grant permission to publish your +work under the +[ISC license](http://www.isc.org/downloads/software-support-policy/isc-license/). + +Patches should be submitted as diffs against a specific version of BIND -- +preferably the current top of the `master` branch. Diffs may be +generated using either `git format-patch` or `git diff`. + +Those wanting to write code for BIND may be interested +in the [developer information](dev.html) page, which includes +information about BIND design and coding practices, including +discussion of internal APIs and overall system architecture. +(This is a work in progress, and still quite preliminary.) + +Every patch submitted will be reviewed by ISC engineers following +our [code review process](dev.html#reviews) before it is merged. + +It may take considerable time to review patch submissions, especially +if they don't meet ISC style and quality guidelines. If the patch +is a good idea, we can and will do additional work to bring them up +to par, but if we're busy with other work, it may take us a long +time to get to it. + +To ensure your patch is acted on as promptly as possible, please: + +* Try to adhere to the [BIND 9 coding style](style.html). +* Run `make` `check` to ensure your change hasn't caused any + functional regressions. +* Document your work, both in the patch itself and in the + accompanying email. +* In patches that make non-trivial functional changes, include system + tests if possible; when introducing or substantially altering a + library API, include unit tests. See [Testing](dev.html#testing) + for more information. + +##### Changes to `configure` + +If you need to make changes to `configure`, you should not edit it +directly; instead, edit `configure.in`, then run `autoconf`. Similarly, +instead of editing `config.h.in` directly, edit `configure.in` and run +`autoheader`. + +When submitting your patch, it is fine to omit the `configure` diffs. +Just send the `configure.in` diffs and we'll generate the new `configure` +during the review process. + +##### Documentation + +All functional changes should be documented. There are three types +of documentation in the BIND source tree: + +* Man pages are kept alongside the source code for the commands + they document, in files ending in `.docbook`; for example, the + `named` man page is `bin/named/named.docbook`. +* The *BIND 9 Administrator Reference Manual* is mostly in + `doc/arm/Bv9ARM-book.xml`, plus a few other XML files that are included + in it. +* API documentation is in the header file describing the API, in + Doxygen-formatted comments. + +It is not necessary to edit any documentation files other than these; the +PDF, HTML, and `nroff`-format files will be generated automatically +from the `docbook` and `XML` files by a script whenever a documentation +change is merged to a release branch. + +#### Contrib code + +The software in the `contrib` directory of the BIND 9 `tar` archive is not +formally supported by ISC, but is included for the convenience of users. +These are things we consider useful or informative, but are not able to +support at the same level as BIND. + +`contrib` includes some useful DNS-related open source tools such as `zkt`, +`nslint`, and the `idnkit` library for internationalized domain name +support; useful scripts such as `nanny.pl` and `mkdane.sh`; performance +testers including `queryperf` and `perftcpdns`; and drivers and modules for +DLZ. + +If you have code with a BSD-compatible license that you would like us to +includ in `contrib`, please send it to `bind-suggest@isc.org`, with +"`[CONTRIB]`" in the subject header. diff --git a/doc/markdown/style.mkd b/doc/markdown/style.mkd new file mode 100644 index 0000000000..17fb14c473 --- /dev/null +++ b/doc/markdown/style.mkd @@ -0,0 +1,772 @@ + +## BIND 9 Coding Style + +BIND 9 is principally written in [C](#cstyle), with some additional code +written in [Python](#pystyle), [Perl](#plstyle) and +[Bourne shell](#shstyle). Style guidelines for each of these are +below. + +### C + +#### Compiler + +An ANSI standard C compiler and library are assumed. Feel free to use any +ANSI C feature. + +#### Warnings + +Given a reasonable set of things to warn about (e.g. -W -Wall for gcc), the +goal is to compile with no warnings. + +#### Copyright Notices + +All source files should have a copyright. The copyright year(s) should be +kept current. The files and the copyright year(s) should be listed in +util/copyrights. When an existing file is updated in the source +repository, its copyright notice and dates are updated automatically. + +#### Line Formatting + +Use tabs for indentation. Spaces before statements are only allowed when +needed to line up a continued expression. In the following example, spaces +used for indentation are indicated with `"_"`: + + if (i == 0) { + printf("this is going to be %s very long %s statement\\n", + _______"a", "printf"); + } + +Text editors should be configured with tabstop set to 8 characters, and +tabs should not be expanded to into spaces. The following `vim` settings +conform well to BIND 9 C style: + + set showmatch + set showmode + set autoindent + set expandtab + + filetype plugin on + let c_syntax_for_h = 1 + autocmd FileType c,cc,cpp set cindent + autocmd FileType c,cc,cpp set cino=(0:0l1 + autocmd FileType c,cc,cpp set fo=rotcq + autocmd FileType c,cc,cpp set noexpandtab ts=8 + autocmd FileType python set ts=4 sw=4 + + filetype indent on + +#### Vertical Whitespace + +Vertical whitespace is encouraged for improved code legibility: closely +related statements should be grouped, and then the groups separated with a +single empty line. There should never be two or more empty lines adjacent +to one another. + +#### Line Length + +Lines should be no longer than 79 characters, even if it requires violating +indentation rules to make them fit. Since ANSI C is assumed, the best way to +deal with strings that extend past column 79 is to break them into two or +more sections separated from each other by a newline and indentation: + + puts("This string got very far to the " + "right and wrapped. ANSI catenation " + "rules will turn this into one " + "long string."); + +#### Comments + +Comments should be used whenever they improve the readability or +comprehensibility of the code. Comments describing public functions are +usually in the header file below the function prototype; comments +describing static functions are above the function declaration. + +Comments may be single-line or multiline. A single-line comment should be +at the end of the line if there is other text on the line, and should start +in the same column as other nearby end-of-line comments. The comment +should be at the same indentation level as the code it is referring to. + +Multiline comments should start with `"/*"` on a line by itself. +Subsequent lines should have `" *"` lined-up with the `"*"` above. The end of +the comment should be `" */"` on a line by itself, again with the `"*"` +lined-up with the one above. Comments should start with a capital letter +and end with a period. + +Good: + + /* + * Private variables. + */ + + static int a /* Description of 'a'. */ + static int b /* Description of 'b'. */ + static char * c /* Description of 'c'. */ + + +The following lint and lint-like comments should be used where appropriate: + + /* ARGSUSED */ + /* FALLTHROUGH */ + /* NOTREACHED */ + /* VARARGS */ + +#### Header files + +.h files should not rely on other files having been included. .h files +should prevent multiple inclusion. The OS is assumed to prevent multiple +inclusion of its .h files. + +A header file defining a public interface is generally placed in the source +tree two levels below the C file that implements the interface. For +example, the include file defining the interface for `lib/dns/zone.c` is in +`lib/dns/include/dns/zone.h`. (The second "dns" in the path enables the file +to be included via `"#include "`.) + +Public header files should include interface documentation in Doxygen +format. + +Private header files, describing interfaces that are for internal use +within a library but not for public use, are kept in the source tree at the +same level as their related C files, and often have `"_p"` in their names, +e.g. `lib/isc/task_p.h`. + +Header files that define modules should have a structure like the +following. Note that `` MUST be included by any public header +file using the ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS macros, so the +correct name-mangling happens for function declarations when C++ programs +include the file. `` SHOULD be included for private header files +or for public files that do not declare any functions. + + + /* + * Copyright (C) 2014 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this + * software for any purpose with or without fee is hereby + * granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE + * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET + * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + #ifndef ISC_WHATEVER_H + #define ISC_WHATEVER_H 1 + + /***** + ***** Module Info + *****/ + + /* + * (Module name here.) + * + * (One line description here.) + * + * (Extended description and notes here.) + * + * MP: + * (Information about multiprocessing considerations + * here, e.g. locking requirements.) + * + * Reliability: + * (Any reliability concerns should be mentioned here.) + * + * Resources: + * (A rough guide to how resources are used by this module.) + * + * Security: + * (Any security issues are discussed here.) + * + * Standards: + * (Any standards relevant to the module are listed here.) + */ + + /*** + *** Imports + ***/ + + /* #includes here. */ + #include + + /*** + *** Types + ***/ + + /* (Type definitions here.) */ + + /*** + *** Functions + ***/ + ISC_LANG_BEGINDECLS + /* (Function declarations here, with full prototypes.) */ + ISC_LANG_ENDDECLS + + #endif /* ISC_WHATEVER_H */ + +#### Including Interfaces (.h files) + +The first file to be included in a C source file must be config.h. The +config.h file must never be included by any public header file (that is, +any header file that will be installed by `"make install"`). + +Try to include only necessary files, not everything under the sun. +Operating-system-specific files should not be included by most modules; if +they are needed, they should be used with `#ifdef` and controlled by +`configure`. + +#### Statements + +There should be at most one statement per line. The comma operator should +not be used to form compound statements. + +Bad: + + if (i > 0) { + printf("yes\\n"); i = 0; j = 0; + x = 4, y *= 2; + } + +#### Functions + +The use of ANSI C function prototypes is required. + +The return type of the function should be listed on a line by itself when +specifying the implementation of the function. The opening curly brace +should occur on the same line as the argument list, unless the argument +list is more than one line long: + + static inline void + func1(int i) { + /* whatever */ + } + + int + func2(int first_argument, int next_argument, + int last_argument) + { + /* whatever */ + } + +To suppress compiler warnings, unused function arguments must be +declared within the function via the `UNUSED()` macro. + +In the function body, local variable declarations must be at the beginning +of the function, followed by any `REQUIRE()` statements, then `UNUSED()` +declarations, then all other code, in that order. These sections should be +separated by blank lines. + +#### Curly Braces + +Curly Braces do not get their own indentation. + +An opening brace does not start a new line. The statements enclosed by the +braces should not be on the same line as the opening or closing brace. A +closing brace should be the only thing on the line, unless it's part of an +else clause. + +Generally speaking, when a control statement (e.g., `if`, `for` or `while`) has +only a single action associated with it, then no bracing is used around the +statement. Exceptions include when the compiler would complain about an +ambiguous else clause, or when extra bracing improves readability or +safety. + +Good: + + static void + f(int i) { + if (i > 0) { + printf("yes\\n"); + i = 0; + } else + printf("no\\n"); + } + +Bad: + + void f(int i) + { + if(i<0){i=0;printf("was negative\\n");} + if (i > 0) + { + printf("yes\\n"); + i = 0; + }} + +#### Spaces + +* DO put a space between operators like `=`, `+`, `==`, etc. +* DO put a space after `,`. +* DO put a space after `;` in a `for` statement. +* DO put spaces after C reserved words such as `if`, `for`, `while`, and `do`. +* DO put a space after `return`, and parenthesize the return value. +* Do NOT put a space between a variable or function name and `(` or `[`. +* Do NOT put a space after the `sizeof` operator name, and DO parenthesize its argument: `malloc(4 * sizeof(long))`. +* Do NOT put a space immediately after a `(` or immediately before a `)`, unless it improves readability. The same goes for `[` and `]`. +* Do NOT put a space before `++` or `--` when used in post-increment/ decrement mode, or after them when used in pre-increment/decrement mode. +* Do NOT put a space before `;` when terminating a statement or in a `for` statement. +* Do NOT put a space after `*` when used to dereference a pointer, or on either side of `->`. +* Do NOT put a space after `~`. +* The `|` operator may either have a space on both sides or it may have no spaces, depending on readability. Either way, if the `|` operator is used more than once in a statement, then the spacing must be consistent. + +#### Return Values + +If a function returns a value, it should be cast to `(void)` if you don't +care what the value is, except for `printf` and its variants, `fputc`, +`fwrite` (when writing text), `fflush`, `memmove`, `memset`, `strcpy`, +`strncpy`, and `strcat`. + +Certain functions will return values or not depending on the operating +system or even compiler flags; these include `openlog` and `srandom`. The +return value of these should not be used nor cast to `(void)`. + +All error conditions must be handled. + +Mixing of error status and valid results within a single type should be +avoided. + +Good: + + os_result_t result; + os_descriptor_t s; + + result = os_socket_create(AF_INET, SOCK_STREAM, 0, &s); + if (result != OS_R_SUCCESS) { + /* Do something about the error. */ + return; + } + +Not so good: + + int s; + + /* + * Obviously using interfaces like socket() (below) is allowed + * since otherwise you couldn't call operating system routines; the + * point is not to write more interfaces like them. + */ + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + /* Do something about the error using errno. */ + return; + } + +#### Integral Types + +Careful thought should be given to whether an integral type should be +signed or unsigned, and to whether a specific size is required. `int` +should be used for generic variables (e.g. iteration counters, array +subscripts). Other than for generic variables, if a negative value isn't +meaningful, the variable should be unsigned. Assignments and comparisons +between signed and unsigned integers should be avoided; suppressing the +warnings with casts is not desireable. + +Typedefs are provided to specify particular sizes of integral variables, +e.g., `isc_uint32_t` (unsigned 32-bit integer), `isc_int16_t` (signed +16-bit integer). These may be used when `unsigned long` or `short` could +be ambiguous. + +#### Clear Success or Failure + +A function should report success or failure, and do so accurately. It +should never fail silently. Use of [design by contract](dev.html#dbc) +can help here. + +When a function is designed to return results to the caller by assigning +to caller variables through pointer arguments, it should perform the +assignment only if it succeeds, and leave the variables unmodified if it +fails. A `REQUIRE()` statement should be used to ensure that the pointer +is in a sane state when the function is called. + +The `isc_result_t` is provided for use by result codes. See the +[results](dev.html#results) section of the [developer +information](dev.html) page for more details. + +#### Testing Bits + +Bit testing should be as follows: + +Good: + + /* Test if flag set. */ + if ((flags & FOO) != 0) { + + } + /* Test if flag clear. */ + if ((flags & BAR) == 0) { + + } + /* Test if both flags set. */ + if ((flags & (FOO|BAR)) == (FOO|BAR)) { + + } + +Bad: + + /* Test if flag set. */ + if (flags & FOO) { + + } + /* Test if flag clear. */ + if (! (flags & BAR)) { + + } + +#### Testing for Zero or Non-zero + +Explicit testing against zero is required for numeric, non-boolean +variables. + +Good: + + int i = 10; + + /* ... */ + + if (i != 0) { + /* Do something. */ + } + +Bad: + + int i = 10; + + /* ... */ + + if (i) { + /* Do something. */ + } + +#### Null Pointer + +The null pointer value should be referred to as `NULL`, not `0`. + +Testing to see whether a pointer is `NULL` should be an explicit +comparison; do not treat a pointer variable as if it were a boolean. + +Good: + + char *c = NULL; + + /* ... */ + + if (c != NULL) { + /* Do something. */ + } + +Bad: + + char *c = NULL; + + /* ... */ + + if (c) { + /* Do something. */ + } + +#### The Ternary Operator + +The `?:` operator should mostly be avoided. It is tolerated when deciding +what value to pass as a parameter to a function, such as frequently happens +with printf, and also when a simple (non-compound) value is being used in +assignment or as part of a calculation. + +If a statement containing a ternary operator spills over more than one +line, put the `?` and `:` at the begginning of the following lines with two +additional spaces of indent. + +Using the ternary operator to specify a return value is very rarely +permissible, and never when returning result codes. + +Good: + + printf("%c is%s a number.\\n", c, isdigit(c) ? "" : " NOT"); + l = (l1 < l2) ? l1 : l2; + s = (a_very_long_variable < an_even_longer_variable) + ? "true" + : "false"; + if (gp.length + (go < 16384 ? 2 : 3) >= name->length) { + /* whatever */ + } + + Okay: + + return ((length1 < length2) ? -1 : 1); + + Bad: + + return (success ? ISC_R_SUCCESS : ISC_R_FAILURE); + +#### Assignment in Parameters + +Variables should not have their values assigned or changed when being +passed as parameters, except perhaps for the increment and decrement +operators. + +Bad: + + isc_mem_get(mctx, size = 20); + +Okay: + + fputc(c++, stdout); + +#### Invalidating Pointers + +When the data a pointer points to has been freed, or is otherwise no longer +valid, the pointer should be set to `NULL` unless the pointer is part of a +structure which is itself going to be freed immediately. + +Good: + + char *text; + + /* text is initialized here. */ + + isc_mem_free(mctx, text); + text = NULL; + +#### Public Interface Namespace + +All public interfaces to functions, macros, typedefs, and variables +provided by the library, should use names of the form +`{library}_{module}_{what}`, such as: + + isc_buffer_t /* typedef */ + dns_name_setbuffer(name, buffer) /* function */ + ISC_LIST_HEAD(list) /* macro */ + isc_commandline_argument /* variable */ + +Structures which are `typedef`'d generally have the name of the typedef +sans the final `_t`: + + typedef struct dns_rbtnode dns_rbtnode_t; + struct dns_rbtnode { + /* ... members ... */ + } + +In some cases, structures are specific to a single C file and are +opaque outside that file. In these cases, the `typedef` occurs in the +associated header file, but the structure definition in the C file +itself. Examples of this include the zone object `dns_zone_t`; +the structure is only acessable via get/set functions in +`lib/dns/zone.c`. Other times, structure members can be accessed +from outside the C file where they are implemented; examples include +`dns_view_t`. Which way to implement a particular object is up to +the developer's discretion. + +Generally speaking, macros are defined with all capital letters, but this +is not universally consistent (eg, numerous `isc_buffer_{foo}` macros). + +The `{module}` and `{what}` segments of the name do not have underscores +separating natural word elements, as demonstrated in +`isc_commandline_argument` and `dns_name_setbuffer` above. The `{module}` +part is usually the same as the basename of the source file, but sometimes +other `{module}` interfaces appear within one file, such as `dns_label_*` +interfaces in `lib/dns/name.c`. However, in the public libraries the file +name must be the same as some module interface provided by the file; e.g., +`dns_rbt_*` interfaces would not be declared in a file named redblack.c (in +lieu of any other `dns_redblack_*` interfaces in the file). + +The one notable exception to this naming rule is the interfaces provided by +``. There's a large caveat associated with the public +description of this file that it is hazardous to use because it pollutes +the general namespace. + +When the signature of a public function needs to change, the old function +name should be retained for backward compatibility, if at all possible. +For example, when `dns_zone_setfile()` needed to include a file format +parameter, it was changed to `dns_zone_setfile2()`; the original function +name became a wrapper for the new function, calling it with the default +value of the format parameter: + + isc_result_t + dns_zone_setfile(dns_zone_t *zone, const char *file) { + return (dns_zone_setfile2(zone, file, dns_masterformat_text); + } + + isc_result_t + dns_zone_setfile2(dns_zone_t *zone, const char *file, + dns_masterformat_t format) + { + ... + } + +#### Shared Private Interfaces + +When a module provides an interface for internal use by other modules in +the library or by unit tests, it should use the same naming convention +described for the public interfaces, except `{library}` and `{module}` are +separated by a double-underscore. This indicates that the name is +internal, its API is not as formal as the public API, and thus it might +change without any sort of notice. Examples of this usage include +`dns__zone_loadpending()` and `isc__taskmgr_ready()`. + +In many cases, a public interface is instantiated by a private back-end +implementation. The double-underscore naming style is sometimes used in +that situation; for example, `isc_task_attach()` calls the `attach` +function provided by a task API implementation; in BIND 9, this function +is provided by `isc__task_attach()`. + +Other times, private interface implementations are static functions +that are pointed to by "method" tables. For example, the `dns_db` +interface is implemented in several places, including `lib/dns/rbtdb.c` +(the red-black tree database used for internal storage of zones and +cache data) and `lib/dns/sdlz.c` (an interface to DLZ modules). +An object of type `dns_dbmethods_t` is created for each of these, +containing function pointers to the local implementations of each +of the `dns_db` API functions. The `dns_db_findnode()` function +is provided by static functions called `findnode()` in each file, +and so on. + +#### Initialization + +When an object is allocated from the heap, all fields in the object must be +initialized. + +#### Dead Code Pruning + +Source which becomes obsolete should be removed, not just disabled with +`#if 0 ... #endif`. + +#### Portability + +When using a C library function, consider whether all operating systems +support it. Is it in the POSIX standard? If so, how long has it been +there? (BIND is still run on some operating systems released in the +1990s.) Is its behavior the same on all platforms? Is its signature +the same? Are integer parameters the same size and signedness? Does it +alwasy return the same values on success, and set the same `errno` codes +on failure? + +If there is a chance the library call may not be completely portable, +edit `configure.in` to check for it on the local system and only call +it from within a suitable `#ifdef`. If the function is nonoptional, +it may be necessary to add your own implentation of it (or copy one +from a source with a BSD-compatible license). + +BIND provides portable internal versions of many common library calls. +Some are designed to ensure that library calls have standardized +[ISC result codes](dev.html#results) instead of using potentially +nonwportable `errno` values; these include the file operations +in `isc_file` and `isc_stdio`. Others, such as `isc_tm_strptime()`, +are needed to ensure consistent cross-platform behavior. +Others simply provide needed functions on platforms that don't +have them: for example, `isc_string_strlcpy()` is an implementation +of the BSD-specific `strlcpy()` function. On Linux and systems +without a `strlcpy()` function, it is `#define`d to `isc_string_strlcpy()` + +In some cases, UNIX and Windows implementations of functions are kept +in separate files, such as `lib/isc/unix/file.c` and `lib/isc/win32/file.c`. + +#### Some notes on standard functions + +* Always use `memmove()` rather than `memcpy()`. +* If using `snprintf()` in a source file, be sure it includes `` + +#### Log messages + +Error and warning messages should be logged through the [logging +system](dev.html#logging). Debugging `printf`s may be used during +development, but must be removed when the debugging is finished. + +Log messages do not start with a capital letter, nor do they end in a +period, and they are not followed by newlines. + +When variable text such as a file name or domain name occurs as part of a +log message, it should be enclosed in single quotes, as in "zone '%s' is +lame". + +When the variable text forms a separate phrase, such as when it separated +from the rest of the message by a colon, it can be left unquoted: + + isc_log_write(... "open: %s: %s", filename, isc_result_totext(result)); + +File names (`__FILE__`), line numbers (`__LINE__`), function names, +memory addresses, and other references to program internals may be used +in debugging messages and in messages to report programming errors detected +at runtime. They may not be used in messages that indicate errors in the +program's inputs or operation. + +### Python + +BIND 9 contains some optional tools written in Python, in the `bin/python` subdirectory. Python scripts are stored in the git repository as `{toolname}.py.in`; and `{toolname}.py` will be generated by `configure` (which determines, among other things, the path to the Python interpreter). + +For Python coding, we abide by the Python style guidelines described [here](http://www.python.org/dev/peps/pep-0008/), with a few modifications: + +* The `__init__()` method should always be the first one declared in a + class definition, like so: + + class Foo: + # constructor definition here + def __init__(self): + ... + # other functions may follow + def bar(self): + ... + Close all file and socket objects + +* All Python standard library objects that have an underlying file + descriptor (fd) should be closed explicitly using the `.close()` method. + +* In cases where a file is opened and closed in a single block, it + is often preferable to use the `with` statement: + + with open('filename') as f: + do_something_with(f) + +### Perl + +Perl is NOT required for building, installing, or using the BIND 9 name +server. However, BIND 9 may use Perl for its system test environment, for +certain optional server add-on components, and in some cases for generating +source files (such as `bind9.xsl.h`, converted from `bind9.xsl`) which are +then committed to to the git repository. + +Perl 5 is assumed; Perl scripts do not need to work in Perl 4. + +Perl source code should follow the conventions for C source code where +applicable. + +### Bourne Shell + +Shell scripts must be as portable as possible and should therefore conform +strictly to POSIX standards. Shell extensions such as those introduced in +Bash should be avoided. Some pitfalls to avoid: + +* To capture the output of a command, use `` `backquotes` `` rather than + `$(parentheses)` +* For arithmetical computation, use `` `expr {expression}` ``, not + `$((expression))` +* To text string length use `` `expr $string : ".*"` `` rather than `` + `expr length $string` `` +* To test for the presence of a string in a file without printing anything + to stdout, use `"grep string filename > /dev/null 2>&1"`, rather than + `"grep -q string filename"`. +* To test for file existence use `"test -f"` rather than `"test -e"` +* Don't use newline (`\\n`) when calling `echo`. Either use another `echo` + statement, or use `"cat << EOF"`. +* To set a variable from outside awk, use `"awk '{...}' var=value"` rather + than `"awk -vvar=value '{...}'"` + diff --git a/util/copyrights b/util/copyrights index bab8d1d0de..9168bdc7dc 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2554,6 +2554,11 @@ ./doc/doxygen/isc-footer.html HTML 2006,2007 ./doc/doxygen/isc-header.html HTML 2006,2007 ./doc/doxygen/mainpage X 2006 +./doc/markdown/.gitignore X 2014 +./doc/markdown/Makefile MAKE 2014 +./doc/markdown/dev.mkd MKD 2014 +./doc/markdown/howto.mkd MKD 2014 +./doc/markdown/style.mkd MKD 2014 ./doc/misc/Makefile.in MAKE 2001,2004,2007,2009,2012 ./doc/misc/dnssec TXT.BRIEF 2000,2001,2002,2004 ./doc/misc/format-options.pl PERL 2001,2004,2007,2012 diff --git a/util/kit.sh b/util/kit.sh index ac139a4e65..c7794f9030 100644 --- a/util/kit.sh +++ b/util/kit.sh @@ -145,7 +145,7 @@ fi # gets accidentally resurrected. rm -rf TODO EXCLUDED conftools doc/design doc/dev doc/draft doc/expired \ - doc/html doc/rfc doc/todo doc/private bin/lwresd doc/man \ + doc/html doc/rfc doc/todo doc/private bin/lwresd doc/man doc/markdown \ lib/lwres/man/resolver.5 contrib/zkt/doc/rfc5011.txt \ bin/tests/system/relay lib/cfg diff --git a/util/update_copyrights b/util/update_copyrights index a87cf04f80..f2cea8cf7d 100644 --- a/util/update_copyrights +++ b/util/update_copyrights @@ -15,8 +15,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id$ - require 5.002; # Map copyright owners to the files containing copyright messages. @@ -213,6 +211,11 @@ foreach $file (keys %file_types) { $start_comment = "\n"; + } elsif ($type eq "MKD") { + $sgml_comment = 1; + $start_comment = "\n"; } elsif ($type eq "TXT") { $prefix = ""; } else {