This repository has been archived by the owner on Mar 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Returns follow-up experiment types for TLS
Work related to ooni/probe-engine#87
- Loading branch information
1 parent
cb398ac
commit 991be9c
Showing
2 changed files
with
160 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// Package nervoushandler contains OONI's nervous handler. | ||
// | ||
// This is currently experimental software. | ||
package nervoushandler | ||
|
||
import ( | ||
"errors" | ||
"sync" | ||
|
||
"github.com/m-lab/go/rtx" | ||
"github.com/ooni/netx/model" | ||
) | ||
|
||
// Transaction contains the transaction summary. The purpose of such | ||
// summary is to recap what we know about the domain -> address -> TLS | ||
// handshake chain. This is inspired by a design document that I am | ||
// working on with Fortuna Vinicius (Jigsaw). See: | ||
// | ||
// https://docs.google.com/document/d/1jcidvZGxBlucyLivAtvwrCidkIgb3yV6bhQIH_jf21M/edit?ts=5db300e4# | ||
type Transaction struct { | ||
ResolveErr error | ||
Domain string | ||
ConnectErr error | ||
Address string | ||
TriedTLS bool | ||
TLSErr error | ||
} | ||
|
||
// Handler is the nervous handler. When Unreliable is nonzero, then | ||
// some of our assumptions about events are broken. | ||
type Handler struct { | ||
All map[int64]*Transaction | ||
Unrealiable int64 | ||
child model.Handler | ||
mu sync.Mutex | ||
} | ||
|
||
// New creates a new handler | ||
func New(handler model.Handler) *Handler { | ||
return &Handler{ | ||
All: make(map[int64]*Transaction), | ||
child: handler, | ||
} | ||
} | ||
|
||
// OnMeasurement dispatches the measurements | ||
func (h *Handler) OnMeasurement(m model.Measurement) { | ||
defer h.child.OnMeasurement(m) | ||
// Implementation note: measurements shall not contain more than | ||
// one event inside each, but one never knows... | ||
if m.ResolveDone != nil { | ||
h.resolveDone(m.ResolveDone) | ||
} | ||
if m.Connect != nil { | ||
h.connectDone(m.Connect) | ||
} | ||
if m.TLSHandshakeDone != nil { | ||
h.tlsDone(m.TLSHandshakeDone) | ||
} | ||
} | ||
|
||
func (h *Handler) resolveDone(ev *model.ResolveDoneEvent) { | ||
txID := ev.TransactionID | ||
h.mu.Lock() | ||
defer h.mu.Unlock() | ||
if _, ok := h.All[txID]; ok { | ||
h.Unrealiable++ // unclear what's happening | ||
return | ||
} | ||
h.All[txID] = &Transaction{ | ||
Domain: ev.Hostname, | ||
ResolveErr: ev.Error, | ||
} | ||
} | ||
|
||
func (h *Handler) connectDone(ev *model.ConnectEvent) { | ||
txID := ev.TransactionID | ||
h.mu.Lock() | ||
defer h.mu.Unlock() | ||
if _, ok := h.All[txID]; !ok { | ||
h.Unrealiable++ // unclear what's happening | ||
return | ||
} | ||
h.All[txID].Address = ev.RemoteAddress | ||
h.All[txID].ConnectErr = ev.Error | ||
} | ||
|
||
func (h *Handler) tlsDone(ev *model.TLSHandshakeDoneEvent) { | ||
txID := ev.TransactionID | ||
h.mu.Lock() | ||
defer h.mu.Unlock() | ||
if _, ok := h.All[txID]; !ok { | ||
h.Unrealiable++ // unclear what's happening | ||
return | ||
} | ||
h.All[txID].TLSErr = ev.Error | ||
h.All[txID].TriedTLS = true | ||
} | ||
|
||
// ExperimentKind is the kind of follow-up experiment | ||
type ExperimentKind string | ||
|
||
const ( | ||
// SNIBlockingExperiment is an experiment where we perform a TLS handshake | ||
// with a test helper, from the probe, using once the Domain that may be | ||
// censored and a second time another domain. It is important that the test | ||
// helper _is not_ using the IPAddress also provided as argument. | ||
SNIBlockingExperiment = ExperimentKind("SNIBlocking") | ||
|
||
// IPValidForDomainExperiment is an experiment where from another vantage | ||
// point we check whether we can establish a TLS connection using the | ||
// specified Domain as SNI to the indicated IPAddress. | ||
IPValidForDomainExperiment = ExperimentKind("IPValidForDomain") | ||
) | ||
|
||
// FollowupInfo describes a follow-up measurement. The semantics of the | ||
// Domain and IPAddress fields depends on the ExperimentKind. | ||
type FollowupInfo struct { | ||
Experiment ExperimentKind | ||
Domain string | ||
IPAddress string | ||
} | ||
|
||
// Followup returns info the required follow-up measurements. | ||
func (h *Handler) Followup() ([]FollowupInfo, error) { | ||
h.mu.Lock() | ||
defer h.mu.Unlock() | ||
if h.Unrealiable != 0 { | ||
return nil, errors.New("unreliable measurements") | ||
} | ||
var result []FollowupInfo | ||
for _, ts := range h.All { | ||
if ts.TriedTLS == false || ts.TLSErr != nil { | ||
result = append(result, FollowupInfo{ | ||
Experiment: SNIBlockingExperiment, | ||
Domain: ts.Domain, | ||
IPAddress: ts.Address, // see above for semantics | ||
}) | ||
result = append(result, FollowupInfo{ | ||
Experiment: IPValidForDomainExperiment, | ||
IPAddress: ts.Address, | ||
Domain: ts.Domain, | ||
}) | ||
} | ||
} | ||
return result, nil | ||
} | ||
|
||
// MustFollowup is like Followup but panics on error. | ||
func (h *Handler) MustFollowup() []FollowupInfo { | ||
result, err := h.Followup() | ||
rtx.PanicOnError(err, "h.Followup failed") | ||
return result | ||
} |