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

fix obfs-tls with shadowsocks aead issue #695 #697

Merged
merged 1 commit into from
Feb 6, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 123 additions & 34 deletions obfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,24 +313,128 @@ var (
0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,
0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203,
}

tlsRecordTypes = []uint8{0x16, 0x14, 0x16, 0x17}
tlsVersionMinors = []uint8{0x01, 0x03, 0x03, 0x03}

ErrBadType = errors.New("bad type")
ErrBadMajorVersion = errors.New("bad major version")
ErrBadMinorVersion = errors.New("bad minor version")
ErrMaxDataLen = errors.New("bad tls data len")
)

const (
tlsRecordStateType = iota
tlsRecordStateVersion0
tlsRecordStateVersion1
tlsRecordStateLength0
tlsRecordStateLength1
tlsRecordStateData
)

type obfsTLSParser struct {
step uint8
state uint8
length uint16
}

type obfsTLSConn struct {
net.Conn
rbuf bytes.Buffer
wbuf bytes.Buffer
host string
isServer bool
handshaked chan struct{}
parser *obfsTLSParser
handshakeMutex sync.Mutex
}

func (r *obfsTLSParser) Parse(b []byte) (int, error) {
i := 0
last := 0
length := len(b)

for i < length {
ch := b[i]
switch r.state {
case tlsRecordStateType:
if tlsRecordTypes[r.step] != ch {
return 0, ErrBadType
}
r.state = tlsRecordStateVersion0
i++
case tlsRecordStateVersion0:
if ch != 0x03 {
return 0, ErrBadMajorVersion
}
r.state = tlsRecordStateVersion1
i++
case tlsRecordStateVersion1:
if ch != tlsVersionMinors[r.step] {
return 0, ErrBadMinorVersion
}
r.state = tlsRecordStateLength0
i++
case tlsRecordStateLength0:
r.length = uint16(ch) << 8
r.state = tlsRecordStateLength1
i++
case tlsRecordStateLength1:
r.length |= uint16(ch)
if r.step == 0 {
r.length = 91
} else if r.step == 1 {
r.length = 1
} else if r.length > maxTLSDataLen {
return 0, ErrMaxDataLen
}
if r.length > 0 {
r.state = tlsRecordStateData
} else {
r.state = tlsRecordStateType
r.step++
}
i++
case tlsRecordStateData:
left := uint16(length - i)
if left > r.length {
left = r.length
}
if r.step >= 2 {
skip := i - last
copy(b[last:], b[i:length])
length -= int(skip)
last += int(left)
i = last
} else {
i += int(left)
}
r.length -= left
if r.length == 0 {
if r.step < 3 {
r.step++
}
r.state = tlsRecordStateType
}
}
}

if last == 0 {
return 0, nil
} else if last < length {
length -= last
}

return length, nil
}

// ClientObfsTLSConn creates a connection for obfs-tls client.
func ClientObfsTLSConn(conn net.Conn, host string) net.Conn {
return &obfsTLSConn{
Conn: conn,
host: host,
handshaked: make(chan struct{}),
parser: &obfsTLSParser{},
}
}

Expand Down Expand Up @@ -416,32 +520,6 @@ func (c *obfsTLSConn) clientHandshake(payload []byte) error {
if _, err := record.WriteTo(c.Conn); err != nil {
return err
}

// server hello handshake message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.Handshake {
return dissector.ErrBadType
}

// change cipher spec message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.ChangeCipherSpec {
return dissector.ErrBadType
}

// encrypted handshake message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.Handshake {
return dissector.ErrBadType
}

_, err = c.rbuf.Write(record.Opaque)
return err
}

Expand Down Expand Up @@ -521,19 +599,30 @@ func (c *obfsTLSConn) Read(b []byte) (n int, err error) {
return
}
}

select {
case <-c.handshaked:
}

if c.rbuf.Len() > 0 {
return c.rbuf.Read(b)
}
record := &dissector.Record{}
if _, err = record.ReadFrom(c.Conn); err != nil {
return
if c.isServer {
if c.rbuf.Len() > 0 {
return c.rbuf.Read(b)
}
record := &dissector.Record{}
if _, err = record.ReadFrom(c.Conn); err != nil {
return
}
n = copy(b, record.Opaque)
_, err = c.rbuf.Write(record.Opaque[n:])
} else {
n, err = c.Conn.Read(b)
if err != nil {
return
}
if n > 0 {
n, err = c.parser.Parse(b[:n])
}
}
n = copy(b, record.Opaque)
_, err = c.rbuf.Write(record.Opaque[n:])
return
}

Expand Down