1package mox
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "net"
8)
9
10// Network returns tcp4 or tcp6, depending on the ip.
11// This network can be passed to Listen instead of "tcp", which may start listening
12// on both ipv4 and ipv6 for addresses 0.0.0.0 and ::, which can lead to errors
13// about the port already being in use.
14// For invalid IPs, "tcp" is returned.
15func Network(ip string) string {
16 v := net.ParseIP(ip)
17 if v == nil {
18 return "tcp"
19 }
20 if v.To4() != nil {
21 return "tcp4"
22 }
23 return "tcp6"
24}
25
26// DomainSPFIPs returns IPs to include in SPF records for domains. It includes the
27// IPs on listeners that have SMTP enabled, and includes IPs configured for SOCKS
28// transports.
29func DomainSPFIPs() (ips []net.IP) {
30 for _, l := range Conf.Static.Listeners {
31 if !l.SMTP.Enabled || l.IPsNATed {
32 continue
33 }
34 ipstrs := l.IPs
35 if len(l.NATIPs) > 0 {
36 ipstrs = l.NATIPs
37 }
38 for _, ipstr := range ipstrs {
39 ip := net.ParseIP(ipstr)
40 if ip.IsUnspecified() {
41 continue
42 }
43 ips = append(ips, ip)
44 }
45 }
46 for _, t := range Conf.Static.Transports {
47 if t.Socks != nil {
48 ips = append(ips, t.Socks.IPs...)
49 }
50 }
51 return ips
52}
53
54// IPs returns ip addresses we may be listening/receiving mail on or
55// connecting/sending from to the outside.
56func IPs(ctx context.Context, receiveOnly bool) ([]net.IP, error) {
57 log := pkglog.WithContext(ctx)
58
59 // Try to gather all IPs we are listening on by going through the config.
60 // If we encounter 0.0.0.0 or ::, we'll gather all local IPs afterwards.
61 var ips []net.IP
62 var ipv4all, ipv6all bool
63 for _, l := range Conf.Static.Listeners {
64 // If NATed, we don't know our external IPs.
65 if l.IPsNATed {
66 return nil, nil
67 }
68 check := l.IPs
69 if len(l.NATIPs) > 0 {
70 check = l.NATIPs
71 }
72 for _, s := range check {
73 ip := net.ParseIP(s)
74 if ip.IsUnspecified() {
75 if ip.To4() != nil {
76 ipv4all = true
77 } else {
78 ipv6all = true
79 }
80 continue
81 }
82 ips = append(ips, ip)
83 }
84 }
85
86 // We'll list the IPs on the interfaces. How useful is this? There is a good chance
87 // we're listening on all addresses because of a load balancer/firewall.
88 if ipv4all || ipv6all {
89 ifaces, err := net.Interfaces()
90 if err != nil {
91 return nil, fmt.Errorf("listing network interfaces: %v", err)
92 }
93 for _, iface := range ifaces {
94 if iface.Flags&net.FlagUp == 0 {
95 continue
96 }
97 addrs, err := iface.Addrs()
98 if err != nil {
99 return nil, fmt.Errorf("listing addresses for network interface: %v", err)
100 }
101 if len(addrs) == 0 {
102 continue
103 }
104
105 for _, addr := range addrs {
106 ip, _, err := net.ParseCIDR(addr.String())
107 if err != nil {
108 log.Errorx("bad interface addr", err, slog.Any("address", addr))
109 continue
110 }
111 v4 := ip.To4() != nil
112 if ipv4all && v4 || ipv6all && !v4 {
113 ips = append(ips, ip)
114 }
115 }
116 }
117 }
118
119 if receiveOnly {
120 return ips, nil
121 }
122
123 for _, t := range Conf.Static.Transports {
124 if t.Socks != nil {
125 ips = append(ips, t.Socks.IPs...)
126 }
127 }
128
129 return ips, nil
130}
131