ooni / probe

OONI Probe network measurement tool for detecting internet censorship
https://ooni.org/install
BSD 3-Clause "New" or "Revised" License
758 stars 142 forks source link

torsf: improvements over initial proof of concept #1686

Open bassosimone opened 3 years ago

bassosimone commented 3 years ago

This is an umbrella/epic issue detailing the next steps for the torsf experiment:

We introduced the torsf experiment in https://github.com/ooni/probe-cli/pull/387 and https://github.com/ooni/spec/pull/218.

cohosh commented 1 year ago

After our meeting, I looked into using Snowflake without Tor and it's much easier than I originally thought. Ever since we implemented a Go API that was motivated by the v2.1 PT specification, it is easy to do so just by calling Snowflake as a library. Consider the following Go code:

package main

import (
    "log"

    sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/client/lib"
)

func main() {

    config := sf.ClientConfig{
        BrokerURL:   "https://snowflake-broker.torproject.net.global.prod.fastly.net/",
        FrontDomain: "foursquare.com",
        ICEAddresses: []string{
            "stun:stun.l.google.com:19302",
            "stun:stun.antisip.com:3478",
            "stun:stun.bluesip.net:3478",
            "stun:stun.dus.net:3478",
            "stun:stun.epygi.com:3478",
            "stun:stun.sonetel.com:3478",
            "stun:stun.uls.co.za:3478",
            "stun:stun.voipgate.com:3478",
            "stun:stun.voys.nl:3478",
        },
        UTLSClientID:      "hellorandomizedalpn",
        BridgeFingerprint: "8838024498816A039FCBBAB14E6F40A0843051FA",
        Max:               1,
    }
    transport, err := sf.NewSnowflakeClient(config)
    if err != nil {
        log.Fatal("Failed to start snowflake transport: ", err)
    }

    conn, err := transport.Dial()
    if err != nil {
        log.Printf("dial error: %s", err)
        return
    }
    _, err = conn.Write([]byte("Nonsense"))
    if err != nil {
        log.Printf(err.Error())
    }
    b := make([]byte, 1)
    _, err = conn.Read(b)
    log.Printf("Connection to bridge closed with %s", err)
    defer conn.Close()

}

The trick is to trigger a response from the bridge. If we don't successfully read bytes from the connection returned by transport.Dial, then there's no guarantee we've actually reached the bridge. For this example, I just wrote enough "nonsense" bytes to trigger the bridge to close the connection. There might be a more gentle way of doing this, it's probably worth asking someone from the network team.

This is going to drastically decrease the test times for Snowflake and the incidences of errors caused by Tor crashing. I also think it will give us all the information we need from OONI tests, which is whether the bridge is reachable and the amount of time it takes to get a working Snowflake proxy to form that connection.

Doing things this way should also make it really easy to integrate the event channel API as well.

cohosh commented 1 year ago

If we don't successfully read bytes from the connection returned by transport.Dial, then there's no guarantee we've actually reached the bridge.

I should amend this to saying that if we don't either read bytes or receive an EOF, which is what is happening here in this case.