Implementing a Tor relay from scratch in golang (2015)
tvdw.eu
tvdw.eu
* yes, more modern versions of Go would likely mitigate some of the memory pain * yes, crypto/tls is fast now * no, crypto/tls still has insufficient functionality for implementing this. crypto/tls implicitly assumes you want to authenticate the channel through certificates, which Tor doesn't do * I was using go 1.4 * yes, I tried Rust
Crypto/tls doesn't support renegotation, which Tor needs, but they are getting rid off.
This could've been mitigated by applying backpressure in a bunch of places, and is ultimately a problem of Tor and not Go, but the nature of Go makes it hard to build code to do that.
As for renegotiation: my work on the Go version of Tor had some nice side-effects, and indeed, renegotiation was finally removed :-) https://gitweb.torproject.org/tor.git/tree/ChangeLog?id=55c4...
I attempted an implementation of Tor in Rust, but because I implemented it in Go a few weeks before that I got bored quickly. That said, some ideas I had for the Rust version have made it to Tor itself (or soon will), such as my ideas on transparently load-balancing Tor hidden services: https://gitweb.torproject.org/torspec.git/tree/proposals/255...
[1] note that in the land of Tor, unpredictable performance (for example because of GC pauses) could lead to user deanonymization.
[0] Russ Cox mentions a ~ 10x performance in a TLS benchmark between 1.5 and 1.6.2 at https://github.com/golang/go/issues/15713
Notably, there were several changes to crypto in 1.5: https://golang.org/doc/go1.5#minor_library_changes
I'd take it any day over openssl, that is for sure. I wish the author had published methodology on the benchmark comparison, would be interesting to dissect that.
When you have to deviate from doing anything that Google/Pike would consider "acceptable" for what they would see as the "plebeian programmer" the STD libs aren't built for it. I'd not say it's a side effect of the language (not even the fact that it's lacking generics). I'd just say it's age. It's too young to be refined.
I know one can use other functions in the same packages to create instances of HTTP servers or flag parsers. But the default approach coming as a part of the std libs will more or less make Go newbies to take it for granted as a programming idiom, which is not the best as the project grows
Granted the cgo stuff and the memory usage would be something that one needs to deal with. Did you talk to go-nuts at all? They might've been able to offer some more insight into all of this, a better way to deal with the cgo related issue and perhaps even make some changes to handle these kinds of cases better.
Yes, it broke the speed record: a multithreaded application outperformed the singlethreaded version. But I wasn't happy with the result. It consumed an order of magnitude more memory, and gc times were potentially harming users (not a widely researched subject, but gc times in low-latency mixnets can likely harm user anonymity). Oh, and it would occasionally crash with OOM errors.
I thought it was rewritten because of the terrible quality of the OpenSSL code, which turned out to be a very good decision.