feat(snowflake): upgrade to v2 (+ small tweaks) (#667)
This diff contains the following changes and enhancements: 1. upgrade snowflake to v2 2. observe that we were not changing defaults from outside of snowflake.go, so remove code allowing to do that; 3. bump the timeout to 600 seconds (it seems 300 was not always enough based on my testing); 4. add useful knob to disable `torsf` progress (it's really annoying on console, we should do something about this); 5. ptx.go: avoid printing an error when the connection has just been closed; 6. snowflake: test AMP cache, see that it's not working currently, so leave it disabled. Related issues: https://github.com/ooni/probe/issues/1845, https://github.com/ooni/probe/issues/1894, and https://github.com/ooni/probe/issues/1917.
This commit is contained in:
+4
-1
@@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -184,7 +185,9 @@ func (lst *Listener) acceptLoop(ctx context.Context, ln ptxSocksListener) {
|
||||
if err, ok := err.(net.Error); ok && err.Temporary() {
|
||||
continue
|
||||
}
|
||||
lst.logger().Warnf("ptx: socks accept error: %s", err)
|
||||
if !errors.Is(err, net.ErrClosed) {
|
||||
lst.logger().Warnf("ptx: socks accept error: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
go lst.handleSocksConn(ctx, conn)
|
||||
|
||||
+25
-45
@@ -4,35 +4,16 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
sflib "git.torproject.org/pluggable-transports/snowflake.git/client/lib"
|
||||
sflib "git.torproject.org/pluggable-transports/snowflake.git/v2/client/lib"
|
||||
"github.com/ooni/probe-cli/v3/internal/stuninput"
|
||||
)
|
||||
|
||||
// SnowflakeDialer is a dialer for snowflake. When optional fields are
|
||||
// not specified, we use defaults from the snowflake repository.
|
||||
type SnowflakeDialer struct {
|
||||
// BrokerURL is the optional broker URL. If not specified,
|
||||
// we will be using a sensible default value.
|
||||
BrokerURL string
|
||||
|
||||
// FrontDomain is the domain to use for fronting. If not
|
||||
// specified, we will be using a sensible default.
|
||||
FrontDomain string
|
||||
|
||||
// ICEAddresses contains the addresses to use for ICE. If not
|
||||
// specified, we will be using a sensible default.
|
||||
ICEAddresses []string
|
||||
|
||||
// MaxSnowflakes is the maximum number of snowflakes we
|
||||
// should create per dialer. If negative or zero, we will
|
||||
// be using a sensible default.
|
||||
MaxSnowflakes int
|
||||
|
||||
// newClientTransport is an optional hook for creating
|
||||
// an alternative snowflakeTransport in testing.
|
||||
newClientTransport func(brokerURL string, frontDomain string,
|
||||
iceAddresses []string, keepLocalAddresses bool,
|
||||
maxSnowflakes int) (snowflakeTransport, error)
|
||||
newClientTransport func(config sflib.ClientConfig) (snowflakeTransport, error)
|
||||
}
|
||||
|
||||
// snowflakeTransport is anything that allows us to dial a snowflake
|
||||
@@ -50,10 +31,14 @@ func (d *SnowflakeDialer) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
func (d *SnowflakeDialer) dialContext(
|
||||
ctx context.Context) (net.Conn, chan interface{}, error) {
|
||||
done := make(chan interface{})
|
||||
txp, err := d.newSnowflakeClient(
|
||||
d.brokerURL(), d.frontDomain(), d.iceAddresses(),
|
||||
false, d.maxSnowflakes(),
|
||||
)
|
||||
txp, err := d.newSnowflakeClient(sflib.ClientConfig{
|
||||
BrokerURL: d.brokerURL(),
|
||||
AmpCacheURL: d.ampCacheURL(),
|
||||
FrontDomain: d.frontDomain(),
|
||||
ICEAddresses: d.iceAddresses(),
|
||||
KeepLocalAddresses: false,
|
||||
Max: d.maxSnowflakes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -83,47 +68,42 @@ func (d *SnowflakeDialer) dialContext(
|
||||
|
||||
// newSnowflakeClient allows us to call a mock rather than
|
||||
// the real sflib.NewSnowflakeClient.
|
||||
func (d *SnowflakeDialer) newSnowflakeClient(brokerURL string, frontDomain string,
|
||||
iceAddresses []string, keepLocalAddresses bool,
|
||||
maxSnowflakes int) (snowflakeTransport, error) {
|
||||
func (d *SnowflakeDialer) newSnowflakeClient(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
if d.newClientTransport != nil {
|
||||
return d.newClientTransport(brokerURL, frontDomain, iceAddresses,
|
||||
keepLocalAddresses, maxSnowflakes)
|
||||
return d.newClientTransport(config)
|
||||
}
|
||||
return sflib.NewSnowflakeClient(
|
||||
brokerURL, frontDomain, iceAddresses,
|
||||
keepLocalAddresses, maxSnowflakes)
|
||||
return sflib.NewSnowflakeClient(config)
|
||||
}
|
||||
|
||||
// ampCacheURL returns a suitable AMP cache URL.
|
||||
func (d *SnowflakeDialer) ampCacheURL() string {
|
||||
// I tried using the following AMP cache and always got:
|
||||
//
|
||||
// 2022/01/19 16:51:28 AMP cache rendezvous response: 500 Internal Server Error
|
||||
//
|
||||
// So I disabled the AMP cache until we figure it out.
|
||||
//
|
||||
//return "https://cdn.ampproject.org/"
|
||||
return ""
|
||||
}
|
||||
|
||||
// brokerURL returns a suitable broker URL.
|
||||
func (d *SnowflakeDialer) brokerURL() string {
|
||||
if d.BrokerURL != "" {
|
||||
return d.BrokerURL
|
||||
}
|
||||
return "https://snowflake-broker.torproject.net.global.prod.fastly.net/"
|
||||
}
|
||||
|
||||
// frontDomain returns a suitable front domain.
|
||||
func (d *SnowflakeDialer) frontDomain() string {
|
||||
if d.FrontDomain != "" {
|
||||
return d.FrontDomain
|
||||
}
|
||||
return "cdn.sstatic.net"
|
||||
}
|
||||
|
||||
// iceAddresses returns suitable ICE addresses.
|
||||
func (d *SnowflakeDialer) iceAddresses() []string {
|
||||
if len(d.ICEAddresses) > 0 {
|
||||
return d.ICEAddresses
|
||||
}
|
||||
return stuninput.AsSnowflakeInput()
|
||||
}
|
||||
|
||||
// maxSnowflakes returns the number of snowflakes to collect.
|
||||
func (d *SnowflakeDialer) maxSnowflakes() int {
|
||||
if d.MaxSnowflakes > 0 {
|
||||
return d.MaxSnowflakes
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
sflib "git.torproject.org/pluggable-transports/snowflake.git/v2/client/lib"
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
@@ -47,7 +48,7 @@ var _ snowflakeTransport = &mockableSnowflakeTransport{}
|
||||
|
||||
func TestSnowflakeDialerWorksWithMocks(t *testing.T) {
|
||||
sfd := &SnowflakeDialer{
|
||||
newClientTransport: func(brokerURL, frontDomain string, iceAddresses []string, keepLocalAddresses bool, maxSnowflakes int) (snowflakeTransport, error) {
|
||||
newClientTransport: func(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
return &mockableSnowflakeTransport{
|
||||
MockDial: func() (net.Conn, error) {
|
||||
return &mocks.Conn{
|
||||
@@ -79,7 +80,7 @@ func TestSnowflakeDialerWorksWithMocks(t *testing.T) {
|
||||
func TestSnowflakeDialerCannotCreateTransport(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sfd := &SnowflakeDialer{
|
||||
newClientTransport: func(brokerURL, frontDomain string, iceAddresses []string, keepLocalAddresses bool, maxSnowflakes int) (snowflakeTransport, error) {
|
||||
newClientTransport: func(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
@@ -95,7 +96,7 @@ func TestSnowflakeDialerCannotCreateTransport(t *testing.T) {
|
||||
func TestSnowflakeDialerCannotCreateConnWithNoContextExpiration(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sfd := &SnowflakeDialer{
|
||||
newClientTransport: func(brokerURL, frontDomain string, iceAddresses []string, keepLocalAddresses bool, maxSnowflakes int) (snowflakeTransport, error) {
|
||||
newClientTransport: func(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
return &mockableSnowflakeTransport{
|
||||
MockDial: func() (net.Conn, error) {
|
||||
return nil, expected
|
||||
@@ -117,7 +118,7 @@ func TestSnowflakeDialerCannotCreateConnWithContextExpiration(t *testing.T) {
|
||||
defer cancel()
|
||||
expected := errors.New("mocked error")
|
||||
sfd := &SnowflakeDialer{
|
||||
newClientTransport: func(brokerURL, frontDomain string, iceAddresses []string, keepLocalAddresses bool, maxSnowflakes int) (snowflakeTransport, error) {
|
||||
newClientTransport: func(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
return &mockableSnowflakeTransport{
|
||||
MockDial: func() (net.Conn, error) {
|
||||
cancel() // before returning to the caller
|
||||
@@ -140,7 +141,7 @@ func TestSnowflakeDialerWorksWithWithCancelledContext(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
sfd := &SnowflakeDialer{
|
||||
newClientTransport: func(brokerURL, frontDomain string, iceAddresses []string, keepLocalAddresses bool, maxSnowflakes int) (snowflakeTransport, error) {
|
||||
newClientTransport: func(config sflib.ClientConfig) (snowflakeTransport, error) {
|
||||
return &mockableSnowflakeTransport{
|
||||
MockDial: func() (net.Conn, error) {
|
||||
cancel() // cause a cancel before we can really have a conn
|
||||
@@ -167,24 +168,3 @@ func TestSnowflakeDialerWorksWithWithCancelledContext(t *testing.T) {
|
||||
t.Fatal("the goroutine did not call close")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnowflakeWeCanSetCustomValues(t *testing.T) {
|
||||
sfd := &SnowflakeDialer{
|
||||
BrokerURL: "antani",
|
||||
FrontDomain: "mascetti",
|
||||
ICEAddresses: []string{"melandri"},
|
||||
MaxSnowflakes: 11,
|
||||
}
|
||||
if sfd.brokerURL() != "antani" {
|
||||
t.Fatal("invalid broker URL")
|
||||
}
|
||||
if sfd.frontDomain() != "mascetti" {
|
||||
t.Fatal("invalid front domain")
|
||||
}
|
||||
if v := sfd.iceAddresses(); len(v) != 1 || v[0] != "melandri" {
|
||||
t.Fatal("invalid ICE addresses")
|
||||
}
|
||||
if sfd.maxSnowflakes() != 11 {
|
||||
t.Fatal("invalid max number of snowflakes")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user