Skip to content

Commit

Permalink
DOCS: Add tips about TXT records (#1622)
Browse files Browse the repository at this point in the history
* Add tips about TXT records

* More comments

* Remove test temporarily

* go generate

* A a link to the test
  • Loading branch information
tlimoncelli committed Jul 14, 2022
1 parent 9b42764 commit 44001dc
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 18 deletions.
4 changes: 2 additions & 2 deletions docs/_includes/matrix.html
Original file line number Diff line number Diff line change
Expand Up @@ -1360,8 +1360,8 @@
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
Expand Down
86 changes: 86 additions & 0 deletions docs/testing-txt-records.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
layout: default
title: TXT record testing
---
# TXT record testing

We recently discovered a strange but with processing TXT records and
double-quotes. Sadly we haven't been able to determine a way to test this
automatically. Therefore, I've written up this methodology.

# The problem

The problem relates to TXT records that have a string with quotes in them.

If a user creates a TXT record who's contents are `"something"` (yes, with
double quotes), some APIs get confused.

This bug is most likely to appear in a provider that uses
`RecordConfig.PopulateFromString()` (see `models/t_parse.go`) to create TXT
records. That function assumes the string should always have the quotes
stripped, though it is more likely that the string should be taken verbatim.

# The test

To complete this test, you will need a test domain that you can add records to.
It won't be modified otherwise.

This bug has to do with double-quotes at the start and end of TXT records. If
your provider doesn't permit double-quotes in TXT records (you'd be surprised!)
you don't have to worry about this bug because those records are banned
in your `auditrecords.go` file.

Step 1: Create the test records

Log into your DNS provider's web UI (portal) and create these 4 TXT records. (Don't use DNSControl!) Yes, include the double-quotes on test 1 and 3!

| Hostname | TXT |
|---------------|-----------|
| t0 | test0 |
| t1 | test1 |
| t2 | "test2" |
| t3 | "test3" |


Step 2: Update `dnsconfig.js`

Now in your `dnsconfig.js` file, add these records to the domain:

TXT("t0", "test0"),
TXT("t1", "\"test1\""),
TXT("t2", "test2"),
TXT("t3", "\"test3\""),

Step 3: Preview

When you do a `dnscontrol preview`, you should see changes for t1 and t2.

```
#1: MODIFY TXT t1.example.com: ("test1" ttl=1) -> ("\"test1\"" ttl=1)
#2: MODIFY TXT t2.example.com: ("\"test2\"" ttl=1) -> ("test2" ttl=1)
```

If you don't see those changes, that's a bug. For example, we found that
Cloudflare leave t2 alone but would try to add double-quotes to t3! This was
fixed in https://github.com/StackExchange/dnscontrol/pull/1543.

Step 3: Push

Let's assume you DO see the changes. Push them using `dnscontrol push`
then check the webui to see that the changes are correct.

```
2 corrections
#1: MODIFY TXT t1.stackoverflow.help: ("test1" ttl=1) -> ("\"test1\"" ttl=1)
SUCCESS!
#2: MODIFY TXT t2.stackoverflow.help: ("\"test2\"" ttl=1) -> ("test2" ttl=1)
SUCCESS!
```

Refresh your provider's web UI and you should see the changes as expected: t1
should have double-quotes and t2 shouldn't. If the change wasn't correctly
done, that's a bug.

Step 4: That's it!

Remove the lines from `dnsconfig.js` and run `dnscontrol push` to clean up.
23 changes: 15 additions & 8 deletions docs/writing-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,21 @@ Some useful `go test` flags:
* If a test will always fail because the provider doesn't support the feature, you can opt out of the test. Look at `func makeTests()` in [integrationTest/integration_test.go](https://github.com/StackExchange/dnscontrol/blob/2f65533e1b92c2967229a92a304fff7c14f7f4b6/integrationTest/integration_test.go#L675) for more details.


## Step 8: Update docs
## Step 8: Manual tests

There is a potential bug in how TXT records are handled. Sadly we haven't found
an automated way to test for this bug. The manual steps are here in
[docs/testing-txt-records.md](testing-txt-records.html)


## Step 9: Update docs

