From fad69bdef1c114d5a0efa5473c750eebef9aeb9b Mon Sep 17 00:00:00 2001 From: ooninoob Date: Sun, 20 Nov 2022 21:04:57 +0100 Subject: [PATCH] Store results/errors from individual ip/port combos (runs) --- internal/engine/experiment/smtp/smtp.go | 115 ++++++++++++++++-------- 1 file changed, 79 insertions(+), 36 deletions(-) diff --git a/internal/engine/experiment/smtp/smtp.go b/internal/engine/experiment/smtp/smtp.go index 09d3484..611adb4 100644 --- a/internal/engine/experiment/smtp/smtp.go +++ b/internal/engine/experiment/smtp/smtp.go @@ -89,13 +89,22 @@ func config(input model.MeasurementTarget) (*Config, error) { // TestKeys contains the experiment results type TestKeys struct { - Queries []*model.ArchivalDNSLookupResult `json:"queries"` - TCPConnect []*model.ArchivalTCPConnectResult `json:"tcp_connect"` - TLSHandshakes []*model.ArchivalTLSOrQUICHandshakeResult `json:"tls_handshakes"` - SMTPErrors map[string][]*string `json:"smtp"` - NoOpCounter uint8 `json:"successful_noops"` + Queries []*model.ArchivalDNSLookupResult `json:"queries"` + Runs map[string]IndividualTestKeys `json:"runs"` // Used for global failure (DNS resolution) Failure string `json:"failure"` + // Used to indicate global failure state + Failed bool `json:"failed"` +} + +// IndividualTestKeys contains results for TCP/IP level stuff for each address found +// in the DNS lookup +type IndividualTestKeys struct { + NoOpCounter uint8 + TCPConnect []*model.ArchivalTCPConnectResult `json:"tcp_connect"` + TLSHandshakes []*model.ArchivalTLSOrQUICHandshakeResult `json:"tls_handshakes"` + // Individual failure aborting the test run for this address/port combo + Failure *string `json:"failure"` } type Measurer struct { @@ -118,7 +127,7 @@ func (m Measurer) ExperimentVersion() string { } // Manages sequential SMTP sessions to the same hostname (over different IPs) -// don't use in parallel! +// don't use in parallel because addr changed dynamically type SMTPRunner struct { trace *measurexlite.Trace logger model.Logger @@ -131,19 +140,31 @@ type SMTPRunner struct { addr string } -func (r SMTPRunner) smtp_error(err error) { - key := net.JoinHostPort(r.addr, r.port) - // Key is initialized in conn() no need to check here - r.tk.SMTPErrors[key] = append(r.tk.SMTPErrors[key], tracex.NewFailure(err)) +func (r *SMTPRunner) run_key() string { + return net.JoinHostPort(r.addr, r.port) } -func (r SMTPRunner) resolve(host string) ([]string, bool) { +func (r *SMTPRunner) run_error(err error) { + r.tk.Failed = true + key := r.run_key() + // Key is initialized in conn() no need to check here + entry, _ := r.tk.Runs[key] + entry.Failure = tracex.NewFailure(err) + r.tk.Runs[key] = entry +} + +func (r *SMTPRunner) global_error(err error) { + r.tk.Failed = true + r.tk.Failure = *tracex.NewFailure(err) +} + +func (r *SMTPRunner) resolve(host string) ([]string, bool) { r.logger.Infof("Resolving DNS for %s", host) resolver := r.trace.NewStdlibResolver(r.logger) addrs, err := resolver.LookupHost(r.ctx, host) r.tk.Queries = append(r.tk.Queries, r.trace.DNSLookupsFromRoundTrip()...) if err != nil { - r.tk.Failure = *tracex.NewFailure(err) + r.global_error(err) return []string{}, false } r.logger.Infof("Finished DNS for %s: %v", host, addrs) @@ -151,38 +172,57 @@ func (r SMTPRunner) resolve(host string) ([]string, bool) { return addrs, true } -func (r SMTPRunner) conn(addr string) (net.Conn, bool) { - // Initialize addr field and corresponding errors in TestKeys - r.addr = addr - if r.tk.SMTPErrors == nil { - r.tk.SMTPErrors = make(map[string][]*string) +func (r *SMTPRunner) get_run() IndividualTestKeys { + if r.tk.Runs == nil { + r.tk.Runs = make(map[string]IndividualTestKeys) } - r.tk.SMTPErrors[net.JoinHostPort(addr, r.port)] = []*string{} + key := r.run_key() + val, exists := r.tk.Runs[key] + if exists { + return val + } else { + return IndividualTestKeys{} + } +} + +func (r *SMTPRunner) save_run(itk IndividualTestKeys) { + key := r.run_key() + r.tk.Runs[key] = itk +} + +func (r *SMTPRunner) conn(addr string) (net.Conn, bool) { + // Initialize addr field and corresponding errors in TestKeys + r.logger.Infof("Establishing TCP to %s", addr) + r.addr = addr + run := r.get_run() dialer := r.trace.NewDialerWithoutResolver(r.logger) - conn, err := dialer.DialContext(r.ctx, "tcp", net.JoinHostPort(r.addr, r.port)) - r.tk.TCPConnect = append(r.tk.TCPConnect, r.trace.TCPConnects()...) + conn, err := dialer.DialContext(r.ctx, "tcp", net.JoinHostPort(addr, r.port)) + run.TCPConnect = append(run.TCPConnect, r.trace.TCPConnects()...) if err != nil { - r.smtp_error(err) + r.run_error(err) return nil, false } + r.save_run(run) return conn, true } -func (r SMTPRunner) handshake(conn net.Conn) (net.Conn, bool) { +func (r *SMTPRunner) handshake(conn net.Conn) (net.Conn, bool) { r.logger.Infof("Starting TLS handshake with %s:%s (%s)", r.host, r.port, r.addr) + run := r.get_run() thx := r.trace.NewTLSHandshakerStdlib(r.logger) tconn, _, err := thx.Handshake(r.ctx, conn, r.tlsconfig) - r.tk.TLSHandshakes = append(r.tk.TLSHandshakes, r.trace.FirstTLSHandshakeOrNil()) + run.TLSHandshakes = append(run.TLSHandshakes, r.trace.FirstTLSHandshakeOrNil()) if err != nil { - r.smtp_error(err) + r.run_error(err) return nil, false } + r.save_run(run) r.logger.Infof("Handshake succeeded") return tconn, true } -func (r SMTPRunner) starttls(conn net.Conn, message string) (net.Conn, bool) { +func (r *SMTPRunner) starttls(conn net.Conn, message string) (net.Conn, bool) { if message != "" { r.logger.Infof("Asking for StartTLS upgrade") conn.Write([]byte(message)) @@ -191,37 +231,40 @@ func (r SMTPRunner) starttls(conn net.Conn, message string) (net.Conn, bool) { return tconn, success } -func (r SMTPRunner) smtp(conn net.Conn, ehlo string, noop uint8) bool { +func (r *SMTPRunner) smtp(conn net.Conn, ehlo string, noop uint8) bool { client, err := smtp.NewClient(conn, ehlo) if err != nil { - r.smtp_error(err) + r.run_error(err) return false } err = client.Hello(ehlo) if err != nil { - r.smtp_error(err) + r.run_error(err) return false } if noop > 0 { + run := r.get_run() r.logger.Infof("Trying to generate more no-op traffic") // TODO: noop counter per IP address - r.tk.NoOpCounter = 0 - for r.tk.NoOpCounter < noop { - r.tk.NoOpCounter += 1 - r.logger.Infof("NoOp Iteration %d", r.tk.NoOpCounter) + run.NoOpCounter = 0 + for run.NoOpCounter < noop { + run.NoOpCounter += 1 + r.logger.Infof("NoOp Iteration %d", run.NoOpCounter) err = client.Noop() if err != nil { - r.smtp_error(err) + r.run_error(err) break } } - if r.tk.NoOpCounter == noop { + r.save_run(run) + + if run.NoOpCounter == noop { r.logger.Infof("Successfully generated no-op traffic") return true } else { - r.logger.Infof("Failed no-op traffic at iteration %d", r.tk.NoOpCounter) + r.logger.Infof("Failed no-op traffic at iteration %d", run.NoOpCounter) return false } } @@ -255,7 +298,7 @@ func (m Measurer) Run( ServerName: config.host, } - runner := SMTPRunner{ + runner := &SMTPRunner{ trace: trace, logger: log, ctx: ctx,