Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server.Serve exits even on temporary errors #180

Closed
drdaeman opened this issue Mar 10, 2022 · 1 comment
Closed

Server.Serve exits even on temporary errors #180

drdaeman opened this issue Mar 10, 2022 · 1 comment

Comments

@drdaeman
Copy link
Contributor

Hey, thank you for this library! I was experimenting with custom net.Listeners and it seems that Serve exits on any error from l.Accept unconditionally:

go-smtp/server.go

Lines 113 to 122 in f9aa4f4

c, err := l.Accept()
if err != nil {
select {
case <-s.done:
// we called Close()
return nil
default:
return err
}
}

Unfortunately, this essentially causes server to shut down upon non-fatal errors. This potentially allows a DoS attack (shutting server down by maliciously closing connections as they're about to get accepted), although this is only my suspicion that I haven't properly validated. IIRC, TLS listener may return net.ErrClosed if something bad happens during the handshake, and even plain TCP connections may accidentally fail with ECONNRESET or ECONNABORTED. Either way, I believe a slightly more sophisticated loop would be an improvement.

Here's an adapted version based on how net/http does it:

var tempDelay time.Duration // how long to sleep on accept failure

for {
	c, err := l.Accept()
	if err != nil {
		select {
		case <-s.done:
			// we called Close()
			return nil
		default:
		}
		if ne, ok := err.(net.Error); ok && ne.Temporary() {
			if tempDelay == 0 {
				tempDelay = 5 * time.Millisecond
			} else {
				tempDelay *= 2
			}
			if max := 1 * time.Second; tempDelay > max {
				tempDelay = max
			}
			s.ErrorLog.Printf("accept error: %v; retrying in %v", err, tempDelay)
			time.Sleep(tempDelay)
			continue
		}
		return err
	}
	go s.handleConn(newConn(c, s))
}

(Possibly somewhat contradictory, those errors are marked as "temporary" - see golang/go#6163 for more details).

@emersion
Copy link
Owner

Indeed, we shouldn't exit on temporary Accept errors. Patches welcome!

drdaeman added a commit to drdaeman/go-smtp that referenced this issue Mar 10, 2022
Now Serve does not exit on non-fatal errors from Accept.
This resolves emersion#180

It also logs handleConn errors to the ErrorLog - which is a partial
improvement for emersion#167
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants