Skip to content

Commit

Permalink
client: Close the connection on mismatched literal length
Browse files Browse the repository at this point in the history
  • Loading branch information
foxcpp authored and emersion committed Dec 1, 2019
1 parent 63cf5f3 commit c8e5db3
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 3 deletions.
12 changes: 12 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ func (c *Client) execute(cmdr imap.Commander, h responses.Handler) (*imap.Status
if err := cmd.WriteTo(c.conn.Writer); err != nil {
// Error while sending the command
close(unregister)

if err, ok := err.(imap.LiteralLengthErr); ok {
// Expected > Actual
// The server is waiting for us to write
// more bytes, we don't have them. Run.
// Expected < Actual
// We are about to send a potentially truncated message, we don't
// want this (ths terminating CRLF is not sent at this point).
c.conn.Close()
return nil, err
}

return nil, err
}
// Flush writer if we are upgrading
Expand Down
97 changes: 97 additions & 0 deletions client/cmd_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,103 @@ func TestClient_Status(t *testing.T) {
}
}

type literalWrap struct {
io.Reader
L int
}

func (lw literalWrap) Len() int {
return lw.L
}

func TestClient_Append_SmallerLiteral(t *testing.T) {
c, s := newTestClient(t)
defer s.Close()

setClientState(c, imap.AuthenticatedState, nil)

msg := "Hello World!\r\nHello Gophers!\r\n"
date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
flags := []string{imap.SeenFlag, imap.DraftFlag}

r := bytes.NewBufferString(msg)

done := make(chan error, 1)
go func() {
done <- c.Append("INBOX", flags, date, literalWrap{r, 35})

// The buffer is not flushed on error, force it so io.ReadFull can
// continue.
c.conn.Flush()
}()

tag, _ := s.ScanCmd()
s.WriteString("+ send literal\r\n")

b := make([]byte, 30)
// The client will close connection.
if _, err := io.ReadFull(s, b); err != io.EOF {
t.Error("Expected EOF, got", err)
}

s.WriteString(tag + " OK APPEND completed\r\n")

err, ok := (<-done).(imap.LiteralLengthErr)
if !ok {
t.Fatalf("c.Append() = %v", err)
}
if err.Expected != 35 {
t.Fatalf("err.Expected = %v", err.Expected)
}
if err.Actual != 30 {
t.Fatalf("err.Actual = %v", err.Actual)
}
}

func TestClient_Append_BiggerLiteral(t *testing.T) {
c, s := newTestClient(t)
defer s.Close()

setClientState(c, imap.AuthenticatedState, nil)

msg := "Hello World!\r\nHello Gophers!\r\n"
date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
flags := []string{imap.SeenFlag, imap.DraftFlag}

r := bytes.NewBufferString(msg)

done := make(chan error, 1)
go func() {
done <- c.Append("INBOX", flags, date, literalWrap{r, 25})

// The buffer is not flushed on error, force it so io.ReadFull can
// continue.
c.conn.Flush()
}()

tag, _ := s.ScanCmd()
s.WriteString("+ send literal\r\n")

// The client will close connection.
b := make([]byte, 25)
if _, err := io.ReadFull(s, b); err != io.EOF {
t.Error("Expected EOF, got", err)
}

s.WriteString(tag + " OK APPEND completed\r\n")

err, ok := (<-done).(imap.LiteralLengthErr)
if !ok {
t.Fatalf("c.Append() = %v", err)
}
if err.Expected != 25 {
t.Fatalf("err.Expected = %v", err.Expected)
}
if err.Actual != 30 {
t.Fatalf("err.Actual = %v", err.Actual)
}
}

func TestClient_Append(t *testing.T) {
c, s := newTestClient(t)
defer s.Close()
Expand Down
19 changes: 16 additions & 3 deletions write.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strconv"
"time"
"unicode"
Expand Down Expand Up @@ -120,6 +121,17 @@ func (w *Writer) writeList(fields []interface{}) error {
return w.writeString(string(listEnd))
}

// LiteralLengthErr is returned when the Len() of the Literal object does not
// match the actual length of the byte stream.
type LiteralLengthErr struct {
Actual int
Expected int
}

func (e LiteralLengthErr) Error() string {
return fmt.Sprintf("imap: size of Literal is not equal to Len() (%d != %d)", e.Expected, e.Actual)
}

func (w *Writer) writeLiteral(l Literal) error {
if l == nil {
return w.writeString(nilAtom)
Expand Down Expand Up @@ -154,12 +166,13 @@ func (w *Writer) writeLiteral(l Literal) error {
n, err := io.CopyN(w, l, literalLen)
if err != nil {
if err == io.EOF && n != literalLen {
return fmt.Errorf("imap: size of Literal is not equal to Len() (%d != %d)", n, l.Len())
return LiteralLengthErr{int(n), l.Len()}
}
return err
}
if n != literalLen {
return fmt.Errorf("imap: size of Literal is not equal to Len() (%d != %d)", n, l.Len())
extra, _ := io.Copy(ioutil.Discard, l)
if extra != 0 {
return LiteralLengthErr{int(n + extra), l.Len()}
}

return nil
Expand Down

0 comments on commit c8e5db3

Please sign in to comment.