albertito / chasquid

SMTP (email) server with a focus on simplicity, security, and ease of operation [mirror]

Home Page:https://blitiri.com.ar/p/chasquid/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question on LookupMX error handling

ludusrusso opened this issue · comments

Hi,
i was looking for your code and I noticed this piece of code for DNS lookup error handling in file smtp.go:

	if err != nil {
		// There was an error. It could be that the domain has no MX, in which
		// case we have to fall back to A, or a bigger problem.
		// Unfortunately, go's API doesn't let us easily distinguish between
		// them. For now, if the error is permanent, we assume it's because
		// there was no MX and fall back, otherwise we return.
		// TODO: Find a better way to do this.
		dnsErr, ok := err.(*net.DNSError)
		if !ok {
			tr.Debugf("MX lookup error: %v", err)
			return nil, err
		} else if dnsErr.Temporary() {
			tr.Debugf("temporary DNS error: %v", dnsErr)
			return nil, err
		}

		// Permanent error, we assume MX does not exist and fall back to A.
		tr.Debugf("failed to resolve MX for %s, falling back to A", domain)
		mxs = []string{domain}

According to go docs, DNSError struct has now a IsNotFoundfield that can help improve the code accordig to your comment! What do you think?

type DNSError struct {
    Err         string // description of the error
    Name        string // name looked for
    Server      string // server used
    IsTimeout   bool   // if true, timed out; not all timeouts set this
    IsTemporary bool   // if true, error is temporary; not all errors set this; added in Go 1.6
    IsNotFound  bool   // if true, host could not be found; added in Go 1.13
}

I was looking at your code for inspiration since I'm working on a custom smtp sender! I was wondering what is the correct way to handle temporary DNS error: Maybe I can simply wait some time (e.g., 1 sec) and retry, but I do not understand if there is a better approach to do so!

Hi!

Thanks for noticing this and opening an issue about it!

You're absolutely correct, the code could be better using IsNotFound, although it should still keep the temporary vs. permanent error distinction.

The problem with doing that, at the moment, is that the field was added in Go 1.13, and chasquid is attempting to support Go 1.11 (because that's the version in Debian stable).

In commit 6641d85 I've edited the TODO to say that this is the proper way to do it.

As to your second question: the best way to handle DNS temporary errors would be to retry. I think retrying delivery after waiting some time (more than 1s since DNS errors could persist for a while) is the right thing to do.

I see that chasquid doesn't do that - it fails delivery as if it was a permanent error, which is a bug.
I will fix that, thanks for finding it :)

I think 8fab350 fixes the retry issue. It's on the next branch for now.
Once it passes the CI tests, I will give it a couple of days of real life testing before moving it to master.

The fix (8fab350) is now in master, and will be included in the next release.

Thanks a lot for finding and reporting this!