* Edit [README.md](https://github.com/StackExchange/dnscontrol): Add the provider to the bullet list.
* Edit [docs/provider-list.md](https://github.com/StackExchange/dnscontrol/blob/master/docs/provider-list.md): Add the provider to the provider list.
* Create `docs/_providers/PROVIDERNAME.md`: Use one of the other files in that directory as a base.
* Edit [OWNERS](https://github.com/StackExchange/dnscontrol/blob/master/OWNERS): Add the directory name and your github id.

## Step 9: Submit a PR
## Step 10: Submit a PR

At this point you can submit a PR.

Expand All @@ -215,7 +222,7 @@ input, or have questions. This is just a good stopping place to
submit a PR if you haven't already.


## Step 10: Capabilities
## Step 11: Capabilities

Some DNS providers have features that others do not. For example some
support the SRV record. A provider announces what it can do using
Expand Down Expand Up @@ -249,7 +256,7 @@ FYI: If a provider's capabilities changes, run `go generate` to update
the documentation.


## Step 11: Clean up
## Step 12: Clean up

Run "go vet" and "golint" and clean up any errors found.

Expand All @@ -267,24 +274,24 @@ go get -u golang.org/x/lint/golint
```


## Step 12: Dependencies
## Step 13: Dependencies

See
[docs/release-engineering.md](https://github.com/StackExchange/dnscontrol/blob/master/docs/release-engineering.md)
for tips about managing modules and checking for outdated
dependencies.


## Step 13: Check your work.
## Step 14: Check your work.

Here are some last-minute things to check before you submit your PR.

1. Run "go generate" to make sure all generated files are fresh.
2. Make sure all appropriate documentation is current. (See Step 8)
3. Check that dependencies are current (See Step 12)
3. Check that dependencies are current (See Step 13)
4. Re-run the integration test one last time (See Step 7)

## Step 14: After the PR is merged
## Step 15: After the PR is merged

1. Remove the "provider-request" label from the PR.
2. Verify that [docs/provider-list.md](https://github.com/StackExchange/dnscontrol/blob/master/docs/provider-list.md) no longer shows the provider as "requested"
5 changes: 3 additions & 2 deletions integrationTest/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,9 @@ func makeTests(t *testing.T) []*TestGroup {
tc("Create TXT with double-quote", txt("foodq", `quo"te`)),
clear(),
tc("Create TXT with ws at end", txt("foows1", "with space at end ")),
clear(),
tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
//clear(),
// TODO(tlim): Re-add this when we fix the RFC1035 escaped-quotes issue.
//tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
),

//
Expand Down
32 changes: 26 additions & 6 deletions models/t_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,33 @@ import (
"net"
)

// PopulateFromString populates a RecordConfig given a type and string.
// Many providers give all the parameters of a resource record in one big
// string (all the parameters of an MX, SRV, CAA, etc). Rather than have
// each provider rewrite this code many times, here's a helper function to use.
// PopulateFromString populates a RecordConfig given a type and string. Many
// providers give all the parameters of a resource record in one big string.
// This helper function lets you not re-invent the wheel.
//
// NOTE: You almost always want to special-case TXT records. Every provider
// seems to quote them differently.
//
// Recommended calling convention: Process the exceptions first, then use the
// function for everything else.
// var err error
// switch rType {
// case "MX":
// // MX priority in a separate field.
// if err := rc.SetTargetMX(cr.Priority, target); err != nil {
// return nil, fmt.Errorf("unparsable MX record received from cloudflare: %w", err)
// }
// case "TXT":
// // TXT records are stored verbatim; no quoting/escaping to parse.
// err = rc.SetTargetTXT(target)
// // ProTip: Use rc.SetTargetTXTs(manystrings) if the API or parser returns a list of substrings.
// default:
// err = rec.PopulateFromString(rType, target, origin)
// }
// if err != nil {
// return nil, fmt.Errorf("unparsable record received from CHANGE_TO_PROVDER_NAME: %w", err)
// }
//
// If this doesn't work for all rtypes, process the special cases then
// call this for the remainder.
func (rc *RecordConfig) PopulateFromString(rtype, contents, origin string) error {
if rc.Type != "" && rc.Type != rtype {
panic(fmt.Errorf("assertion failed: rtype already set (%s) (%s)", rtype, rc.Type))
Expand Down

0 comments on commit 44001dc

Please sign in to comment.