Nonchalant Guidance

About Me·RSS·My Projects·LinkedIn


Added on: Sunday, 09 July, 2023 | Updated on: Monday, 21 October, 2024

GSoC 2023 Blog 4

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.

Brief Intro on My Project

The project I was selected for is titled “Arti API exploration to build example tools”.

Confused? Let’s break it down.

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.

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.

Documenting the DNS resolver

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.

Changing return type for parsing methods

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!

Handling timeouts in download manager

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.

More Channel API being exposed

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.

Conclusion

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