Smail/server/services/setup/dns.go
smail dd04bdb49e
Some checks are pending
Security Checks / security (push) Waiting to run
完成安全整改、配置重构与邮件认证优化
2026-08-28 14:33:13 +08:00

102 lines
3.0 KiB
Go

package setup
import (
"fmt"
"net"
"net/netip"
"strings"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/i18n"
"git.pcux.cn/xuans/Smail/services/auth"
"git.pcux.cn/xuans/Smail/utils/context"
"git.pcux.cn/xuans/Smail/utils/errors"
"git.pcux.cn/xuans/Smail/utils/ip"
)
type DNSItem struct {
Type string `json:"type"`
Host string `json:"host"`
Value string `json:"value"`
TTL int `json:"ttl"`
Tips string `json:"tips"`
}
func GetDNSSettings(ctx *context.Context) (map[string][]*DNSItem, error) {
configData, err := config.ReadConfig()
if err != nil {
return nil, errors.Wrap(err)
}
dkimPublicKey, err := auth.DkimGen()
if err != nil {
return nil, errors.Wrap(err)
}
ipv4 := ip.GetIPv4()
ipv6 := ip.GetIPv6()
ipTips := i18n.GetText(ctx.Lang, "ip_taps")
ret := make(map[string][]*DNSItem, len(configData.Domains))
for _, domain := range configData.Domains {
ret[domain] = buildDNSItems(configData, domain, dkimPublicKey, ipv4, ipv6, ipTips)
}
return ret, nil
}
func buildDNSItems(configData *config.Config, domain, dkimPublicKey, ipv4, ipv6, ipTips string) []*DNSItem {
address := strings.TrimSpace(ipv4)
if address == "" {
address = "[Your Server IP]"
}
return []*DNSItem{
{Type: "A", Host: strings.ReplaceAll(configData.WebDomain, "."+configData.Domain, ""), Value: address, TTL: 3600, Tips: ipTips},
{Type: "A", Host: "smtp", Value: address, TTL: 3600, Tips: ipTips},
{Type: "A", Host: "imap", Value: address, TTL: 3600, Tips: ipTips},
{Type: "A", Host: "pop", Value: address, TTL: 3600, Tips: ipTips},
{Type: "A", Host: "@", Value: address, TTL: 3600, Tips: ipTips},
{Type: "MX", Host: "@", Value: fmt.Sprintf("smtp.%s", domain), TTL: 3600},
{Type: "TXT", Host: "@", Value: buildSPFRecord(ipv4, ipv6), TTL: 3600},
{Type: "TXT", Host: "_dmarc", Value: "v=DMARC1; p=quarantine", TTL: 3600},
{Type: "TXT", Host: "default._domainkey", Value: dkimPublicKey, TTL: 3600},
}
}
func buildSPFRecord(ipv4, ipv6 string) string {
mechanisms := []string{"v=spf1", "a", "mx"}
if mechanism := buildSPFAddressMechanism(ipv4); mechanism != "" {
mechanisms = append(mechanisms, mechanism)
}
if mechanism := buildSPFAddressMechanism(ipv6); mechanism != "" {
mechanisms = append(mechanisms, mechanism)
}
mechanisms = append(mechanisms, "~all")
return strings.Join(mechanisms, " ")
}
func buildSPFAddressMechanism(value string) string {
address, err := netip.ParseAddr(strings.TrimSpace(value))
if err != nil {
return ""
}
address = address.Unmap()
if !isPublicDNSAddress(address) {
return ""
}
if address.Is4() {
return "ip4:" + address.String()
}
if address.Is6() {
return "ip6:" + address.String()
}
return ""
}
func isPublicDNSAddress(address netip.Addr) bool {
return address.IsValid() && address.IsGlobalUnicast() &&
!address.IsPrivate() && !address.IsLoopback() &&
!address.IsLinkLocalUnicast() && !address.IsLinkLocalMulticast() &&
!address.IsMulticast() && !address.IsUnspecified() &&
!net.IP(address.AsSlice()).IsInterfaceLocalMulticast()
}