diff --git a/CHANGELOG b/CHANGELOG index 4d5ecc4a..dca89d19 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,12 @@ +1.2.1 (2017-01-22) + * Fix bug in handling of UDP OTA packets + 1.2.0 (2017-01-20) - * Support UDP reley on server side, and OTA + * Support UDP relay on server side, and OTA * Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin) * Support "chacha20-ietf" encryption method * Improve performance of "chacha20" encryption method - * Corrently close connection if handshake failed + * Correctly close connection if handshake failed 1.1.5 (2016-05-04) * Support OTA (Thanks for @ayanamist for implementing this feature) diff --git a/README.md b/README.md index 185531bc..dfc24f59 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.2.0 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.2.1 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) shadowsocks-go is a lightweight tunnel proxy which can help you get through firewalls. It is a port of [shadowsocks](https://github.com/clowwindy/shadowsocks). diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index 058e93e1..62a55d84 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -92,6 +92,7 @@ func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { packetLen := len(b) + len(iv) if c.ota { + b[idType] |= OneTimeAuthMask packetLen += lenHmacSha1 key := cipher.key actualHmacSha1Buf := HmacSha1(append(iv, key...), b) @@ -129,24 +130,6 @@ func (c *SecurePacketConn) IsOta() bool { return c.ota } -func (c *SecurePacketConn) ForceOTAWriteTo(b []byte, dst net.Addr) (n int, err error) { - cipher := c.Copy() - iv, err := cipher.initEncrypt() - if err != nil { - return - } - packetLen := len(b) + len(iv) - - packetLen += lenHmacSha1 - key := cipher.key - actualHmacSha1Buf := HmacSha1(append(iv, key...), b) - b = append(b, actualHmacSha1Buf...) - - cipherData := make([]byte, packetLen) - copy(cipherData, iv) - - cipher.encrypt(cipherData[len(iv):], b) - n, err = c.PacketConn.WriteTo(cipherData, dst) - n -= lenHmacSha1 - return +func (c *SecurePacketConn) ForceOTA() net.PacketConn { + return NewSecurePacketConn(c.PacketConn, c.Cipher.Copy(), true) } diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index c38dbe0a..727efb67 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -43,14 +43,15 @@ func newNatTable() *natTable { return &natTable{conns: map[string]net.PacketConn{}} } -func (table *natTable) DeleteAndClose(index string) { +func (table *natTable) Delete(index string) net.PacketConn { table.Lock() defer table.Unlock() c, ok := table.conns[index] if ok { - c.Close() delete(table.conns, index) + return c } + return nil } func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) { @@ -67,13 +68,13 @@ func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) return } -type ReqList struct { +type requestHeaderList struct { sync.Mutex List map[string]([]byte) } -func newReqList() *ReqList { - ret := &ReqList{List: map[string]([]byte){}} +func newReqList() *requestHeaderList { + ret := &requestHeaderList{List: map[string]([]byte){}} go func() { for { time.Sleep(reqListRefreshTime) @@ -83,7 +84,7 @@ func newReqList() *ReqList { return ret } -func (r *ReqList) Refresh() { +func (r *requestHeaderList) Refresh() { r.Lock() defer r.Unlock() for k, _ := range r.List { @@ -91,21 +92,21 @@ func (r *ReqList) Refresh() { } } -func (r *ReqList) Get(dstaddr string) (req []byte, ok bool) { +func (r *requestHeaderList) Get(dstaddr string) (req []byte, ok bool) { r.Lock() defer r.Unlock() req, ok = r.List[dstaddr] return } -func (r *ReqList) Put(dstaddr string, req []byte) { +func (r *requestHeaderList) Put(dstaddr string, req []byte) { r.Lock() defer r.Unlock() r.List[dstaddr] = req return } -func ParseHeader(addr net.Addr) ([]byte, int) { +func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { // if the request address type is domain, it cannot be reverselookuped ip, port, err := net.SplitHostPort(addr.String()) if err != nil { @@ -129,13 +130,13 @@ func ParseHeader(addr net.Addr) ([]byte, int) { return buf[:1+iplen+2], 1 + iplen + 2 } -func Pipeloop(ss *SecurePacketConn, addr net.Addr, in net.PacketConn, compatiblemode bool) { +func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn) { buf := leakyBuf.Get() defer leakyBuf.Put(buf) - defer in.Close() + defer readClose.Close() for { - in.SetDeadline(time.Now().Add(udpTimeout)) - n, raddr, err := in.ReadFrom(buf) + readClose.SetDeadline(time.Now().Add(udpTimeout)) + n, raddr, err := readClose.ReadFrom(buf) if err != nil { if ne, ok := err.(*net.OpError); ok { if ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE { @@ -144,24 +145,15 @@ func Pipeloop(ss *SecurePacketConn, addr net.Addr, in net.PacketConn, compatible Debug.Println("[udp]read error:", err) } } - Debug.Printf("[udp]closed pipe %s<-%s\n", addr, in.LocalAddr()) + Debug.Printf("[udp]closed pipe %s<-%s\n", writeAddr, readClose.LocalAddr()) return } // need improvement here if req, ok := reqList.Get(raddr.String()); ok { - if compatiblemode { - ss.ForceOTAWriteTo(append(req, buf[:n]...), addr) - } else { - ss.WriteTo(append(req, buf[:n]...), addr) - } + write.WriteTo(append(req, buf[:n]...), writeAddr) } else { - header, hlen := ParseHeader(raddr) - if compatiblemode { - ss.ForceOTAWriteTo(append(header[:hlen], buf[:n]...), addr) - } else { - ss.WriteTo(append(header[:hlen], buf[:n]...), addr) - } - + header, hlen := parseHeaderFromAddr(raddr) + write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) } } } @@ -176,6 +168,7 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if addrType&OneTimeAuthMask > 0 { ota = true } + receive[idType] &= ^OneTimeAuthMask compatiblemode := !handle.IsOta() && ota switch addrType & AddrMask { @@ -229,8 +222,13 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if !exist { Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) go func() { - Pipeloop(handle, src, remote, compatiblemode) - natlist.DeleteAndClose(src.String()) + if compatiblemode { + Pipeloop(handle.ForceOTA(), src, remote) + } else { + Pipeloop(handle, src, remote) + } + + natlist.Delete(src.String()) }() } else { Debug.Printf("[udp]using cached client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) @@ -248,7 +246,9 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive } else { Debug.Println("[udp]error connecting to:", dst, err) } - natlist.DeleteAndClose(src.String()) + if conn := natlist.Delete(src.String()); conn != nil { + conn.Close() + } } // Pipeloop return