Hello there! This is the fourth in a series of blogposts detailing the work I will be doing for the Tor Project as part of Google Summer of Code 2023.
Here I will detail some of the work I did during some period of time, the challenges I faced and the outcomes of that work.
The project I was selected for is titled “Arti API exploration to build example tools”.
Confused? Let’s break it down.
Arti is the Rust rewrite of the Tor software that allows you to make TCP connections through the Tor Network, run Tor relays and bridges etc. You can read more about it from its repository
Arti was built in mind with the goal that other developers should be able to use Arti to build Tor-powered software, and has incorporated that thinking into its design.
So, it exports some crates that other developers can use in their Rust projects, and has some documentation on them, including some basic demonstration code snippets that you can follow along with at home.
However, Arti is a fairly bleeding-edge project. It didn’t hit version 1.0.0 too long ago, and due to the breakneck speed of development, APIs are not set in stone. There is a lot of breakage that could be potentially encountered by another developer.
In this project, I will be creating certain sample programs in Rust using Arti’s APIs
My goal will be to build my sample programs and document any difficulties that come up.
In this way, the project can get valuable feedback from an outsider who doesn’t have much knowledge of the codebase and the way the Arti proxy does things.
I worked on the notes I’d gotten on the DNS resolver this week,
mainly to document everything. During this process I mainly got used to
the rustdocs
convention of documenting my code and also
setup a clippy rule to warn me in case any method had missing
documentation.
I ended up documenting structs, their fields and a couple functions, as well as adding some introductory text detailing the DNS resolver project’s objective and a warning that this homegrown DNS implementation really wasn’t meant to be used anywhere in production, but rather served as a demonstration that any homegrown network protocol could be subbed in and served over Tor using the Arti APIs.
This was a fun exercise, and I will expand the other projects’ documentation in the coming weeks as well.
In the DNS resolver, there is a trait called FromBytes
that is implemented for the structs Header
(which is the
DNS header represented as a struct), Query
(which is the
DNS header + query), ResourceRecord
(which can be thought
of as containing the answer for a query) and Response
(which is just the header + query + one or more resource records).
This FromBytes
trait defines a method
from_bytes()
which takes in a slice of bytes and returns
the initialized struct. As the name implies, all the values for the
fields will be taken from the bytes passed to this method.
Now, the problem with this method was that it passes the struct no matter what. Even if we give invalid byte sequence to the method, we will get a struct. This is highly dangerous since we don’t have any way to handle invalid byte sequences or convey this message to functions lower down the call stack.
Hence, I decided to change the return type from Self
(the struct itself) to Option<Self>
(Option is an
enum which either returns Some<Self>
or
None
, hence we can denote the error condition using
None
and a valid struct using Some
).
However, since this is a trait, Self
can be a variable
sized struct, and Rust doesn’t like that. So, we wrap it inside a
Box
(really just a very simple pointer setup, the
Box
will contain a reference to wherever the struct is
located on the heap so we can pass the Box
around easily
and cheaply and also be aware of its size)
Hence the final return type becomes
Option<Box<Self>>
which is a cleaner API than
just returning Self
This also has the nice side effect that
the DNS resolver doesn’t crash when I make a request for a domain name
that isn’t registered!
For this week, I intended to work on adding resume capabilities for the download manager, ie, if I were to abruptly cut off network connection and later restart the download manager, I should be able to pick up my download where I left off.
However, I did notice a feature that was in a way more important: retrying requests. See, when I configured the download manager to use Snowflake to download Tor Browser, I would see a lot of failed circuits and timeouts. This also happened when I used a normal Tor connection, but to a much lesser extent.
So, to address this pitfall, I simply decided I wanted to check if we got anything back from the network and if not, just re-run the code that makes the request using recursion.
However, Rust doesn’t permit asynchronous recursive functions, likely because it will make the call stack very weird, so I ended up just doing it iteratively instead. The code now tries up to six times to make the same request and if it doesn’t work after all that, it just gives up.
This has made the download manager much more robust, as I do see that on occasion one or two requests do indeed trigger a retry.
While working on the obfs4 connection checker, it was suggested that
ChanMgr
not be used, and instead a lower level API would be
more suitable, since ChanMgr
does some setup steps that are
just irrelevant for this use case.
The ChannelFactory
trait in tor-chanmgr
defines an interface through which I can build a channel. Exposing this
in ChanMgr
will allow me to directly create a Channel. This
API is exposed in this
MR and is currently not merged.
This week was also fairly eventful, I ended up working on a lot of the review notes I got on the DNS resolver, fix up download manager, learn and work around the Rust compiler’s rules to implement better design practices in my programs and made another MR upstream.
This website was made using Markdown, Pandoc, and a custom program to automatically add headers and footers (including this one) to any document that’s published here.
Copyright © 2024 Saksham Mittal. All rights reserved. Unless otherwise stated, all content on this website is licensed under the CC BY-SA 4.0 International License