15 ms·
A year of Rust and DNS
- nickpsecurity 10y agoWith Ironsides, that makes at least two of you using safest languages you can find to try to improve DNS. Good goal and tool to pick. :) http://ironsides.martincarlisle.com/ http://ironsides.martincarlisle.com/ "Take a look at the full list sometime, I think roughly 50% of those could have been avoided by using (safe) Rust." This is true for most vulnerabilities I see in C-related apps. We also know there's techniques to prevent that with acceptable performance. So, outside hobbyist or non-critical stuff, I tell people to use a different language to get the baseline of quality/security up. DNS is kind of example that makes it more true. Now, I haven't learned Rust yet, so I won't be able to fully appreciate the article or spot coding ideas until I do. What I did spot was that you slogged through all the RFC's implementing and testing what you thought should be in the DNS system. Aside from a DNS server/client, I think one of most valuable things you could do in this project is create a single specification of various things in DNS that cites parts of RFC's or advice from real-world implementations to justify each part. Kind of a walkthrough for other people creating DNS's that gives them high-level view and/or drill-down into details of something. "While in the pit of dispair" "Then I climbed saviors peak" Haha. Good job getting through all the work as most people quit that I can tell. "It’s currently not used in production (as far as I know). I’ve put a lot of work into validating correctness of what is going on" It's good you have gone through the specs and have plenty of features. You did hit on a risk area that might need to be in this section: difference between the specs and real-world implementations that you have to work with. The differences could cause your server to fail. If those exist in DNS, too, there might already be write-ups on common ones out there. Biggest way to find them, though, is running combo's of popular clients and servers against each other in testing. So, that's a possible consideration for the future.
- bluejekyll 10y ago> With Ironsides, that makes at least two of you using safest languages you can find to try to improve DNS I looked at that a while ago, I should take another. Thanks for pointing that out. > I think one of most valuable things you could do in this project is create a single specification of various things in DNS that cites parts of RFC's or advice from real-world implementations to justify each part. Kind of a walkthrough for other people creating DNS's that gives them high-level view and/or drill-down into details of something. I was thinking of doing something like this, but you have an even better idea here. Actual references back to the code. I might start trying to do that. (Though that's probably as much work, if not more, as writing the software ;) > Biggest way to find them, though, is running combo's of popular clients and servers against each other in testing. So, that's a possible consideration for the future. Yeah, I've wanted to stand up some automated tests with the most popular systems out there, but there's a time issue here. If others are inspired and want to join the project, I'd love help in this area!
- voltagex_ 10y agoHappy to help with that (although I've uttered that phrase way too much this month). I've got a toy DNS server and I also hit the problem of specs not matching reality (whoda-thunkit). Wireshark's DNS dissector is exceptionally useful here. I agree that it's a project in and of itself.
- e12e 10y ago> I was thinking of doing something like this, but you have an even better idea here. Actual references back to the code. I might start trying to do that. Literate programming (in the Knuth sense): yay! > (Though that's probably as much work, if not more, as writing the software ;) Yep. Hence one of the few big projects I'm aware of that use lp (apart from Knuth) is: Axiom: http://axiom.axiom-developer.org/axiom-website/books.html http://axiom.axiom-developer.org/axiom-website/books.html Ed: from the post: > (And writing this post helped uncover a bug, so even if no one reads this, it was worth it). Literate programming: yay! Or perhaps narrative-driven design: ndd, to go with tdd and bdd...
- spc476 10y agoI marked the RFC where each RR is defined (https://github.com/spc476/SPCDNS/blob/master/src/dns.h https://github.com/spc476/SPCDNS/blob/master/src/dns.h) in my DNS decoding library (in C: https://github.com/spc476/SPCDNS https://github.com/spc476/SPCDNS). I've also referenced RFCs (in comments) in the main code (https://github.com/spc476/SPCDNS/blob/master/src/codec.c https://github.com/spc476/SPCDNS/blob/master/src/codec.c) so someone going through the code knows where to look. And just to note: I separated the network portion from the encoder/decoder functions. It's easier to integrate that way.
- deleted 10y ago[deleted]
- kenOfYugen 10y agoAn inspirational side-project. > I want to get a DNS fuzzer running against it to really pound on it, and then get some benchmark and comparison tests against other servers. Even if the benchmarks turn out to be not in favor of Trust-DNS, the added safety overhead (if any) should be worth it. > (I’ll try to post more regularly on progress) Please do. I thoroughly enjoyed the content as well as the writing style!
- bluejekyll 10y ago>Please do. I thoroughly enjoyed the content as well as the writing style! Thanks!
- SomeCallMeTim 10y agoLove the idea of reimplementing DNS in Rust. Would love to see more efforts like this so that we have secure-by-design language implementation of core security services. But BIND isn't just failing because "it's written in C", it's failing because it's written in terrible C. That said, "terrible C" is probably most every C routine written by someone with less than 10 years of solid low level experience, so "writing good C code" is not very scalable. There are active, solid projects with very few security exploits that are written entirely in C. Nginx comes to mind. The article makes a brief reference to DJBDNS, which is written in C, but has suffered zero security exploits [1], and is extremely performant. And it is being used in production, so presumably if it had any exploits they would have been exposed by now. But...DJB's code is (usually?) released under a rather unfriendly (though mostly open) license, and DJBDNS hasn't been updated in some time, so a more modern project isn't a bad idea. And Rust developers can probably write solid code with only a few years of programming experience, which makes it easier to extend without adding security holes on a weekly basis (coughBINDcoughOPENSSLcough)... [1] http://cr.yp.to/djbdns/guarantee.html http://cr.yp.to/djbdns/guarantee.html
- bradleyjg 10y agoI thought DJB generally donates his code to the public domain. Hard to be "friendlier" than that.
- deleted 10y ago[deleted]
- feld 10y agoPublic domain code is actually illegal in some places, if I recall correctly
- davidu 10y agoNot quite. As legal explained to me -- it's too legally ambiguous for some entities to feel comfortable using because your only fall back on public domain is copyright, which can be overly restrictive, and the usage rights are undefined and enforcement or rights can be applied or modified at will by the copyright holder in the absence of a clear license. And that copyright, even in the public domain, often exists inherently. That's not an issue for me at my $corporation because they are sane, but apparently that's the argument.
- killbrad 10y agoIsn't trying to write a secure implementation of DNS as it is today, like adding a chain to your door? Mostly for show?
- Retra 10y agoWhy do you think that?
- johncolanduoni 10y agoSecurity of DNS the protocol (and the way it's deployed) is different from not exposing the computer running your DNS server to attacks. This is trying to solve the latter, which seems like a good idea.
- viraptor 10y agobluejekyll, have you thought about splitting the server into separate crates? (for example the parsing, the dnssec validation, the forwarding/resolving part, etc.) I'm planning to write a purely forwarding/reporting DNS server with white/black-lists and it looks like I could reuse 99% of code from you. But unless I'm missing something, the whole of the server is in one crate/repo. I did see that it's split into one lib and one bin, but haven't looked into how reusable that lib is yet.
- bluejekyll 10y agoI have, and specifically for purposes like this. Honestly, it just hasn't been a high priority though. At least for the client/server code, I think you'd end up with 90% of all the same dependencies, so I don't know exactly how I'd divide it up. If you have ideas, definitely let me know!
- DigitalJack 10y agoJust curious. Is this the normal way: let recursion_available = (0b1000_0000 & r_z_ad_cd_rcod) == 0b1000_0000 That just seems redundant. If you declared the var as a Boolean, would the == 0bxxxx part have been necessary?
- chrismorgan 10y agoIf r_z_ad_cd_rcod is of type u8, then 0b1000_0000 & r_z_ad_cd_rcod is also of type u8, containing the value 0 or 128. It is (by design) not possible to simply cast a number to a bool in Rust; comparing it with some value is the way you are required to do this. You can do this with `== 0b1000_0000` if you want, or `!= 0` would do just as well. Rust is deliberately explicit about conversions between types; it’s less error-prone, though often more verbose. It’s a part of the language’s philosophy.
- cpeterso 10y agoThis seems like a good use case for a bit mask query function. I see Rust has a bitflags crate: https://doc.rust-lang.org/bitflags/bitflags/index.html https://doc.rust-lang.org/bitflags/bitflags/index.html
- bluejekyll 10y agoI found that after I had already written a lot of that code. I haven't had much reason to go back and change it, but any PR's if someone wants to do it, would be accepted.
- deleted 10y ago[deleted]
- vog 10y agoWhat about simply replacing all "== 0b..." with "!= 0"? That would already be a good improvement, both in terms of DRY and readability, without the need of adding another dependency.
- jcranmer 10y agoI don't know if you're aware of this or not, but when implementing standards with dozens of haphazard extensions, often the best way to keep track of all of the extensions is via the IANA assignments (see http://www.iana.org/protocols http://www.iana.org/protocols). For DNS, the main page seems to be here: http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml http://www.iana.org/assignments/dns-parameters/dns-parameter...
- bluejekyll 10y agoYes, I am very familiar with that page, but thanks for posting it! I've used it for double checking many of the record type assignments and the current sets of crypto graphic assignments.
- colemickens 10y agoGreat post, great library. I used it in a CloudFlare dynamic dns client I wrote to play around with Rust. I'm still very new to Rust so these sorts of post are really helpful. Thank you bluejekyll!
- masgui 10y agomuch more fun to write in scala: https://github.com/MasseGuillaume/Scala-DNS/tree/master/src/main/scala/dns https://github.com/MasseGuillaume/Scala-DNS/tree/master/src/...
- voltagex_ 10y agoGreat article. >Implementing rfc1035 was deceivingly easy This stuck out to me - RFC1035 is the first RFC I've read all the way through and had a shot at implementing. Then you realise it's RFCs all the way down and there's no easy way to navigate through all the overridden / deprecated parts.
- nly 10y agoInded. The old RFCs are just the tip of the iceberg, and in the end you have to pretty much ask "What would BIND do?". Zone master files are a great example of this. There's no official grammar and so there are subtle differences in the way different DNS servers parse them. Its also easy to under-estimate and not notice for example, until you've written a lexer or two, that the grammar is context sensitive and ambiguous... so I'd almost guarantee that trust-dns can be made to interpret one differently to BIND, Knot, or unbound.
- nialv7 10y agoNit: Rust doesn't prevent memory leak.
- kevincox 10y agoIt makes it much more difficult. In fact safe rust provides similar guarantees to garbage collected languages as the heap-allocated classes will clean themselves up.
- nialv7 10y agoNot if they have circular references... (I agree that Rust makes it difficult to do this accidentally) Also, remember mem::forget() is not unsafe I'm pointing this out because I don't want people to have wrong expectation of Rust. Rust doesn't prevent memory leak. Rust doesn't consider memory leak to be "unsafe". Even the Rust team themselves have pointed this out multiple times. Somehow people are still repeating that Rust can rid you of memory leaks. And I'm downvoted just because I'm pointing out facts. Sigh.
- steveklabnik 10y agoIt is unfortunate you're downvoted for making a true statement. It's extremely important that people don't see Rust as a panacea.
- topspin 10y agoFortunately it appears this perfectly legitimate comment has been salvaged. Steve, I was disappointed that this topic[1] received no discussion at all at HN. Has anyone inside Mozilla looked at it? The title is a bit click-baity for academic work, in my opinion, but I do think the points raised are worthy. Much like recent criticism of PostgreSQL by Uber, one hopes the purveyors of Rust are also open and respond well to constructive criticism. 1. https://news.ycombinator.com/item?id=12258957 https://news.ycombinator.com/item?id=12258957
- steveklabnik 10y ago
- pjmlp 10y agoAny benchmarks against the MirageOS TCP/IP stack? It would be interesting to see how the way Rust manages memory and its current optimizer match against OCaml in a production TCP/IP stack.
- bluejekyll 10y agoI'd love to get some benchmarks going. It's not as high a priority for me right now as getting to feature complete (my definition of that anyway). The question you pose though is awkward. MirageOS is a unikernel, Trust-DNS is an application, a consumer of the TCP stack. I have seen Rust apps built into rumprun, but as of right now this isn't something on my list, in theory it's possible.
- pjmlp 10y agoIt is not awkward at all. You can build a DNS server with MirageOS and it is used like that in the context to the new Docker versions for Mac OS X and Windows, which is one example of being used in production.
- bsder 10y agoThe only technical comment I have is that it continues to be amazing to me that destructuring binary data is so stupidly verbose in so many languages. I think C and Erlang are the only two languages that got this right.
- deleted 10y ago[deleted]
- masklinn 10y agoErlang I can easily see (destructuring binary data in erlang is an absolute pleasure) but C? Most binary destructuring in C seems to be done with shifts and masks, there's some gain from implicit integral conversions but I don't remember it being "right". IIRC the memory layout of bitfields is not specified so you can't use them to portably destructure binary streams, only to define "packed" structures for memory saving.
- Jtsummers 10y ago> IIRC the memory layout of bitfields is not specified so you can't use them to portably destructure binary streams, only to define "packed" structures for memory saving. This is correct as my colleagues have recently relearned. Moving from one endianness to another seriously complicated their lives for a few weeks (time to initial discovery of error, time to add in new definitions, time to test the updated system).
- bsder 10y ago> IIRC the memory layout of bitfields is not specified so you can't use them to portably destructure binary streams This is true, but for many of us in the embedded world, we use the specific packing anyway. I know my endianness, and I know my compiler--so my structs map straight across.
- dochtman 10y agoAs a ring [1] contributor, would be curious to hear if it could satisfy your crypto requirements. Using it could maybe help with your "(oh-my-dear-god I can not unsee what I saw in there, the C, not Rust)" experience. [1] https://github.com/briansmith/ring https://github.com/briansmith/ring
- bluejekyll 10y agoFunny you should ask, I was just looking at that last week to try and answer that question. I was also thinking of getting my feet wet in ring with some of the easy issues on the github page.
- dochtman 10y agoSounds awesome, looking forward to it! If you are an IRC kind of person, maybe also consider joining the #rust-crypto channel on irc.mozilla.org.
- fulafel 10y agoSee also BIND 10: https://ripe68.ripe.net/presentations/208-The_Decline_and_Fall_of_BIND_10.pdf https://ripe68.ripe.net/presentations/208-The_Decline_and_Fa...
- signa11 10y agowith erlang you can parse the dns-header like so: ,---- | %% extract dns-header fields from a raw packet. | parse_dns_header(raw_packet) -> | << | ID:16, | QR:1, OPCODE:4, AA:1, TC:1, RD:1, RA:1, Z:3, RCODE:4, | QDCOUNT:16, | ANCOUNT:16, | NSCOUNT:16, ARCOUNT:16, | Tail/binary | >> = raw_packet, | | {#dns_header_record { | id = ID, | qr = QR, opcode = OPCODE, aa = AA, tc = TC, rd = RD, ra = RA, z = Z, rcode = RCODE, | qdcount = QDCOUNT, | ancount = ANCOUNT, | nscount = NSCOUNT, | arcount = ARCOUNT | }, Tail}. | `---- almost verbatim 'transliteration' of sec:4.1.1 of 1035 :)
- rkrzr 10y agoSyntax-wise this is brilliant, I think. Very similar to how a good parsing-combinator library looks almost like the grammar it is parsing. I wish more programming languages would make an effort like this to optimize readability and be self-documenting.
- signa11 10y ago> Very similar to how a good parsing-combinator library looks almost like the grammar it is parsing. yup. getting from an ascii art representation of the pdu, to something like this should be fairly trivial :) only if writing fsm's were that easy ;)
- masklinn 10y agoFWIW you can get code-formatting by prefixing lines with a 4-space indent: parse_dns_header(raw_packet) -> << ID:16, QR:1, OPCODE:4, AA:1, TC:1, RD:1, RA:1, Z:3, RCODE:4, QDCOUNT:16, ANCOUNT:16, NSCOUNT:16, ARCOUNT:16, Tail/binary >> = raw_packet, Also for readers, this does depend on customisable (per-segment/field) defaults: `ID:16` is a shorthand for `ID:16/big-unsigned-integer-unit:1` aka "16-bits wide segment parsed as an unsigned integer in big endian"
- signa11 10y ago
- jedisct1 10y agoAnother DNS-related project in Rust: EdgeDNS https://github.com/jedisct1/edgedns https://github.com/jedisct1/edgedns
- arunmu 10y agoWould love to repeat this sometime using pure modern C++.
- detaro 10y agoIt would be really interesting to have a set of extremely similar programs in, let's say, "matured C", modern C++ and Rust for comparison purposes.
- arunmu 10y agoYeah. That would be really nice as these languages are competing for the same ground. Go beats all in ease of programming but something is not quite satisfying with it(my personal opinion). I really like having control of my memory. But that doesn't matter usually for most of the application processes.