The 'new' architecture implemented after the code was split to ohybridproxy+zonestitcher works as follows: There are essentially 5 components to a DNS 'toy': - main (that deals with setting up the other parts + starts event loop) - socket abstraction (socket.c) - responsible for dealing with (inbound) UDP/TCP connections and the DNS packets there. - generic request abstraction (io.c) - it handles 'requests', which compromise of one or more queries, that are started and stopped. - cache of results / pending requests (cache.c) - storage of rr lists of what we have found from somewhere - backend that actually handles query, and possibly starts sub-queries as well Actual call flow: - main calls backend-specific setup code - main calls io_run(), and then (socket.c) creates sockets and starts uloop - when new request shows up, (~per-session/message) request structure is added and it's regiestered with the cache (cache_register_request). That triggers either start of request (eventual io_req_start), wait for other request, or immediate reply if result is cached (io_send_reply) .. assuming we started new request .. - (io.c) io_req_start calls io_query_start, and then b_query_start (which may fail immediately, or not) .. backend runs the queries, now and then calling io_query_stop to indicate a single query has completed, and storing results in the rrlist of cache entry .. - (io.c) when last io_query_stop is received, cache_entry_completed is called and that results in io_send_reply to the pending request(s); it leads to clean-up path in socket.c even if there is nothing to be said.