refactor(netx): remove the self censorship mechanism (#364)
We're currently use jafar for QA and jafar is a better mechanism, even though it is not portable outside of Linux. This self censorship mechanism was less cool and added a bunch of (also cognitive) complexity to netx. If we ever want to go down a self censorship like road, we probably want to do as little work as possible in the problem and as much work as possible inside a helper like jafar. Part of https://github.com/ooni/probe/issues/1591.
This commit is contained in:
parent
c553afdbd5
commit
adbde7246b
|
@ -19,7 +19,6 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine"
|
"github.com/ooni/probe-cli/v3/internal/engine"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/legacy/assetsdir"
|
"github.com/ooni/probe-cli/v3/internal/engine/legacy/assetsdir"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/humanize"
|
"github.com/ooni/probe-cli/v3/internal/humanize"
|
||||||
"github.com/ooni/probe-cli/v3/internal/kvstore"
|
"github.com/ooni/probe-cli/v3/internal/kvstore"
|
||||||
"github.com/ooni/probe-cli/v3/internal/version"
|
"github.com/ooni/probe-cli/v3/internal/version"
|
||||||
|
@ -41,7 +40,6 @@ type Options struct {
|
||||||
Proxy string
|
Proxy string
|
||||||
Random bool
|
Random bool
|
||||||
ReportFile string
|
ReportFile string
|
||||||
SelfCensorSpec string
|
|
||||||
TorArgs []string
|
TorArgs []string
|
||||||
TorBinary string
|
TorBinary string
|
||||||
Tunnel string
|
Tunnel string
|
||||||
|
@ -108,10 +106,6 @@ func init() {
|
||||||
&globalOptions.ReportFile, "reportfile", 'o',
|
&globalOptions.ReportFile, "reportfile", 'o',
|
||||||
"Set the report file path", "PATH",
|
"Set the report file path", "PATH",
|
||||||
)
|
)
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.SelfCensorSpec, "self-censor-spec", 0,
|
|
||||||
"Enable and configure self censorship", "JSON",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
getopt.FlagLong(
|
||||||
&globalOptions.TorArgs, "tor-args", 0,
|
&globalOptions.TorArgs, "tor-args", 0,
|
||||||
"Extra args for tor binary (may be specified multiple times)",
|
"Extra args for tor binary (may be specified multiple times)",
|
||||||
|
@ -305,9 +299,6 @@ func MainWithConfiguration(experimentName string, currentOptions Options) {
|
||||||
extraOptions := mustMakeMap(currentOptions.ExtraOptions)
|
extraOptions := mustMakeMap(currentOptions.ExtraOptions)
|
||||||
annotations := mustMakeMap(currentOptions.Annotations)
|
annotations := mustMakeMap(currentOptions.Annotations)
|
||||||
|
|
||||||
err := selfcensor.MaybeEnable(currentOptions.SelfCensorSpec)
|
|
||||||
fatalOnError(err, "cannot parse --self-censor-spec argument")
|
|
||||||
|
|
||||||
logger := &log.Logger{Level: log.InfoLevel, Handler: &logHandler{Writer: os.Stderr}}
|
logger := &log.Logger{Level: log.InfoLevel, Handler: &logHandler{Writer: os.Stderr}}
|
||||||
if currentOptions.Verbose {
|
if currentOptions.Verbose {
|
||||||
logger.Level = log.DebugLevel
|
logger.Level = log.DebugLevel
|
||||||
|
@ -323,7 +314,7 @@ func MainWithConfiguration(experimentName string, currentOptions Options) {
|
||||||
homeDir := gethomedir(currentOptions.HomeDir)
|
homeDir := gethomedir(currentOptions.HomeDir)
|
||||||
fatalIfFalse(homeDir != "", "home directory is empty")
|
fatalIfFalse(homeDir != "", "home directory is empty")
|
||||||
miniooniDir := path.Join(homeDir, ".miniooni")
|
miniooniDir := path.Join(homeDir, ".miniooni")
|
||||||
err = os.MkdirAll(miniooniDir, 0700)
|
err := os.MkdirAll(miniooniDir, 0700)
|
||||||
fatalOnError(err, "cannot create $HOME/.miniooni directory")
|
fatalOnError(err, "cannot create $HOME/.miniooni directory")
|
||||||
|
|
||||||
// We cleanup the assets files used by versions of ooniprobe
|
// We cleanup the assets files used by versions of ooniprobe
|
||||||
|
|
|
@ -20,8 +20,8 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/archival"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/archival"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/errorx"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/errorx"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/randx"
|
"github.com/ooni/probe-cli/v3/internal/randx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -319,11 +319,11 @@ type Dialer struct {
|
||||||
// DialContext dials a specific connection and arranges such that
|
// DialContext dials a specific connection and arranges such that
|
||||||
// headers in the outgoing request are transformed.
|
// headers in the outgoing request are transformed.
|
||||||
func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
dialer := d.Dialer
|
child := d.Dialer
|
||||||
if dialer == nil {
|
if child == nil {
|
||||||
dialer = selfcensor.DefaultDialer
|
child = dialer.Default
|
||||||
}
|
}
|
||||||
conn, err := dialer.DialContext(ctx, network, address)
|
conn, err := child.DialContext(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type dialManager struct {
|
type dialManager struct {
|
||||||
|
@ -36,7 +35,7 @@ func newDialManager(ndt7URL string, logger model.Logger, userAgent string) dialM
|
||||||
func (mgr dialManager) dialWithTestName(ctx context.Context, testName string) (*websocket.Conn, error) {
|
func (mgr dialManager) dialWithTestName(ctx context.Context, testName string) (*websocket.Conn, error) {
|
||||||
var reso resolver.Resolver = resolver.SystemResolver{}
|
var reso resolver.Resolver = resolver.SystemResolver{}
|
||||||
reso = resolver.LoggingResolver{Resolver: reso, Logger: mgr.logger}
|
reso = resolver.LoggingResolver{Resolver: reso, Logger: mgr.logger}
|
||||||
var dlr dialer.Dialer = selfcensor.SystemDialer{}
|
var dlr dialer.Dialer = dialer.Default
|
||||||
dlr = dialer.TimeoutDialer{Dialer: dlr}
|
dlr = dialer.TimeoutDialer{Dialer: dlr}
|
||||||
dlr = dialer.ErrorWrapperDialer{Dialer: dlr}
|
dlr = dialer.ErrorWrapperDialer{Dialer: dlr}
|
||||||
dlr = dialer.LoggingDialer{Dialer: dlr, Logger: mgr.logger}
|
dlr = dialer.LoggingDialer{Dialer: dlr, Logger: mgr.logger}
|
||||||
|
|
24
internal/engine/netx/dialer/system.go
Normal file
24
internal/engine/netx/dialer/system.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultNetDialer is the net.Dialer we use by default.
|
||||||
|
var defaultNetDialer = &net.Dialer{
|
||||||
|
Timeout: 15 * time.Second,
|
||||||
|
KeepAlive: 15 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemDialer is the system dialer.
|
||||||
|
type SystemDialer struct{}
|
||||||
|
|
||||||
|
// DialContext implements Dialer.DialContext
|
||||||
|
func (d SystemDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return defaultNetDialer.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default is the dialer we use by default.
|
||||||
|
var Default = SystemDialer{}
|
20
internal/engine/netx/dialer/system_test.go
Normal file
20
internal/engine/netx/dialer/system_test.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ooni/psiphon/oopsi/golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSystemDialer(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel() // fail immediately
|
||||||
|
conn, err := Default.DialContext(ctx, "tcp", "8.8.8.8:853")
|
||||||
|
if err == nil || !strings.HasSuffix(err.Error(), "operation was canceled") {
|
||||||
|
t.Fatal("not the error we expected", err)
|
||||||
|
}
|
||||||
|
if conn != nil {
|
||||||
|
t.Fatal("expected nil conn here")
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockQUICDialer struct{}
|
type MockQUICDialer struct{}
|
||||||
|
@ -42,7 +42,7 @@ func TestHTTP3TransportSNI(t *testing.T) {
|
||||||
namech := make(chan string, 1)
|
namech := make(chan string, 1)
|
||||||
sni := "sni.org"
|
sni := "sni.org"
|
||||||
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
||||||
Dialer: selfcensor.SystemDialer{}, QUICDialer: MockSNIQUICDialer{namech: namech}, TLSConfig: &tls.Config{ServerName: sni}})
|
Dialer: dialer.Default, QUICDialer: MockSNIQUICDialer{namech: namech}, TLSConfig: &tls.Config{ServerName: sni}})
|
||||||
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -67,7 +67,7 @@ func TestHTTP3TransportSNINoVerify(t *testing.T) {
|
||||||
namech := make(chan string, 1)
|
namech := make(chan string, 1)
|
||||||
sni := "sni.org"
|
sni := "sni.org"
|
||||||
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
||||||
Dialer: selfcensor.SystemDialer{}, QUICDialer: MockSNIQUICDialer{namech: namech}, TLSConfig: &tls.Config{ServerName: sni, InsecureSkipVerify: true}})
|
Dialer: dialer.Default, QUICDialer: MockSNIQUICDialer{namech: namech}, TLSConfig: &tls.Config{ServerName: sni, InsecureSkipVerify: true}})
|
||||||
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -89,7 +89,7 @@ func TestHTTP3TransportCABundle(t *testing.T) {
|
||||||
certch := make(chan *x509.CertPool, 1)
|
certch := make(chan *x509.CertPool, 1)
|
||||||
certpool := x509.NewCertPool()
|
certpool := x509.NewCertPool()
|
||||||
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
||||||
Dialer: selfcensor.SystemDialer{}, QUICDialer: MockCertQUICDialer{certch: certch}, TLSConfig: &tls.Config{RootCAs: certpool}})
|
Dialer: dialer.Default, QUICDialer: MockCertQUICDialer{certch: certch}, TLSConfig: &tls.Config{RootCAs: certpool}})
|
||||||
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -114,7 +114,7 @@ func TestHTTP3TransportCABundle(t *testing.T) {
|
||||||
|
|
||||||
func TestUnitHTTP3TransportSuccess(t *testing.T) {
|
func TestUnitHTTP3TransportSuccess(t *testing.T) {
|
||||||
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
||||||
Dialer: selfcensor.SystemDialer{}, QUICDialer: MockQUICDialer{}})
|
Dialer: dialer.Default, QUICDialer: MockQUICDialer{}})
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
req, err := http.NewRequest("GET", "https://www.google.com", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -134,7 +134,7 @@ func TestUnitHTTP3TransportSuccess(t *testing.T) {
|
||||||
|
|
||||||
func TestUnitHTTP3TransportFailure(t *testing.T) {
|
func TestUnitHTTP3TransportFailure(t *testing.T) {
|
||||||
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
txp := httptransport.NewHTTP3Transport(httptransport.Config{
|
||||||
Dialer: selfcensor.SystemDialer{}, QUICDialer: MockQUICDialer{}})
|
Dialer: dialer.Default, QUICDialer: MockQUICDialer{}})
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
cancel() // so that the request immediately fails
|
cancel() // so that the request immediately fails
|
||||||
|
|
|
@ -36,7 +36,6 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/quicdialer"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/quicdialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsdialer"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsdialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsx"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsx"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/trace"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/trace"
|
||||||
|
@ -151,7 +150,7 @@ func NewDialer(config Config) Dialer {
|
||||||
if config.FullResolver == nil {
|
if config.FullResolver == nil {
|
||||||
config.FullResolver = NewResolver(config)
|
config.FullResolver = NewResolver(config)
|
||||||
}
|
}
|
||||||
var d Dialer = selfcensor.SystemDialer{}
|
var d Dialer = dialer.Default
|
||||||
d = dialer.TimeoutDialer{Dialer: d}
|
d = dialer.TimeoutDialer{Dialer: d}
|
||||||
d = dialer.ErrorWrapperDialer{Dialer: d}
|
d = dialer.ErrorWrapperDialer{Dialer: d}
|
||||||
if config.Logger != nil {
|
if config.Logger != nil {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/dialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/httptransport"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsdialer"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/tlsdialer"
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/trace"
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/trace"
|
||||||
)
|
)
|
||||||
|
@ -245,7 +244,7 @@ func TestNewDialerVanilla(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +284,7 @@ func TestNewDialerWithResolver(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +333,7 @@ func TestNewDialerWithLogger(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,7 +383,7 @@ func TestNewDialerWithDialSaver(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +433,7 @@ func TestNewDialerWithReadWriteSaver(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,7 +479,7 @@ func TestNewDialerWithContextByteCounting(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
if _, ok := td.Dialer.(selfcensor.SystemDialer); !ok {
|
if _, ok := td.Dialer.(dialer.SystemDialer); !ok {
|
||||||
t.Fatal("not the dialer we expected")
|
t.Fatal("not the dialer we expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,29 @@
|
||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import "github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
// SystemResolver is the system resolver. It is implemented using
|
// SystemResolver is the system resolver.
|
||||||
// selfcensor.SystemResolver so that we can perform integration testing
|
type SystemResolver struct{}
|
||||||
// by forcing the code to return specific responses.
|
|
||||||
type SystemResolver = selfcensor.SystemResolver
|
// LookupHost implements Resolver.LookupHost.
|
||||||
|
func (r SystemResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||||
|
return net.DefaultResolver.LookupHost(ctx, hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network implements Resolver.Network.
|
||||||
|
func (r SystemResolver) Network() string {
|
||||||
|
return "system"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address implements Resolver.Address.
|
||||||
|
func (r SystemResolver) Address() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default is the resolver we use by default.
|
||||||
|
var Default = SystemResolver{}
|
||||||
|
|
||||||
var _ Resolver = SystemResolver{}
|
var _ Resolver = SystemResolver{}
|
||||||
|
|
|
@ -1,230 +0,0 @@
|
||||||
// Package selfcensor contains code that triggers censorship. We use
|
|
||||||
// this functionality to implement integration tests.
|
|
||||||
//
|
|
||||||
// The self censoring functionality is disabled by default. To enable it,
|
|
||||||
// call Enable with a JSON-serialized Spec structure as its argument.
|
|
||||||
//
|
|
||||||
// The following example causes NXDOMAIN to be returned for `dns.google`:
|
|
||||||
//
|
|
||||||
// selfcensor.Enable(`{"PoisonSystemDNS":{"dns.google":["NXDOMAIN"]}}`)
|
|
||||||
//
|
|
||||||
// The following example blocks connecting to `8.8.8.8:443`:
|
|
||||||
//
|
|
||||||
// selfcensor.Enable(`{"BlockedEndpoints":{"8.8.8.8:443":"REJECT"}}`)
|
|
||||||
//
|
|
||||||
// The following example blocks packets containing dns.google:
|
|
||||||
//
|
|
||||||
// selfcensor.Enable(`{"BlockedFingerprints":{"dns.google":"RST"}}`)
|
|
||||||
//
|
|
||||||
// The documentation of the Spec structure contains further information on
|
|
||||||
// how to populate the JSON. Miniooni uses the `--self-censor-spec flag` to
|
|
||||||
// which you are supposed to pass a serialized JSON.
|
|
||||||
package selfcensor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Spec indicates what self censorship techniques to implement.
|
|
||||||
type Spec struct {
|
|
||||||
// PoisonSystemDNS allows you to change the behaviour of the system
|
|
||||||
// DNS regarding specific domains. They keys are the domains and the
|
|
||||||
// values are the IP addresses to return. If you set the values for
|
|
||||||
// a domain to `[]string{"NXDOMAIN"}`, the system resolver will return
|
|
||||||
// an NXDOMAIN response. If you set the values for a domain to
|
|
||||||
// `[]string{"TIMEOUT"}` the system resolver will return "i/o timeout".
|
|
||||||
PoisonSystemDNS map[string][]string
|
|
||||||
|
|
||||||
// BlockedEndpoints allows you to block specific IP endpoints. The key is
|
|
||||||
// `IP:port` to block. The format is the same of net.JoinHostPort. If
|
|
||||||
// the value is "REJECT", then the connection attempt will fail with
|
|
||||||
// ECONNREFUSED. If the value is "TIMEOUT", then the connector will return
|
|
||||||
// claiming "i/o timeout". If the value is anything else, we will
|
|
||||||
// perform a "REJECT".
|
|
||||||
BlockedEndpoints map[string]string
|
|
||||||
|
|
||||||
// BlockedFingerprints allows you to block packets whose body contains
|
|
||||||
// specific fingerprints. Of course, the key is the fingerprint. If
|
|
||||||
// the value is "RST", then the connection will be reset. If the value
|
|
||||||
// is "TIMEOUT", then the code will return claiming "i/o timeout". If
|
|
||||||
// the value is anything else, we will perform a "RST".
|
|
||||||
BlockedFingerprints map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
attempts *atomicx.Int64 = &atomicx.Int64{}
|
|
||||||
enabled *atomicx.Int64 = &atomicx.Int64{}
|
|
||||||
mu sync.Mutex
|
|
||||||
spec *Spec
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enabled returns whether self censorship is enabled
|
|
||||||
func Enabled() bool {
|
|
||||||
return enabled.Load() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempts returns the number of self censorship attempts so far. A self
|
|
||||||
// censorship attempt is defined as the code entering into the branch that
|
|
||||||
// _may_ perform self censorship. We expected to see this counter being
|
|
||||||
// equal to zero when Enabled() returns false.
|
|
||||||
func Attempts() int64 {
|
|
||||||
return attempts.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable turns on the self censorship engine. This function returns
|
|
||||||
// an error if we cannot parse a Spec from the serialized JSON inside
|
|
||||||
// data. Each time you call Enable you overwrite the previous spec.
|
|
||||||
func Enable(data string) error {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
s := new(Spec)
|
|
||||||
if err := json.Unmarshal([]byte(data), s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
spec = s
|
|
||||||
enabled.Add(1)
|
|
||||||
log.Printf("selfcensor: spec %+v", *spec)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaybeEnable is like enable except that it does nothing in case
|
|
||||||
// the string provided as argument is an empty string.
|
|
||||||
func MaybeEnable(data string) (err error) {
|
|
||||||
if data != "" {
|
|
||||||
err = Enable(data)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SystemResolver is a self-censoring system resolver. This resolver does
|
|
||||||
// not censor anything unless you call selfcensor.Enable().
|
|
||||||
type SystemResolver struct{}
|
|
||||||
|
|
||||||
// errTimeout indicates that a timeout error has occurred.
|
|
||||||
var errTimeout = errors.New("i/o timeout")
|
|
||||||
|
|
||||||
// LookupHost implements Resolver.LookupHost
|
|
||||||
func (r SystemResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
|
||||||
if enabled.Load() != 0 { // jumps not taken by default
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
attempts.Add(1)
|
|
||||||
if spec.PoisonSystemDNS != nil {
|
|
||||||
values := spec.PoisonSystemDNS[hostname]
|
|
||||||
if len(values) == 1 && values[0] == "NXDOMAIN" {
|
|
||||||
return nil, errors.New("no such host")
|
|
||||||
}
|
|
||||||
if len(values) == 1 && values[0] == "TIMEOUT" {
|
|
||||||
return nil, errTimeout
|
|
||||||
}
|
|
||||||
if len(values) > 0 {
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FALLTHROUGH
|
|
||||||
}
|
|
||||||
return net.DefaultResolver.LookupHost(ctx, hostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network implements Resolver.Network
|
|
||||||
func (r SystemResolver) Network() string {
|
|
||||||
return "system"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address implements Resolver.Address
|
|
||||||
func (r SystemResolver) Address() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// SystemDialer is a self-censoring system dialer. This dialer does
|
|
||||||
// not censor anything unless you call selfcensor.Enable().
|
|
||||||
type SystemDialer struct{}
|
|
||||||
|
|
||||||
// defaultNetDialer is the dialer we use by default.
|
|
||||||
var defaultNetDialer = &net.Dialer{
|
|
||||||
Timeout: 15 * time.Second,
|
|
||||||
KeepAlive: 15 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultDialer is the dialer you should use in code that wants
|
|
||||||
// to take advantage of selfcensor capabilities.
|
|
||||||
var DefaultDialer = SystemDialer{}
|
|
||||||
|
|
||||||
// DialContext implements Dialer.DialContext
|
|
||||||
func (d SystemDialer) DialContext(
|
|
||||||
ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
if enabled.Load() != 0 { // jumps not taken by default
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
attempts.Add(1)
|
|
||||||
if spec.BlockedEndpoints != nil {
|
|
||||||
action, ok := spec.BlockedEndpoints[address]
|
|
||||||
if ok && action == "TIMEOUT" {
|
|
||||||
return nil, errTimeout
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
switch network {
|
|
||||||
case "tcp", "tcp4", "tcp6":
|
|
||||||
return nil, errors.New("connection refused")
|
|
||||||
default:
|
|
||||||
// not applicable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if spec.BlockedFingerprints != nil {
|
|
||||||
conn, err := defaultNetDialer.DialContext(ctx, network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return connWrapper{Conn: conn, closed: make(chan interface{}, 128),
|
|
||||||
fingerprints: spec.BlockedFingerprints}, nil
|
|
||||||
}
|
|
||||||
// FALLTHROUGH
|
|
||||||
}
|
|
||||||
return defaultNetDialer.DialContext(ctx, network, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
type connWrapper struct {
|
|
||||||
net.Conn
|
|
||||||
closed chan interface{}
|
|
||||||
fingerprints map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c connWrapper) Write(p []byte) (int, error) {
|
|
||||||
// TODO(bassosimone): implement reassembly to workaround the
|
|
||||||
// splitting of the ClientHello message.
|
|
||||||
if _, err := c.match(p, len(p)); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return c.Conn.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c connWrapper) match(p []byte, n int) (int, error) {
|
|
||||||
p = p[:n] // trim
|
|
||||||
for key, value := range c.fingerprints {
|
|
||||||
if bytes.Index(p, []byte(key)) != -1 {
|
|
||||||
if value == "TIMEOUT" {
|
|
||||||
return 0, errTimeout
|
|
||||||
}
|
|
||||||
return 0, errors.New("connection reset by peer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c connWrapper) Close() error {
|
|
||||||
// Implementation note: we will block here if we attempt to close
|
|
||||||
// too many times and noone's reading. Because we have a large buffer,
|
|
||||||
// and because this is integration testing code, that's fine.
|
|
||||||
c.closed <- true
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
|
@ -1,271 +0,0 @@
|
||||||
package selfcensor_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx"
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/selfcensor"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestDisabled MUST be the first test in this file.
|
|
||||||
func TestDisabled(t *testing.T) {
|
|
||||||
if selfcensor.Enabled() != false {
|
|
||||||
t.Fatal("self censorship should be disabled by default")
|
|
||||||
}
|
|
||||||
if selfcensor.Attempts() != 0 {
|
|
||||||
t.Fatal("we expect no self censorship attempts at the beginning")
|
|
||||||
}
|
|
||||||
t.Run("the system resolver does not trigger selfcensor events", func(t *testing.T) {
|
|
||||||
addrs, err := selfcensor.SystemResolver{}.LookupHost(
|
|
||||||
context.Background(), "dns.google",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if addrs == nil {
|
|
||||||
t.Fatal("expected non-nil addrs here")
|
|
||||||
}
|
|
||||||
if selfcensor.Attempts() != 0 {
|
|
||||||
t.Fatal("we expect no self censorship attempts by default")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.Run("the system dialer does not trigger selfcensor events", func(t *testing.T) {
|
|
||||||
conn, err := selfcensor.SystemDialer{}.DialContext(
|
|
||||||
context.Background(), "tcp", "8.8.8.8:443",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if conn == nil {
|
|
||||||
t.Fatal("expected non-nil conn here")
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
if selfcensor.Attempts() != 0 {
|
|
||||||
t.Fatal("we expect no self censorship attempts by default")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDisabled MUST be the second test in this file.
|
|
||||||
func TestEnableInvalidJSON(t *testing.T) {
|
|
||||||
if selfcensor.Enabled() != false {
|
|
||||||
t.Fatal("we need to start with self censorship not enabled")
|
|
||||||
}
|
|
||||||
err := selfcensor.Enable("{")
|
|
||||||
if err == nil || !strings.HasSuffix(err.Error(), "unexpected end of JSON input") {
|
|
||||||
t.Fatal("not the error we expectd")
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != false {
|
|
||||||
t.Fatal("we expected self censorship to still be not enabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMaybeEnableWorksAsIntended MUST be the second test in this file.
|
|
||||||
func TestMaybeEnableWorksAsIntended(t *testing.T) {
|
|
||||||
if selfcensor.Enabled() != false {
|
|
||||||
t.Fatal("we need to start with self censorship not enabled")
|
|
||||||
}
|
|
||||||
err := selfcensor.MaybeEnable("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != false {
|
|
||||||
t.Fatal("we expected self censorship to still be not enabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolveCauseNXDOMAIN(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"PoisonSystemDNS":{"dns.google":["NXDOMAIN"]}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemResolver{}.LookupHost(
|
|
||||||
context.Background(), "dns.google",
|
|
||||||
)
|
|
||||||
if err == nil || !strings.HasSuffix(err.Error(), "no such host") {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if addrs != nil {
|
|
||||||
t.Fatal("expected nil addrs here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolveCauseTimeout(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
err := selfcensor.MaybeEnable(`{"PoisonSystemDNS":{"dns.google":["TIMEOUT"]}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemResolver{}.LookupHost(ctx, "dns.google")
|
|
||||||
if err == nil || err.Error() != "i/o timeout" {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if addrs != nil {
|
|
||||||
t.Fatal("expected nil addrs here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolveCauseBogon(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"PoisonSystemDNS":{"dns.google":["10.0.0.7"]}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemResolver{}.LookupHost(
|
|
||||||
context.Background(), "dns.google")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(addrs) != 1 || addrs[0] != "10.0.0.7" {
|
|
||||||
t.Fatal("not the addrs we expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResolveCheckNetworkAndAddress(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"PoisonSystemDNS":{"dns.google":["10.0.0.7"]}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
reso := selfcensor.SystemResolver{}
|
|
||||||
if reso.Network() != "system" {
|
|
||||||
t.Fatal("invalid Network")
|
|
||||||
}
|
|
||||||
if reso.Address() != "" {
|
|
||||||
t.Fatal("invalid Address")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDialHandlesErrorsWithBlockedFingerprints(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
cancel() // so we should fail immediately!
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedFingerprints":{"dns.google":"TIMEOUT"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemDialer{}.DialContext(ctx, "tcp", "8.8.8.8:443")
|
|
||||||
if err == nil || !strings.HasSuffix(err.Error(), "operation was canceled") {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if addrs != nil {
|
|
||||||
t.Fatal("expected nil addrs here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDialCauseTimeout(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedEndpoints":{"8.8.8.8:443":"TIMEOUT"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemDialer{}.DialContext(ctx, "tcp", "8.8.8.8:443")
|
|
||||||
if err == nil || err.Error() != "i/o timeout" {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if addrs != nil {
|
|
||||||
t.Fatal("expected nil addrs here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDialCauseConnectionRefused(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedEndpoints":{"8.8.8.8:443":"REJECT"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
addrs, err := selfcensor.SystemDialer{}.DialContext(
|
|
||||||
context.Background(), "tcp", "8.8.8.8:443")
|
|
||||||
if err == nil || !strings.HasSuffix(err.Error(), "connection refused") {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if addrs != nil {
|
|
||||||
t.Fatal("expected nil addrs here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlockedFingerprintsTimeout(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedFingerprints":{"dns.google":"TIMEOUT"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
tlsDialer := netx.NewTLSDialer(netx.Config{
|
|
||||||
Dialer: selfcensor.SystemDialer{},
|
|
||||||
})
|
|
||||||
conn, err := tlsDialer.DialTLSContext(
|
|
||||||
context.Background(), "tcp", "dns.google:443")
|
|
||||||
if err == nil || err.Error() != "generic_timeout_error" {
|
|
||||||
t.Fatal("not the error expected")
|
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
t.Fatal("expected nil conn here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlockedFingerprintsNoMatch(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedFingerprints":{"ooni.io":"TIMEOUT"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
tlsDialer := netx.NewTLSDialer(netx.Config{
|
|
||||||
Dialer: selfcensor.SystemDialer{},
|
|
||||||
})
|
|
||||||
conn, err := tlsDialer.DialTLSContext(
|
|
||||||
context.Background(), "tcp", "dns.google:443")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if conn == nil {
|
|
||||||
t.Fatal("expected non-nil conn here")
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlockedFingerprintsConnectionReset(t *testing.T) {
|
|
||||||
err := selfcensor.MaybeEnable(`{"BlockedFingerprints":{"dns.google":"RST"}}`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if selfcensor.Enabled() != true {
|
|
||||||
t.Fatal("we expected self censorship to be enabled now")
|
|
||||||
}
|
|
||||||
tlsDialer := netx.NewTLSDialer(netx.Config{
|
|
||||||
Dialer: selfcensor.SystemDialer{},
|
|
||||||
})
|
|
||||||
conn, err := tlsDialer.DialTLSContext(
|
|
||||||
context.Background(), "tcp", "dns.google:443")
|
|
||||||
if err == nil || err.Error() != "connection_reset" {
|
|
||||||
t.Fatal("not the error we expected")
|
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
t.Fatal("expected nil conn here")
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user