It appears that several systems (at least MacOS) may send DNS queries to the same server with the same query ID but from different source sockets. Within connlib, we operate multiple DNS servers (one for each upstream) and use the tuple of server address and query ID to remember the necessary state we need to map the response back once we have the response from the upstream server. Given the discovery that this tuple is not necessarily unique, we now need to also track the source socket that _we_ are using to send our queries in order to correctly remember, which socket we need to send the response back to. For that, we extend the layer 3 UDP and TCP clients to return us the socket they are using for each query that we queue. In very specific circumstances, this can still fail. In particular, when connlib receives an SRV or TXT query for a resource, it resolves that query in the context of the resource's site by sending it to port 53535 of the Gateway's TUN device. The Gateway listens to DNS queries on this port and resolves them using its configured system resolvers. It however only listens on a single address, meaning we may be forwarding queries from several of connlib's "servers" to a single query which again may break the uniqueness constraint if two queries with the same ID are received at the same time because we would reuse the TCP connection to the resolver running in the Gateway and thus send them from the same source port. We consider this case to be sufficiently rare and handle it by just failing the 2nd DNS query. There may be ways of resolving it but it requires a bigger refactoring of how we establish TCP connections to upstream resolvers.
Rust development guide
Firezone uses Rust for all data plane components. This directory contains the Linux and Windows clients, and low-level networking implementations related to STUN/TURN.
We target the last stable release of Rust using rust-toolchain.toml.
If you are using rustup, that is automatically handled for you.
Otherwise, ensure you have the latest stable version of Rust installed.
Reading Client logs
The Client logs are written as JSONL for machine-readability.
To make them more human-friendly, pipe them through jq like this:
cd path/to/logs # e.g. `$HOME/.cache/dev.firezone.client/data/logs` on Linux
cat *.log | jq -r '"\(.time) \(.severity) \(.message)"'
Resulting in, e.g.
2024-04-01T18:25:47.237661392Z INFO started log
2024-04-01T18:25:47.238193266Z INFO GIT_VERSION = 1.0.0-pre.11-35-gcc0d43531
2024-04-01T18:25:48.295243016Z INFO No token / actor_name on disk, starting in signed-out state
2024-04-01T18:25:48.295360641Z INFO null
Benchmarking on Linux
The recommended way for benchmarking any of the Rust components is Linux' perf utility.
For example, to attach to a running application, do:
- Ensure the binary you are profiling is compiled with the
releaseprofile. sudo perf record -g --freq 10000 --pid $(pgrep <your-binary>).- Run the speed test or whatever load-inducing task you want to measure.
sudo perf script > profile.perf- Open profiler.firefox.com and load
profile.perf
Instead of attaching to a process with --pid, you can also specify the path to executable directly.
That is useful if you want to capture perf data for a test or a micro-benchmark.