-
Notifications
You must be signed in to change notification settings - Fork 1
/
mxsrv.go
118 lines (104 loc) · 3.7 KB
/
mxsrv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright © 2017,2018 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt
//go:build go1.8
package main
import (
"fmt"
"net"
"strconv"
"go.pennock.tech/smtpdane/internal/errorlist"
)
// probeMX is the top-level function of a go-routine, much like probeHost,
// but is not responsible for probing any specific SMTP connections; instead
// it should spin up checks for each hostname which we care about
//
// We allow ports on domains; we still lookup MX records, but override port 25
// with the supplied port.
func probeMX(domainSpec string, status *programStatus) {
status = status.ChildBatcher("probeMX", domainSpec)
defer status.BatchFinished()
domain, port, err := HostnameMaybePortFrom(domainSpec)
if err != nil {
status.Errorf("error parsing %q: %s", domainSpec, err)
return
}
tieredResults, mxCount, err := ResolveMXTiers(domain)
if err != nil {
switch e := err.(type) {
case *errorlist.List:
status.Errorf("error resolving MX %q:\n%s", domain, e.FmtIndented())
default:
status.Errorf("error resolving MX %q: %s", domain, err)
}
return
}
status.Wafflef("found %d MX records for %q across %d preference levels", mxCount, domain, len(tieredResults))
// MX returns DNS label sequences for hostnames, so by definition each is already IsFqdn(),
// so no need to check before looking for TLSA records, etc.
seen := make(map[string]struct{}, mxCount)
for i := range tieredResults {
status.Wafflef(" %q MX preference %d: %v", domain, tieredResults[i].Preference, tieredResults[i].Hostnames)
for _, hn := range tieredResults[i].Hostnames {
if _, already := seen[hn]; already {
status.Messagef("skipping dup MX hostname: %q", hn)
continue
}
seen[hn] = struct{}{}
if port != "" {
hn = net.JoinHostPort(hn, port)
}
probeHostGo(hn, status, domain)
}
}
}
// probeSRV the top-level function of a go-routine, much like probeHost,
// but is not responsible for probing any specific SMTP connections; instead
// it should spin up checks for each hostname which we care about
//
// We allow ports on domains; we still lookup SRV records, but override the
// port therein with the supplied port.
func probeSRV(srvName, domainSpec string, status *programStatus) {
domain, port, err := HostnameMaybePortFrom(domainSpec)
if err != nil {
status.Errorf("error parsing %q: %s", domainSpec, err)
status.probing.Done()
return
}
lookup := fmt.Sprintf("_%s._tcp.%s", srvName, domain)
status = status.ChildBatcher("probeSRV", lookup)
defer status.BatchFinished()
srvList, err := ResolveSRV(lookup)
if err != nil {
switch e := err.(type) {
case *errorlist.List:
status.Errorf("error resolving SRV %q:\n%s", lookup, e.FmtIndented())
default:
status.Errorf("error resolving SRV %q: %s", lookup, err)
}
return
}
status.Wafflef("found %d SRV records for %q: %v", len(srvList), lookup, srvList)
// SRV returns DNS label sequences for hostnames, so by definition each is already IsFqdn(),
// so no need to check before looking for TLSA records, etc.
seen := make(map[string]struct{}, len(srvList))
for _, srv := range srvList {
// There might be two different ports in SRV, but if we've overridden the port
// then this becomes a dup because of us, not DNS; we still skip the dup.
//
// We ignore weight & priority because we check them all, in parallel.
var hn string
if port != "" {
hn = net.JoinHostPort(srv.Target, port)
} else {
// uint16 should always fit inside int
hn = net.JoinHostPort(srv.Target, strconv.Itoa(int(srv.Port)))
}
if _, already := seen[hn]; already {
status.Messagef("skipping dup SRV hostport: %q", hn)
continue
}
seen[hn] = struct{}{}
probeHostGo(hn, status, domain)
}
}