github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/pkg/normalize/flatten.go (about)

     1  package normalize
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/pkg/errors"
     7  
     8  	"github.com/StackExchange/dnscontrol/models"
     9  	"github.com/StackExchange/dnscontrol/pkg/spflib"
    10  )
    11  
    12  // hasSpfRecords returns true if this record requests SPF unrolling.
    13  func flattenSPFs(cfg *models.DNSConfig) []error {
    14  	var cache spflib.CachingResolver
    15  	var errs []error
    16  	var err error
    17  	for _, domain := range cfg.Domains {
    18  		apexTXTs := domain.Records.Grouped()[models.RecordKey{Type: "TXT", Name: "@"}]
    19  		// flatten all spf records that have the "flatten" metadata
    20  		for _, txt := range apexTXTs {
    21  			var rec *spflib.SPFRecord
    22  			if txt.Metadata["flatten"] != "" || txt.Metadata["split"] != "" {
    23  				if cache == nil {
    24  					cache, err = spflib.NewCache("spfcache.json")
    25  					if err != nil {
    26  						return []error{err}
    27  					}
    28  				}
    29  				rec, err = spflib.Parse(txt.GetTargetField(), cache)
    30  				if err != nil {
    31  					errs = append(errs, err)
    32  					continue
    33  				}
    34  			}
    35  			if flatten, ok := txt.Metadata["flatten"]; ok && strings.HasPrefix(txt.GetTargetField(), "v=spf1") {
    36  				rec = rec.Flatten(flatten)
    37  				err = txt.SetTargetTXT(rec.TXT())
    38  				if err != nil {
    39  					errs = append(errs, err)
    40  					continue
    41  				}
    42  			}
    43  			// now split if needed
    44  			if split, ok := txt.Metadata["split"]; ok {
    45  				if !strings.Contains(split, "%d") {
    46  					errs = append(errs, Warning{errors.Errorf("Split format `%s` in `%s` is not proper format (should have %%d in it)", split, txt.GetLabelFQDN())})
    47  					continue
    48  				}
    49  				recs := rec.TXTSplit(split + "." + domain.Name)
    50  				for k, v := range recs {
    51  					if k == "@" {
    52  						txt.SetTargetTXT(v)
    53  					} else {
    54  						cp, _ := txt.Copy()
    55  						cp.SetTargetTXT(v)
    56  						cp.SetLabelFromFQDN(k, domain.Name)
    57  						domain.Records = append(domain.Records, cp)
    58  					}
    59  				}
    60  			}
    61  		}
    62  	}
    63  	if cache == nil {
    64  		return errs
    65  	}
    66  	// check if cache is stale
    67  	for _, e := range cache.ResolveErrors() {
    68  		errs = append(errs, Warning{errors.Errorf("problem resolving SPF record: %s", e)})
    69  	}
    70  	if len(cache.ResolveErrors()) == 0 {
    71  		changed := cache.ChangedRecords()
    72  		if len(changed) > 0 {
    73  			if err := cache.Save("spfcache.updated.json"); err != nil {
    74  				errs = append(errs, err)
    75  			} else {
    76  				errs = append(errs, Warning{errors.Errorf("%d spf record lookups are out of date with cache (%s).\nWrote changes to spfcache.updated.json. Please rename and commit:\n    $ mv spfcache.updated.json spfcache.json\n    $ git commit spfcache.json", len(changed), strings.Join(changed, ","))})
    77  			}
    78  		}
    79  	}
    80  	return errs
    81  }