package e2e import ( "fmt" "net/url" "regexp" "strings" "testing" "time" ) // testInboundUpstreamDown: an inbound domain whose upstream refuses // connections (the sink's address, a port nothing listens on). Port 25 takes // the message; Postfix cannot hand it on and logs "relay=none ... // status=deferred (connect to ...: Connection refused)" (Postfix 3.10.13, // src/smtp/smtp_trouble.c:273, 421), which the Inbound log shows as deferred // with that reason and no host. Then the message is made to expire - postsuper // -e, postqueue -i, as testExpiredMessageIsClosed does - and its row is // bounced, its last reply kept. The queue is emptied after: the bounce to the // stranger has nowhere to go on the stand. Runs inside testInboundRelay. func testInboundUpstreamDown(t *testing.T, sc *scenario, message func(to, token string) string) { const domain = "down.e2e.test" to := "user@" + domain resp, body, err := sc.panel.postForm("/inbound/domains", url.Values{"name": {domain}}) if err != nil { t.Fatal(err) } m := regexp.MustCompile(`^/inbound/domains/(\d+)$`).FindStringSubmatch(resp.Request.URL.Path) if m == nil { t.Fatalf("add inbound domain: landed on %s:\n%.800s", resp.Request.URL.Path, body) } base := "/inbound/domains/" + m[1] for path, form := range map[string]url.Values{ base + "/upstream": {"host": {"10.77.0.11"}, "port": {"2526"}, "tls_mode": {"none"}}, base + "/recipients": {"recipient_mode": {"list"}, "addresses": {to}}, } { resp, body, err := sc.panel.postForm(path, form) if err != nil { t.Fatal(err) } if resp.StatusCode != 200 || resp.Request.URL.Path != base { t.Fatalf("POST %s: status %d, landed on %s:\n%.800s", path, resp.StatusCode, resp.Request.URL.Path, body) } } defer h.execIn("selfpost", "postsuper", "-d", "ALL") token := uniqueToken("inbound-down") if res := deliverPort25("someone@stranger.e2e.test", to, message(to, token)); !res.ok() { t.Fatalf("port 25 did not accept mail for %s: %v", to, res.firstErr()) } row := func() (string, error) { return asPanel(`import sqlite3,sys r=sqlite3.connect("/data/selfpost.db").execute("select queue_id, upstream_status, upstream_relay, upstream_reply from inbound_spam_log where subject like ? order by id desc",("%"+sys.argv[1]+"%",)).fetchone() print("" if r is None else "|".join(r))`, token) } var queueID string if err := waitFor("the row to be deferred", 60*time.Second, time.Second, func() (bool, error) { out, err := row() if err != nil { return false, err } f := strings.Split(out, "|") if len(f) == 4 && f[1] == "deferred" { if f[2] != "" || !strings.HasPrefix(f[3], "connect to 10.77.0.11[10.77.0.11]:2526: Connection refused") { return false, fmt.Errorf("deferred with host %q, reply %q", f[2], f[3]) } queueID = f[0] return true, nil } return false, fmt.Errorf("row %q\n%s", out, mailLogTail(h, "postfix/")) }); err != nil { t.Fatal(err) } _, body, err = sc.panel.get("/inbound/log") if err != nil { t.Fatal(err) } if !strings.Contains(body, `deferred