diff --git a/docs/spf-optimizer.md b/docs/spf-optimizer.md index 6962cf659..b42819f1d 100644 --- a/docs/spf-optimizer.md +++ b/docs/spf-optimizer.md @@ -105,9 +105,13 @@ The parameters are: * `overhead1:` "Overhead for the 1st TXT record". When calculating the max length of each TXT record, reduce the maximum for the first TXT record in the chain by this amount. * `raw:` The label of the unaltered SPF settings. Setting to an empty string `''` will disable this. (Optional. Default: `"_rawspf"`) * `ttl:` This allows setting a specific TTL on this SPF record. (Optional. Default: using default record TTL) +* `txtMaxSize` The maximum size for each TXT record. Values over 255 will result in [multiple strings][multi-string]. General recommendation is to [not go higher than 450][record-size] so that DNS responses will still fit in a UDP packet. (Optional. Default: `"255"`) * `parts:` The individual parts of the SPF settings. * `flatten:` Which includes should be inlined. For safety purposes the flattening is done on an opt-in basis. If `"*"` is listed, all includes will be flattened... this might create more problems than is solves due to length limitations. +[multi-string]: https://tools.ietf.org/html/rfc4408#section-3.1.3 +[record-size]: https://tools.ietf.org/html/rfc4408#section-3.1.4 + `SPR_BUILDER()` returns multiple `TXT()` records: * `TXT("@", "v=spf1 .... ~all")` diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index bbd49f434..aa02aed36 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -733,6 +733,7 @@ var FRAME = recordBuilder('FRAME'); // split: The template for additional records to be created (default: '_spf%d') // flatten: A list of domains to be flattened. // overhead1: Amout of "buffer room" to reserve on the first item in the spf chain. +// txtMaxSize: The maximum size for each TXT string. Values over 255 will result in multiple strings (default: '255') function SPF_BUILDER(value) { if (!value.parts || value.parts.length < 2) { @@ -771,6 +772,10 @@ function SPF_BUILDER(value) { p.overhead1 = value.overhead1; } + if (value.txtMaxSize) { + p.txtMaxSize = value.txtMaxSize; + } + // Generate a TXT record with the metaparameters. if (value.ttl) { r.push(TXT(value.label, rawspf, p, TTL(value.ttl))); diff --git a/pkg/js/static.go b/pkg/js/static.go index d3ac25b08..b4de5eba0 100644 --- a/pkg/js/static.go +++ b/pkg/js/static.go @@ -212,110 +212,112 @@ var _escData = map[string]*_escFile{ "/helpers.js": { name: "helpers.js", local: "pkg/js/helpers.js", - size: 23999, + size: 24193, modtime: 0, compressed: ` -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 +NmaBXgAA `, }, } diff --git a/pkg/normalize/flatten.go b/pkg/normalize/flatten.go index c6aff7058..6f7789e23 100644 --- a/pkg/normalize/flatten.go +++ b/pkg/normalize/flatten.go @@ -58,17 +58,27 @@ func flattenSPFs(cfg *models.DNSConfig) []error { overhead1 = i } + // Default txtMaxSize will not result in multiple TXT strings + txtMaxSize := 255 + if oh, ok := txt.Metadata["txtMaxSize"]; ok { + i, err := strconv.Atoi(oh) + if err != nil { + errs = append(errs, Warning{fmt.Errorf("split txtMaxSize %q is not an int", oh)}) + } + txtMaxSize = i + } + if !strings.Contains(split, "%d") { errs = append(errs, Warning{fmt.Errorf("Split format `%s` in `%s` is not proper format (should have %%d in it)", split, txt.GetLabelFQDN())}) continue } - recs := rec.TXTSplit(split+"."+domain.Name, overhead1) + recs := rec.TXTSplit(split+"."+domain.Name, overhead1, txtMaxSize) for k, v := range recs { if k == "@" { - txt.SetTargetTXT(v) + txt.SetTargetTXTs(v) } else { cp, _ := txt.Copy() - cp.SetTargetTXT(v) + cp.SetTargetTXTs(v) cp.SetLabelFromFQDN(k, domain.Name) domain.Records = append(domain.Records, cp) } diff --git a/pkg/spflib/flatten.go b/pkg/spflib/flatten.go index 3de225989..9304f1eb5 100644 --- a/pkg/spflib/flatten.go +++ b/pkg/spflib/flatten.go @@ -5,6 +5,31 @@ import ( "strings" ) +// Chunks splits strings into arrays of a certain chunk size. We +// use this to split TXT records into 255 sized chunks for RFC 4408 +// https://tools.ietf.org/html/rfc4408#section-3.1.3 +// Borrowed from https://stackoverflow.com/a/61469854/11477663 +func Chunks(s string, chunkSize int) []string { + if chunkSize >= len(s) { + return []string{s} + } + var chunks []string + chunk := make([]rune, chunkSize) + len := 0 + for _, r := range s { + chunk[len] = r + len++ + if len == chunkSize { + chunks = append(chunks, string(chunk)) + len = 0 + } + } + if len > 0 { + chunks = append(chunks, string(chunk[:len])) + } + return chunks +} + // TXT outputs s as a TXT record. func (s *SPFRecord) TXT() string { text := "v=spf1" @@ -14,7 +39,10 @@ func (s *SPFRecord) TXT() string { return text } -const maxLenDefault = 255 +// Maximum length of a single TXT string. Anything +// bigger than this will be split into multiple strings +// if the user has set a txtMaxSize length greater than 255 +const txtStringLength = 255 // TXTSplit returns a set of txt records to use for SPF. // pattern given is used to name all chained spf records. @@ -26,20 +54,20 @@ const maxLenDefault = 255 // overhead of that many bytes. For example, if there are other txt // records and you wish to reduce the first SPF record size to prevent // DNS over TCP. -func (s *SPFRecord) TXTSplit(pattern string, overhead int) map[string]string { - m := map[string]string{} - s.split("@", pattern, 1, m, overhead) +func (s *SPFRecord) TXTSplit(pattern string, overhead int, txtMaxSize int) map[string][]string { + m := map[string][]string{} + s.split("@", pattern, 1, m, overhead, txtMaxSize) return m } -func (s *SPFRecord) split(thisfqdn string, pattern string, nextIdx int, m map[string]string, overhead int) { - maxLen := maxLenDefault - overhead +func (s *SPFRecord) split(thisfqdn string, pattern string, nextIdx int, m map[string][]string, overhead int, txtMaxSize int) { + maxLen := txtMaxSize - overhead base := s.TXT() // simple case. it fits if len(base) <= maxLen { - m[thisfqdn] = base + m[thisfqdn] = Chunks(base, txtStringLength) return } @@ -62,7 +90,7 @@ func (s *SPFRecord) split(thisfqdn string, pattern string, nextIdx int, m map[st over = true if addedCount == 0 { // the first part is too big to include. We kinda have to give up here. - m[thisfqdn] = base + m[thisfqdn] = []string{base} return } } @@ -71,8 +99,9 @@ func (s *SPFRecord) split(thisfqdn string, pattern string, nextIdx int, m map[st newRec.Parts = append(newRec.Parts, part) } } - m[thisfqdn] = thisText + tail - newRec.split(nextFQDN, pattern, nextIdx+1, m, 0) + + m[thisfqdn] = Chunks(thisText+tail, txtStringLength) + newRec.split(nextFQDN, pattern, nextIdx+1, m, 0, txtMaxSize) } // Flatten optimizes s. diff --git a/pkg/spflib/flatten_test.go b/pkg/spflib/flatten_test.go index 92bb7b7f7..4d13d95c1 100644 --- a/pkg/spflib/flatten_test.go +++ b/pkg/spflib/flatten_test.go @@ -1,6 +1,7 @@ package spflib import ( + "reflect" "strconv" "strings" "testing" @@ -94,6 +95,7 @@ var splitTests = [][]string{ func TestSplit(t *testing.T) { for _, tst := range splitTests { overhead := 0 + txtMaxSize := 255 v := strings.Split(tst[0], "-") if len(v) > 1 { @@ -107,17 +109,91 @@ func TestSplit(t *testing.T) { if err != nil { t.Fatal(err) } - res := rec.TXTSplit("_spf%d.stackex.com", overhead) - if res["@"] != tst[2] { - t.Fatalf("Root record wrong. \nExp %s\ngot %s", tst[2], res["@"]) + res := rec.TXTSplit("_spf%d.stackex.com", overhead, txtMaxSize) + if res["@"][0] != tst[2] { + t.Fatalf("Root record wrong. \nExp %s\ngot %s", tst[2], res["@"][0]) } for i := 3; i < len(tst); i += 2 { fqdn := tst[i] exp := tst[i+1] - if res[fqdn] != exp { + if res[fqdn][0] != exp { t.Fatalf("Record %s.\nExp %s\ngot %s", fqdn, exp, res[fqdn]) } } }) } } + +func TestMultiStringSplit(t *testing.T) { + var tests = []struct { + description string + unsplitInput string + txtMaxSize int + want map[string][]string + }{ + { + description: "basic multiple strings", + unsplitInput: "v=spf1 -all", + txtMaxSize: 255, + want: map[string][]string{ + "@": { + "v=spf1 -all", + }, + }, + }, + { + description: "too long to split, not enough txtMaxSize for a multi string", + unsplitInput: "v=spf1 include:a0123456789012345678901234567890123456789012345sssss6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.com -all", + txtMaxSize: 255, + want: map[string][]string{ + "@": { + "v=spf1 include:a0123456789012345678901234567890123456789012345sssss6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.com -all", + }, + }, + }, + { + description: "can actually be split with multiple txt strings", + unsplitInput: "v=spf1 include:a0123456789012345678901234567890123456789012345sssss6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.com -all", + txtMaxSize: 450, + want: map[string][]string{ + "@": { + // First string + "v=spf1 include:a0123456789012345678901234567890123456789012345sssss6789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.com", + // Second string + " -all", + }, + }, + }, + { + description: "really big with multiple txt strings", + unsplitInput: "v=spf1 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178" + + " ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178" + + " ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 -all", + txtMaxSize: 450, + want: map[string][]string{ + "@": { + "v=spf1 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.", + "192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 include:_spf1.stackex.com -all", + }, + "_spf1.stackex.com": { + "v=spf1 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.", + "192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 ip4:200.192.169.178 -all", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + rec, err := Parse(test.unsplitInput, nil) + if err != nil { + t.Fatal(err) + } + got := rec.TXTSplit("_spf%d.stackex.com", 0, test.txtMaxSize) + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("got %v want %v", got, test.want) + } + }) + } +}