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

net/smtp: Quit() will continously fail if a previous EHLO/HELO failed #70011

Closed
wneessen opened this issue Oct 23, 2024 · 2 comments
Closed

net/smtp: Quit() will continously fail if a previous EHLO/HELO failed #70011

wneessen opened this issue Oct 23, 2024 · 2 comments

Comments

@wneessen
Copy link

Go version

go version go1.23.2 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/xxx/.cache/go-build'
GOENV='/home/xxx/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/xxx/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/xxx/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.2'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/xxx/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/xxxx/go/src/go-mail/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2680745108=/tmp/go-build -gno-record-gcc-switches'

What did you do?

While working on some test in go-mail (we forked net/smtp) I was working on a test to check a failed HELO/EHLO. I was sending an c.Hello() with an empty hostname, which would just send a the EHLO command and when that failed a HELO. I was then trying to close the connection to the server with c.Quit() which caused a hang, since Quit() will call c.hello() which itself will fail everytime the c.Quit() is called.

What did you see happen?

The issue is the c.hello() call in the c.Quit(). If we sent a HELO and it failed, it will set c.didHello to true, but it will also set the error to c.helloError. When c.Quit() calls c.hello(), it will see that c.didHellois already set and just return c.helloError (which was already set with the error we got when the EHLO/HELO failed before. Therefore c.Quit() will fail with that error from the HELO that we've seen before and we are basically stuck. We will not be able to send a QUIT to the server to close the connection gracefully.

What did you expect to see?

I think we should always be able to send a QUIT to the server, even if we didn't send a successful HELO or EHLO to the server. That way we are able close the connection gracefully even if we error'd.

I suggest that the c.hello() is only called when either c.didHello is false or if c.didHello is true and c.helloError is nil. That's how I implemented it in go-mail.

See commit: wneessen/go-mail@74fa3f6 (our net/smtp fork is concurrency safe, therefore the locking. The net/smtp patch would not need this.

This would probably suffice:
At https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/net/smtp/smtp.go;l=83

Make it:

if !c.didHello || c.helloError == nil {
	if err := c.hello(); err != nil {
		return err
	}
}
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/622476 mentions this issue: net/smtp: ignore HELO error in QUIT

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

3 participants