"Fossies" - the Fresh Open Source Software Archive

Member "udns-0.4/NOTES" (23 May 2011, 11470 Bytes) of package /linux/misc/dns/udns-0.4.tar.gz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 Assorted notes about udns (library).
    2 
    3 UDP-only mode
    4 ~~~~~~~~~~~~~
    5 
    6 First of all, since udns is (currently) UDP-only, there are some
    7 shortcomings.
    8 
    9 It assumes that a reply will fit into a UDP buffer.  With adoption of EDNS0,
   10 and general robustness of IP stacks, in most cases it's not an issue.  But
   11 in some cases there may be problems:
   12 
   13  - if an RRset is "very large" so it does not fit even in buffer of size
   14    requested by the library (current default is 4096; some servers limits
   15    it further), we will not see the reply, or will only see "damaged"
   16    reply (depending on the server).
   17 
   18  - many DNS servers ignores EDNS0 option requests.  In this case, no matter
   19    which buffer size udns library will request, such servers reply is limited
   20    to 512 bytes (standard pre-EDNS0 DNS packet size).  (Udns falls back to
   21    non-EDNO0 query if EDNS0-enabled one received FORMERR or NOTIMPL error).
   22 
   23 The problem is that with this, udns currently will not consider replies with
   24 TC (truncation) bit set, and will treat such replies the same way as it
   25 treats SERVFAIL replies, thus trying next server, or temp-failing the query
   26 if no more servers to try.  In other words, if the reply is really large, or
   27 if the servers you're using don't support EDNS0, your application will be
   28 unable to resolve a given name.
   29 
   30 Yet it's not common situation - in practice, it's very rare.
   31 
   32 Implementing TCP mode isn't difficult, but it complicates API significantly.
   33 Currently udns uses only single UDP socket (or - maybe in the future - two,
   34 see below), but in case of TCP, it will need to open and close sockets for
   35 TCP connections left and right, and that have to be integrated into an
   36 application's event loop in an easy and efficient way.  Plus all the
   37 timeouts - different for connect(), write, and several stages of read.
   38 
   39 IPv6 vs IPv4 usage
   40 ~~~~~~~~~~~~~~~~~~
   41 
   42 This is only relevant for nameservers reachable over IPv6, NOT for IPv6
   43 queries.  I.e., if you've IPv6 addresses in 'nameservers' line in your
   44 /etc/resolv.conf file.  Even more: if you have BOTH IPv6 AND IPv4 addresses
   45 there.  Or pass them to udns initialization routines.
   46 
   47 Since udns uses a single UDP socket to communicate with all nameservers,
   48 it should support both v4 and v6 communications.  Most current platforms
   49 supports this mode - using PF_INET6 socket and V4MAPPED addresses, i.e,
   50 "tunnelling" IPv4 inside IPv6.  But not all systems supports this.  And
   51 more, it has been said that such mode is deprecated.
   52 
   53 So, list only IPv4 or only IPv6 addresses, but don't mix them, in your
   54 /etc/resolv.conf.
   55 
   56 An alternative is to use two sockets instead of 1 - one for IPv6 and one
   57 for IPv4.  For now I'm not sure if it's worth the complexity - again, of
   58 the API, not the library itself (but this will not simplify library either).
   59 
   60 Single socket for all queries
   61 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   62 
   63 Using single UDP socket for sending queries to all nameservers has obvious
   64 advantages.  First it's, again, trivial, simple to use API.  And simple
   65 library too.  Also, after sending queries to all nameservers (in case first
   66 didn't reply in time), we will be able to receive late reply from first
   67 nameserver and accept it.
   68 
   69 But this mode has disadvantages too.  Most important is that it's much easier
   70 to send fake reply to us, as the UDP port where we expects the reply to come
   71 to is constant during the whole lifetime of an application.  More secure
   72 implementations uses random port for every single query.  While port number
   73 (16 bits integer) can not hold much randomness, it's still of some help.
   74 Ok, udns is a stub resolver, so it expects sorta friendly environment, but
   75 on LAN it's usually much easier to fire an attack, due to the speed of local
   76 network, where a bad guy can generate alot of packets in a short time.
   77 
   78 Spoofing of replies (Kaminsky attack, CVE-2008-1447)
   79 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   80 
   81 While udns uses random numbers for query IDs, it uses single UDP port for
   82 all queries (see previous item).  And even if it used random UDP port for
   83 each query, the attack described in CVE-2008-1447 is still quite trivial.
   84 This is not specific to udns library unfortunately - it is inherent property
   85 of the protocol.  Udns is designed to work in a LAN, it needs full recursive
   86 resolver nearby, and modern LAN usually uses high-bandwidth equipment which
   87 makes the Kaminsky attack trivial.  The problem is that even with qID (16
   88 bits) and random UDP port (about 20 bits available to a regular process)
   89 combined still can not hold enough randomness, so on a fast network it is
   90 still easy to flood the target with fake replies and hit the "right" reply
   91 before real reply comes.  So random qIDs don't add much protection anyway,
   92 even if this feature is implemented in udns, and using all available
   93 techniques wont solve it either.
   94 
   95 See also long comment in udns_resolver.c, udns_newid().
   96 
   97 Assumptions about RRs returned
   98 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   99 
  100 Currently udns processes records in the reply it received sequentially.
  101 This means that order of the records is significant.  For example, if
  102 we asked for foo.bar A, but the server returned that foo.bar is a CNAME
  103 (alias) for bar.baz, and bar.baz, in turn, has address 1.2.3.4, when
  104 the CNAME should come first in reply, followed by A.  While DNS specs
  105 does not say anything about order of records - it's an rrSET - unordered, -
  106 I think an implementation which returns the records in "wrong" order is
  107 somewhat insane...
  108 
  109 CNAME recursion
  110 ~~~~~~~~~~~~~~~
  111 
  112 Another interesting point is the handling of CNAMEs returned as replies
  113 to non-CNAME queries.  If we asked for foo.bar A, but it's a CNAME, udns
  114 expects BOTH the CNAME itself and the target DN to be present in the reply.
  115 In other words, udns DOES NOT RECURSE CNAMES.  If we asked for foo.bar A,
  116 but only record in reply was that foo.bar is a CNAME for bar.baz, udns will
  117 return no records to an application (NXDOMAIN).  Strictly speaking, udns
  118 should repeat the query asking for bar.baz A, and recurse.  But since it's
  119 stub resolver, recursive resolver should recurse for us instead.
  120 
  121 It's not very difficult to implement, however.  Probably with some (global?)
  122 flag to en/dis-able the feature.  Provided there's some demand for it.
  123 
  124 To clarify: udns handles CNAME recursion in a single reply packet just fine.
  125 
  126 Note also that standard gethostbyname() routine does not recurse in this
  127 situation, too.
  128 
  129 Error reporting
  130 ~~~~~~~~~~~~~~~
  131 
  132 Too many places in the code (various failure paths) sets generic "TEMPFAIL"
  133 error condition.  For example, if no nameserver replied to our query, an
  134 application will get generic TEMPFAIL, instead of something like TIMEDOUT.
  135 This probably should be fixed, but most applications don't care about the
  136 exact reasons of failure - 4 common cases are already too much:
  137   - query returned some valid data
  138   - NXDOMAIN
  139   - valid domain but no data of requested type - =NXDOMAIN in most cases
  140   - temporary error - this one sometimes (incorrectly!) treated as NXDOMAIN
  141     by (naive) applications.
  142 DNS isn't yes/no, it's at least 3 variants, temp err being the 3rd important
  143 case!  And adding more variations for the temp error case is complicating things
  144 even more - again, from an application writer standpoint.  For diagnostics,
  145 such more specific error cases are of good help.
  146 
  147 Planned API changes
  148 ~~~~~~~~~~~~~~~~~~~
  149 
  150 At least one thing I want to change for some future version is a way how
  151 queries are submitted and how replies are handled.
  152 
  153 I want to made dns_query object to be owned by an application.  So that instead
  154 of udns library allocating it for the lifetime of query, it will be pre-
  155 allocated by an application.  This simplifies and enhances query submitting
  156 interface, and complicates it a bit too, in simplest cases.
  157 
  158 Currently, we have:
  159 
  160 dns_submit_dn(dn, cls, typ, flags, parse, cbck, data)
  161 dns_submit_p(name, cls, typ, flags, parse, cbck, data)
  162 dns_submit_a4(ctx, name, flags, cbck, data)
  163 
  164 and so on -- with many parameters missed for type-specific cases, but generic
  165 cases being too complex for most common usage.
  166 
  167 Instead, with dns_query being owned by an app, we will be able to separately
  168 set up various parts of the query - domain name (various forms), type&class,
  169 parser, flags, callback...  and even change them at runtime.  And we will also
  170 be able to reuse query structures, instead of allocating/freeing them every
  171 time.  So the whole thing will look something like:
  172 
  173  q = dns_alloc_query();
  174  dns_submit(dns_q_flags(dns_q_a4(q, name, cbck), DNS_F_NOSRCH), data);
  175 
  176 The idea is to have a set of functions accepting struct dns_query* and
  177 returning it (so the calls can be "nested" like the above), to set up
  178 relevant parts of the query - specific type of callback, conversion from
  179 (type-specific) query parameters into a domain name (this is for type-
  180 specific query initializers), and setting various flags and options and
  181 type&class things.
  182 
  183 One example where this is almost essential - if we want to support
  184 per-query set of nameservers (which isn't at all useless: imagine a
  185 high-volume mail server, were we want to direct DNSBL queries to a separate
  186 set of nameservers, and rDNS queries to their own set and so on).  Adding
  187 another argument (set of nameservers to use) to EVERY query submitting
  188 routine is.. insane.  Especially since in 99% cases it will be set to
  189 default NULL.  But with such "nesting" of query initializers, it becomes
  190 trivial.
  191 
  192 This change (the way how queries gets submitted) will NOT break API/ABI
  193 compatibility with old versions, since the new submitting API works in
  194 parallel with current (and current will use the new one as building
  195 blocks, instead of doing all work at once).
  196 
  197 Another way to do the same is to manipulate query object right after a
  198 query has been submitted, but before any events processing (during this
  199 time, query object is allocated and initialized, but no actual network
  200 packets were sent - it will happen on the next event processing).  But
  201 this way it become impossible to perform syncronous resolver calls, since
  202 those calls hide query objects they use internally.
  203 
  204 Speaking of replies handling - the planned change is to stop using dynamic
  205 memory (malloc) inside the library.  That is, instead of allocating a buffer
  206 for a reply dynamically in a parsing routine (or memdup'ing the raw reply
  207 packet if no parsing routine is specified), I want udns to return the packet
  208 buffer it uses internally, and change parsing routines to expect a buffer
  209 for result.  When parsing, a routine will return true amount of memory it
  210 will need to place the result, regardless of whenever it has enough room
  211 or not, so that an application can (re)allocate properly sized buffer and
  212 call a parsing routine again.
  213 
  214 This, in theory, also can be done without breaking current API/ABI, but in
  215 that case we'll again need a parallel set of routines (parsing included),
  216 which makes the library more complicated with too many ways of doing the
  217 same thing.  Still, code reuse is at good level.
  218 
  219 Another modification I plan to include is to have an ability to work in
  220 terms of domain names (DNs) as used with on-wire DNS packets, not only
  221 with asciiz representations of them.  For this to work, the above two
  222 changes (query submission and result passing) have to be completed first
  223 (esp. the query submission part), so that it will be possible to specify
  224 some additional query flags (for example) to request domain names instead
  225 of the text strings, and to allow easy query submissions with either DNs
  226 or text strings.