2018-02-16 01:02:50 +08:00
|
|
|
package models
|
|
|
|
|
2022-06-18 21:58:55 +08:00
|
|
|
import "strings"
|
|
|
|
|
2021-03-08 02:19:22 +08:00
|
|
|
/*
|
|
|
|
Sadly many providers handle TXT records in strange and non-compliant ways.
|
|
|
|
|
|
|
|
The variations we've seen:
|
|
|
|
|
|
|
|
* a TXT record is a list of strings, each 255-octets or fewer.
|
|
|
|
* a TXT record is a list of strings, all but the last will be 255 octets in length.
|
|
|
|
* a TXT record is a string less than 256 octets.
|
|
|
|
* a TXT record is any length, but we'll split it into 255-octet chunks behind the scenes.
|
|
|
|
|
|
|
|
DNSControl stores the string as the user specified, then lets the
|
|
|
|
provider work out how to handle the given input. There are two
|
|
|
|
opportunties to work with the data:
|
|
|
|
|
|
|
|
1. The provider's AuditRecords() function is called to permit
|
|
|
|
the provider to return an error if it won't be able to handle the
|
|
|
|
contents. For example, it might detect that the string contains a char
|
|
|
|
the provider doesn't support (for example, a backtick). This auditing
|
|
|
|
is done without any communication to the provider's API. This allows
|
|
|
|
such errors to be detected at the "dnscontrol check" stage. 2. When
|
|
|
|
performing corrections (GetDomainCorrections()), the provider can slice
|
|
|
|
and dice the user's input however they want.
|
|
|
|
|
|
|
|
* If the user input is a list of strings:
|
|
|
|
* The strings are stored in RecordConfig.TxtStrings
|
|
|
|
* If the user input is a single string:
|
|
|
|
* The strings are stored in RecordConfig.TxtStrings[0]
|
|
|
|
|
|
|
|
In both cases, the .Target stores a string that can be used in error
|
|
|
|
messages and other UI messages. This string should not be used by the
|
|
|
|
provider in any other way because it has been modified from the user's
|
|
|
|
original input.
|
|
|
|
|
|
|
|
*/
|
2020-11-18 20:05:26 +08:00
|
|
|
|
2021-01-25 04:36:48 +08:00
|
|
|
// HasFormatIdenticalToTXT returns if a RecordConfig has a format which is
|
|
|
|
// identical to TXT, such as SPF. For more details, read
|
|
|
|
// https://tools.ietf.org/html/rfc4408#section-3.1.1
|
|
|
|
func (rc *RecordConfig) HasFormatIdenticalToTXT() bool {
|
2021-03-08 02:19:22 +08:00
|
|
|
return rc.Type == "TXT" || rc.Type == "SPF"
|
2021-01-25 04:36:48 +08:00
|
|
|
}
|
|
|
|
|
2018-02-16 01:02:50 +08:00
|
|
|
// SetTargetTXT sets the TXT fields when there is 1 string.
|
2021-03-08 02:19:22 +08:00
|
|
|
// The string is stored in .Target, and split into 255-octet chunks
|
|
|
|
// for .TxtStrings.
|
2018-02-16 01:02:50 +08:00
|
|
|
func (rc *RecordConfig) SetTargetTXT(s string) error {
|
|
|
|
if rc.Type == "" {
|
|
|
|
rc.Type = "TXT"
|
2021-03-08 02:19:22 +08:00
|
|
|
} else if !rc.HasFormatIdenticalToTXT() {
|
|
|
|
panic("assertion failed: SetTargetTXT called when .Type is not TXT or compatible type")
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
2021-03-08 02:19:22 +08:00
|
|
|
|
|
|
|
rc.TxtStrings = []string{s}
|
|
|
|
rc.SetTarget(rc.zoneFileQuoted())
|
2018-02-16 01:02:50 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetTargetTXTs sets the TXT fields when there are many strings.
|
2021-03-08 02:19:22 +08:00
|
|
|
// The individual strings are stored in .TxtStrings, and joined to make .Target.
|
2018-02-16 01:02:50 +08:00
|
|
|
func (rc *RecordConfig) SetTargetTXTs(s []string) error {
|
|
|
|
if rc.Type == "" {
|
|
|
|
rc.Type = "TXT"
|
2021-03-08 02:19:22 +08:00
|
|
|
} else if !rc.HasFormatIdenticalToTXT() {
|
|
|
|
panic("assertion failed: SetTargetTXTs called when .Type is not TXT or compatible type")
|
2018-02-16 01:02:50 +08:00
|
|
|
}
|
2021-03-08 02:19:22 +08:00
|
|
|
|
|
|
|
rc.TxtStrings = s
|
|
|
|
rc.SetTarget(rc.zoneFileQuoted())
|
2018-02-16 01:02:50 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-06-18 21:58:55 +08:00
|
|
|
// GetTargetTXTJoined returns the TXT target as one string. If it was stored as multiple strings, concatenate them.
|
|
|
|
func (rc *RecordConfig) GetTargetTXTJoined() string {
|
|
|
|
return strings.Join(rc.TxtStrings, "")
|
|
|
|
}
|
|
|
|
|
2018-02-25 02:40:18 +08:00
|
|
|
// SetTargetTXTString is like SetTargetTXT but accepts one big string,
|
|
|
|
// which must be parsed into one or more strings based on how it is quoted.
|
2018-02-16 01:02:50 +08:00
|
|
|
// Ex: foo << 1 string
|
|
|
|
// foo bar << 1 string
|
2022-06-18 21:58:55 +08:00
|
|
|
// "foo bar" << 1 string
|
2018-02-16 01:02:50 +08:00
|
|
|
// "foo" "bar" << 2 strings
|
2022-06-18 21:58:55 +08:00
|
|
|
// FIXME(tlim): This function is badly named. It obscures the fact
|
|
|
|
// that the string is parsed for quotes and should only be used for returns TXTMulti.
|
|
|
|
// Deprecated: Use SetTargetTXTfromRFC1035Quoted instead.
|
2018-02-16 01:02:50 +08:00
|
|
|
func (rc *RecordConfig) SetTargetTXTString(s string) error {
|
|
|
|
return rc.SetTargetTXTs(ParseQuotedTxt(s))
|
|
|
|
}
|
2022-06-18 21:58:55 +08:00
|
|
|
|
|
|
|
func (rc *RecordConfig) SetTargetTXTfromRFC1035Quoted(s string) error {
|
|
|
|
many, err := ParseQuotedFields(s)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return rc.SetTargetTXTs(many)
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is no GetTargetTXTfromRFC1025Quoted(). Use GetTargetRFC1035Quoted()
|