[bind10-dev] Plugin proposal

Jelte Jansen jelte at isc.org
Tue Mar 29 09:21:20 UTC 2011


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Just wrote this up, so it's quite fresh, but i thought i'd send it out
while it was hot, and before i get too wrapped up in other issues again
here :)

Proposal for a plugin architecture in BIND10 v0.01a (beta)
- ----------------------------------------------------------

ToC
1. Requirements
2. Outline
3. Similarities in current code
4. Proposed API
5. Python
6. Changes needed
7. Some example ideas for plugins
8. Possible future extensions


Requirements
- ------------

In general, I want the system to allow for things we did not think of.
This means that we should try to give as few restraints as possible,
given the architecture we come up with. I want this to be as flexible as
possible, yet not too hard to actually use and configure. In this
proposal, I think I'll err on the side of 'simple' rather than
'powerful', though I think we can allow for some pretty interesting things.



Outline
- -------

A DNS nameserver receives a query, and sends a response. What this
response is, and how it is built, is normally strictly defined. In
short, the flow is

Receive query -> (do something) -> send response.

Let's expand that do something to multiple somethings first;

Receive query -> [do something, do something, do something] -> send
response.

In the case of our resolver, this would currently be:

Receive query -> [validate query, check cache, do lookup, finalize
answer] -> send response.

What I'm thinking of is simply this: we make those 'do somethings' have
the same fingerprint: SomeResult doSomething(query, answer, context),
and make all of them dynamically loaded. Then we make that list of
dosomethings configurable.

These doSomething calls return a value that tells the system whether to
send the result back now, or to continue to the next doSomething in the
chain.

I think that at least for now, we should make this a fixed,
onedimensional list.
IMHO this is a nice tradeoff between flexibility and simplicity; we
don't need separate explicit callbacks in different parts of the code
and the interface would be pretty tightly defined.

(note that even with a one-dimensional list, one could come up with a
plugin that has multidimensional lists or trees).



Similarities in current code
- ----------------------------

We actually already have part of this, in the form of DNSLookup and
DNSAnswer; DNSLookup performs whatever it needs to do to construct the
answer, and DNSAnswer finalizes it, dotting the i's so we can send it
back. I think we can generalize these, and add some more separation (to
allow for more elements in the list where people can put their own stuff
back in).


Proposed API
- ------------

(This is just a first proposal, and as such this can be extended, but
let me restate that this is for an initial version and I suspect we'll
end up with something a little more extensive.)

Each 'doSomething' needs at least the query, the answer-so-far, some
context about the query, and perhaps some plugin-specific state. I think
it should have write-access to all of these, including the original query.

It would return some hint as to what to do next, which i think can be
quite straightforward:

enum PluginResult {
	OK_CONTINUE,
	OK_STOP,
	ERROR
}

When starting the system, or when adding a plugin to the list, we would
probably want to run some initialization code, that has a the very least
access to the config backend;

/// \return true if init is successful, false if there is a failure
bool pluginInit(ModuleCCSession ccsession);

Similarly, it might want some cleanup;
void pluginCleanup();
(but perhaps we can simply let this be handled by the destructor)

Then the important call would be
PluginResult handle(MessagePtr query_message,
                    MessagePtr answer_message,
                    HandleContext context);

HandleContext is a class that contains per-query info, like the address
of the sender, etc. I think it should also allow for some aribtrary data
that plugins can use to 'communicate' with each other (though we would
have to come up with some rules for this, but see also the example ideas).

This call can modify, the answer, or the question, or it can decide the
system should not continue, and send back a REFUSED for instance. Then
it would return with the PluginResult, and the system would either
continue, send back the answer as it currently is, or bail out (And send
some fixed error answer like SERVFAIL).



Python
- ------

I think we can reasonably easily add python plugin support as well, by
creating one standard 'python' plugin and a bit of magic in how we
configure things; for instance instead of the name of the loadable
module directly, we add a magic keyword, that would load our
python-plugin plugin, which would set itself up with the 'real' python
plguin provided by the user;

i.e. if we had a c++ module 'myACL.so', and we would configure it by
adding 'myACL' to the handlerList config; the python version of myACL
(myACL.py), would be configured by adding 'python:myACL' to the list.



Changes needed
- --------------

First of all, we'd need to add support for dynamically loaded libraries;
we would also need to define a place where we put these libraries, and a
way to specify and load them.

Then we would need to change our current callback to conform to this
API, and we might want to split a bit of functionality into separate
'fixed' plugins.

Then we need to change udp_server and tcp_server to go through the
configure list, instead of calling the DNSLookup and DNSAnswer callbacks
as it does now. We would probably want to move some 'fixed' code which
is currently handled directly (like the validation of the query) to such
a 'fixed' plugin.

And finally, we'd need to define and write the python plugin plugin.



Example ideas
- -------------

Idea 1: Dynamic signer
This plugin would be added after the final step of an authoritative
server, just before sending the response; it would add dynamically
generated DNSSEC data to the answer we have already constructed. It
would not change the normal lookup process in any other way.

Idea 2: Personal ACL
Should our ACL support not be sufficient for a specific user, they can
add their own; by defining a plugin that checks the context for the
sender's address, and if it does not match whatever rule they come up
with, it would clear the answer_message (just in case a previous plugin
has added something), set the answer rcode to REFUSED and return
OK_STOP. Our system would then not process it further but send it back
directly. (OK_STOP instead of ERROR, which is reserved for actualy
code/setup problems and indicate an unexpected status)

Idea 3: TSIG support
Not that we don't intend to implement this anyway, but it could also
work through plugins (or maybe TSIG2 will be, if someone comes up with a
new tsig protocol). For this one could add 2 plugins. The first would
recognize whether there is a TSIG RR, read it, validates it, and if all
is ok, it would strip it from the query, and put a marker somewhere in
the context that this query was signed, and so the response should be
to. The second plugin would live at the end of the list, and would sign
the answer_message with a new TSIG record, before signaling to the
system that the answer can be sent back.



Possible future extensions
- --------------------------

We may need a bit more flexibility in the return values.

More importantly, we might want to add such list in a few more places
(for instance make a similar thing for the actual resolution process,
though that would probably be quite a bit more complicated, as the
resolver naturally needs a lot of assumptions about what it is doing
itself).
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk2RpJAACgkQ4nZCKsdOncW2lQCfVFAx4uRTTXBQcmzTXVDJU75n
B/sAn2CUs3iDPin3gJomTpIjNdasD8N6
=dXvK
-----END PGP SIGNATURE-----



More information about the bind10-dev mailing list