Skip to content

Commit 90cbf3c

Browse files
foxcppemersion
authored andcommitted
client: Restrict the line length for responses
1 parent 1f576e0 commit 90cbf3c

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

client.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,21 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
6565
// NewClient returns a new Client using an existing connection and host as a
6666
// server name to be used when authenticating.
6767
func NewClient(conn net.Conn, host string) (*Client, error) {
68-
text := textproto.NewConn(conn)
68+
rwc := struct {
69+
io.Reader
70+
io.Writer
71+
io.Closer
72+
}{
73+
Reader: lineLimitReader{
74+
R: conn,
75+
// Doubled maximum line length per RFC 5321 (Section 4.5.3.1.6)
76+
LineLimit: 2000,
77+
},
78+
Writer: conn,
79+
Closer: conn,
80+
}
81+
82+
text := textproto.NewConn(rwc)
6983
_, _, err := text.ReadResponse(220)
7084
if err != nil {
7185
text.Close()

client_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,52 @@ func TestBasic_SMTPError(t *testing.T) {
221221
}
222222
}
223223

224+
func TestClient_TooLongLine(t *testing.T) {
225+
faultyServer := []string{
226+
"220 mx.google.com at your service\r\n",
227+
"220 mx.google.com at your service\r\n",
228+
"500 5.0.0 nU6XC5JJUfiuIkC7NhrxZz36Rl/rXpkfx9QdeZJ+rno6W5J9k9HvniyWXBBi1gOZ/CUXEI6K7Uony70eiVGGGkdFhP1rEvMGny1dqIRo3NM2NifrvvLIKGeX6HrYmkc7NMn9BwHyAnt5oLe5eNVDI+grwIikVPNVFZi0Dg4Xatdg5Cs8rH1x9BWhqyDoxosJst4wRoX4AymYygUcftM3y16nVg/qcb1GJwxSNbah7VjOiSrk6MlTdGR/2AwIIcSw7pZVJjGbCorniOTvKBcyut1YdbrX/4a/dBhvLfZtdSccqyMZAdZno+tGrnu+N2ghFvz6cx6bBab9Z4JJQMlkK/g1y7xjEPr6nKwruAf71NzOclPK5wzs2hY3Ku9xEjU0Cd+g/OjAzVsmeJk2U0q+vmACZsFAiOlRynXKFPLqMAg8skM5lioRTm05K/u3aBaUq0RKloeBHZ/zNp/kfHNp6TmJKAzvsXD3Xdo+PRAgCZRTRAl3ydGdrOOjxTULCVlgOL6xSAJdj9zGkzQoEW4tRmp1OiIab4GSxCtkIo7XnAowJ7EPUfDGTV3hhl5Qn7jvZjPCPlruRTtzVTho7D3HBEouWv1qDsqdED23myw0Ma9ZlobSf9eHqsSv1MxjKG2D5DdFBACu6pXGz3ceGreOHYWnI74TkoHtQ5oNuF6VUkGjGN+f4fOaiypQ54GJ8skTNoSCHLK4XF8ZutSxWzMR+LKoJBWMb6bdAiFNt+vXZOUiTgmTqs6Sw79JXqDX9YFxryJMKjHMiFkm+RZbaK5sIOXqyq+RNmOJ+G0unrQHQMCES476c7uvOlYrNoJtq+uox1qFdisIE/8vfSoKBlTtw+r2m87djIQh4ip/hVmalvtiF5fnVTxigbtwLWv8rAOCXKoktU0c2ie0a5hGtvZT0SXxwX8K2CeYXb81AFD2IaLt/p8Q4WuZ82eOCeXP72qP9yWYj6mIZdgyimm8wjrDowt2yPJU28ZD6k3Ei6C31OKgMpCf8+MW504/VCwld7czAIwjJiZe3DxtUdfM7Q565OzLiWQgI8fxjsvlCKMiOY7q42IGGsVxXJAFMtDKdchgqQA1PJR1vrw+SbI3Mh4AGnn8vKn+WTsieB3qkloo7MZlpMz/bwPXg7XadOVkUaVeHrZ5OsqDWhsWOLtPZLi5XdNazPzn9uxWbpelXEBKAjZzfoawSUgGT5vCYACNfz/yIw1DB067N+HN1KvVddI6TNBA32lpqkQ6VwdWztq6pREE51sNl9p7MUzr+ef0331N5DqQsy+epmRDwebosCx15l/rpvBc91OnxmMMXDNtmxSzVxaZjyGDmJ7RDdTy/Su76AlaMP1zxivxg2MU/9zyTzM16coIAMOd/6Uo9ezKgbZEPeMROKTzAld9BhK9BBPWofoQ0mBkVc7btnahQe3u8HoD6SKCkr9xcTcC9ZKpLkc4svrmxT9e0858pjhis9BbWD/owa6552n2+KwUMRyB8ys7rPL86hh9lBTS+05cVL+BmJfNHOA6ZizdGc3lpwIVbFmzMR5BM0HRf3OCntkWojgsdsP8BGZWHiCGGqA7YGa5AOleR887r8Zhyp47DT3Cn3Rg/icYurIx7Yh0p696gxfANo4jEkE2BOroIscDnhauwck5CCJMcabpTrGwzK8NJ+xZnCUplXnZiIaj85Uh9+yI670B4bybWlZoVmALUxxuQ8bSMAp7CAzMcMWbYJHwBqLF8V2qMj3/g81S3KOptn8b7Idh7IMzAkV8VxE3qAguzwS0zEu8l894sOFUPiJq2/llFeiHNOcEQUGJ+8ATJSAFOMDXAeQS2FoIDOYdesO6yacL0zUkvDydWbA84VXHW8DvdHPli/8hmc++dn5CXSDeBJfC/yypvrpLgkSilZMuHEYHEYHEYEHYEHEYEHEYEHEYEYEYEYEYEYEYEYEYEYEYEYEYEYEYEYEYEYEYYEYEYEYEYEYEYEYYEYEYEYEYEYEYEYEY\r\n",
229+
"220 2.0.0 Kk\r\n",
230+
}
231+
232+
// The pipe is used to avoid bufio.Reader reading the too long line ahead
233+
// of time (in NewClient) and failing eariler than we expect.
234+
pr, pw := io.Pipe()
235+
236+
go func() {
237+
for _, l := range faultyServer {
238+
pw.Write([]byte(l))
239+
}
240+
pw.Close()
241+
}()
242+
243+
var wrote bytes.Buffer
244+
var fake faker
245+
fake.ReadWriter = struct {
246+
io.Reader
247+
io.Writer
248+
}{
249+
pr,
250+
&wrote,
251+
}
252+
c, err := NewClient(fake, "fake.host")
253+
if err != nil {
254+
t.Fatalf("NewClient failed: %v", err)
255+
}
256+
257+
err = c.Mail("whatever", nil)
258+
if err != ErrTooLongLine {
259+
t.Fatal("MAIL succeded or returned a different error:", err)
260+
}
261+
262+
// ErrTooLongLine is "sticky" since the connection is in broken state and
263+
// the only reasonable way to recover is to close it.
264+
err = c.Mail("whatever", nil)
265+
if err != ErrTooLongLine {
266+
t.Fatal("Second MAIL succeded or returned a different error:", err)
267+
}
268+
}
269+
224270
var basicServer = `250 mx.google.com at your service
225271
502 Unrecognized command.
226272
250-mx.google.com at your service

0 commit comments

Comments
 (0)