fix(measurexlite): expose TCP connect event (#934)

See https://github.com/ooni/probe/issues/2254
This commit is contained in:
Simone Basso 2022-09-05 12:21:16 +02:00 committed by GitHub
parent 3766ab2721
commit a72a9284f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 26 deletions

View File

@ -53,6 +53,8 @@ func (tx *Trace) OnConnectDone(
started time.Time, network, domain, remoteAddr string, err error, finished time.Time) { started time.Time, network, domain, remoteAddr string, err error, finished time.Time) {
switch network { switch network {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
// insert into the tcpConnect buffer
select { select {
case tx.tcpConnect <- NewArchivalTCPConnectResult( case tx.tcpConnect <- NewArchivalTCPConnectResult(
tx.Index, tx.Index,
@ -63,6 +65,23 @@ func (tx *Trace) OnConnectDone(
): ):
default: // buffer is full default: // buffer is full
} }
// insert into the networkEvent buffer
// see https://github.com/ooni/probe/issues/2254
select {
case tx.networkEvent <- NewArchivalNetworkEvent(
tx.Index,
started.Sub(tx.ZeroTime),
netxlite.ConnectOperation,
"tcp",
remoteAddr,
0,
err,
finished.Sub(tx.ZeroTime),
):
default: // buffer is full
}
default: default:
// ignore UDP connect attempts because they cannot fail // ignore UDP connect attempts because they cannot fail
// in interesting ways that make sense for censorship // in interesting ways that make sense for censorship

View File

@ -97,31 +97,59 @@ func TestNewDialerWithoutResolver(t *testing.T) {
if conn != nil { if conn != nil {
t.Fatal("expected nil conn") t.Fatal("expected nil conn")
} }
events := trace.TCPConnects()
if len(events) != 1 {
t.Fatal("expected to see single TCPConnect event")
}
expectedFailure := netxlite.FailureInterrupted expectedFailure := netxlite.FailureInterrupted
expect := &model.ArchivalTCPConnectResult{
IP: "1.1.1.1", t.Run("for TCPConnect", func(t *testing.T) {
Port: 443, events := trace.TCPConnects()
Status: model.ArchivalTCPConnectStatus{ if len(events) != 1 {
Blocked: nil, t.Fatal("expected to see single TCPConnect event")
Failure: &expectedFailure, }
Success: false, expect := &model.ArchivalTCPConnectResult{
}, IP: "1.1.1.1",
T: time.Second.Seconds(), Port: 443,
} Status: model.ArchivalTCPConnectStatus{
got := events[0] Blocked: nil,
if diff := cmp.Diff(expect, got); diff != "" { Failure: &expectedFailure,
t.Fatal(diff) Success: false,
} },
T: time.Second.Seconds(),
}
got := events[0]
if diff := cmp.Diff(expect, got); diff != "" {
t.Fatal(diff)
}
})
t.Run("for NetworkEvents", func(t *testing.T) {
events := trace.NetworkEvents()
if len(events) != 1 {
t.Fatal("expected to see single NetworkEvent event")
}
expectedFailure := netxlite.FailureInterrupted
expect := &model.ArchivalNetworkEvent{
Address: "1.1.1.1:443",
Failure: &expectedFailure,
NumBytes: 0,
Operation: netxlite.ConnectOperation,
Proto: "tcp",
T0: 0,
T: time.Second.Seconds(),
TransactionID: 0,
Tags: []string{},
}
got := events[0]
if diff := cmp.Diff(expect, got); diff != "" {
t.Fatal(diff)
}
})
}) })
t.Run("DialContext discards events when buffer is full", func(t *testing.T) { t.Run("DialContext discards events when buffer is full", func(t *testing.T) {
zeroTime := time.Now() zeroTime := time.Now()
trace := NewTrace(0, zeroTime) trace := NewTrace(0, zeroTime)
trace.tcpConnect = make(chan *model.ArchivalTCPConnectResult) // no buffer trace.tcpConnect = make(chan *model.ArchivalTCPConnectResult) // no buffer
trace.networkEvent = make(chan *model.ArchivalNetworkEvent) // ditto
dialer := trace.NewDialerWithoutResolver(model.DiscardLogger) dialer := trace.NewDialerWithoutResolver(model.DiscardLogger)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cancel() // we cancel immediately so connect is ~instantaneous cancel() // we cancel immediately so connect is ~instantaneous
@ -132,10 +160,20 @@ func TestNewDialerWithoutResolver(t *testing.T) {
if conn != nil { if conn != nil {
t.Fatal("expected nil conn") t.Fatal("expected nil conn")
} }
events := trace.TCPConnects()
if len(events) != 0 { t.Run("for TCPConnect", func(t *testing.T) {
t.Fatal("expected to see no TCPConnect events") events := trace.TCPConnects()
} if len(events) != 0 {
t.Fatal("expected to see no TCPConnect events")
}
})
t.Run("for NetworkEvents", func(t *testing.T) {
events := trace.NetworkEvents()
if len(events) != 0 {
t.Fatal("expected to see no NetworkEvent events")
}
})
}) })
t.Run("DialContext ignores UDP connect attempts", func(t *testing.T) { t.Run("DialContext ignores UDP connect attempts", func(t *testing.T) {
@ -151,10 +189,20 @@ func TestNewDialerWithoutResolver(t *testing.T) {
if conn != nil { if conn != nil {
t.Fatal("expected nil conn") t.Fatal("expected nil conn")
} }
events := trace.TCPConnects()
if len(events) != 0 { t.Run("for TCP connect", func(t *testing.T) {
t.Fatal("expected to see no TCPConnect events") events := trace.TCPConnects()
} if len(events) != 0 {
t.Fatal("expected to see no TCPConnect events")
}
})
t.Run("for NetworkEvents", func(t *testing.T) {
events := trace.NetworkEvents()
if len(events) != 0 {
t.Fatal("expected to see no NetworkEvent events")
}
})
}) })
t.Run("DialContext uses a dialer without a resolver", func(t *testing.T) { t.Run("DialContext uses a dialer without a resolver", func(t *testing.T) {