1
0

Chore: use protobytes replace most of bytes.Buffer

This commit is contained in:
Dreamacro 2023-04-17 14:08:39 +08:00
parent df61a586c9
commit b7aade5e11
15 changed files with 244 additions and 236 deletions

View File

@ -1,7 +1,6 @@
package outbound package outbound
import ( import (
"bytes"
"net" "net"
"strconv" "strconv"
"time" "time"
@ -9,6 +8,8 @@ import (
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/protobytes"
) )
func tcpKeepAlive(c net.Conn) { func tcpKeepAlive(c net.Conn) {
@ -19,24 +20,24 @@ func tcpKeepAlive(c net.Conn) {
} }
func serializesSocksAddr(metadata *C.Metadata) []byte { func serializesSocksAddr(metadata *C.Metadata) []byte {
var buf [][]byte buf := protobytes.BytesWriter{}
addrType := metadata.AddrType() addrType := metadata.AddrType()
aType := uint8(addrType) buf.PutUint8(uint8(addrType))
p, _ := strconv.ParseUint(metadata.DstPort, 10, 16) p, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
port := []byte{uint8(p >> 8), uint8(p & 0xff)}
switch addrType { switch addrType {
case socks5.AtypDomainName: case socks5.AtypDomainName:
len := uint8(len(metadata.Host)) buf.PutUint8(uint8(len(metadata.Host)))
host := []byte(metadata.Host) buf.PutString(metadata.Host)
buf = [][]byte{{aType, len}, host, port}
case socks5.AtypIPv4: case socks5.AtypIPv4:
host := metadata.DstIP.To4() buf.PutSlice(metadata.DstIP.To4())
buf = [][]byte{{aType}, host, port}
case socks5.AtypIPv6: case socks5.AtypIPv6:
host := metadata.DstIP.To16() buf.PutSlice(metadata.DstIP.To16())
buf = [][]byte{{aType}, host, port}
} }
return bytes.Join(buf, nil)
buf.PutUint16be(uint16(p))
return buf.Bytes()
} }
func resolveUDPAddr(network, address string) (*net.UDPAddr, error) { func resolveUDPAddr(network, address string) (*net.UDPAddr, error) {

View File

@ -3,9 +3,14 @@ package pool
import ( import (
"bytes" "bytes"
"sync" "sync"
"github.com/Dreamacro/protobytes"
) )
var bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }} var (
bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }}
bytesBufferPool = sync.Pool{New: func() any { return &protobytes.BytesWriter{} }}
)
func GetBuffer() *bytes.Buffer { func GetBuffer() *bytes.Buffer {
return bufferPool.Get().(*bytes.Buffer) return bufferPool.Get().(*bytes.Buffer)
@ -15,3 +20,12 @@ func PutBuffer(buf *bytes.Buffer) {
buf.Reset() buf.Reset()
bufferPool.Put(buf) bufferPool.Put(buf)
} }
func GetBytesBuffer() *protobytes.BytesWriter {
return bytesBufferPool.Get().(*protobytes.BytesWriter)
}
func PutBytesBuffer(buf *protobytes.BytesWriter) {
buf.Reset()
bufferPool.Put(buf)
}

1
go.mod
View File

@ -26,6 +26,7 @@ require (
) )
require ( require (
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd // indirect
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-cmp v0.5.9 // indirect

2
go.sum
View File

@ -1,3 +1,5 @@
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd h1:ygk7IF14j4ep4H2ZyeDe3IEoMZF8JdbX851RVVa/4D8=
github.com/Dreamacro/protobytes v0.0.0-20230324064118-87bc784139cd/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=

View File

@ -1,7 +1,6 @@
package route package route
import ( import (
"bytes"
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv" "strconv"
@ -9,6 +8,7 @@ import (
"github.com/Dreamacro/clash/tunnel/statistic" "github.com/Dreamacro/clash/tunnel/statistic"
"github.com/Dreamacro/protobytes"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -47,11 +47,11 @@ func getConnections(w http.ResponseWriter, r *http.Request) {
interval = t interval = t
} }
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
sendSnapshot := func() error { sendSnapshot := func() error {
buf.Reset() buf.Reset()
snapshot := statistic.DefaultManager.Snapshot() snapshot := statistic.DefaultManager.Snapshot()
if err := json.NewEncoder(buf).Encode(snapshot); err != nil { if err := json.NewEncoder(&buf).Encode(snapshot); err != nil {
return err return err
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel/statistic" "github.com/Dreamacro/clash/tunnel/statistic"
"github.com/Dreamacro/protobytes"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/cors" "github.com/go-chi/cors"
"github.com/go-chi/render" "github.com/go-chi/render"
@ -151,12 +152,12 @@ func traffic(w http.ResponseWriter, r *http.Request) {
tick := time.NewTicker(time.Second) tick := time.NewTicker(time.Second)
defer tick.Stop() defer tick.Stop()
t := statistic.DefaultManager t := statistic.DefaultManager
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
var err error var err error
for range tick.C { for range tick.C {
buf.Reset() buf.Reset()
up, down := t.Now() up, down := t.Now()
if err := json.NewEncoder(buf).Encode(Traffic{ if err := json.NewEncoder(&buf).Encode(Traffic{
Up: up, Up: up,
Down: down, Down: down,
}); err != nil { }); err != nil {

View File

@ -126,11 +126,11 @@ func (g *Conn) Write(b []byte) (n int, err error) {
grpcPayloadLen := uint32(varuintSize + 1 + len(b)) grpcPayloadLen := uint32(varuintSize + 1 + len(b))
binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen) binary.BigEndian.PutUint32(grpcHeader[1:5], grpcPayloadLen)
buf := pool.GetBuffer() buf := pool.GetBytesBuffer()
defer pool.PutBuffer(buf) defer pool.PutBytesBuffer(buf)
buf.Write(grpcHeader) buf.PutSlice(grpcHeader)
buf.Write(protobufHeader[:varuintSize+1]) buf.PutSlice(protobufHeader[:varuintSize+1])
buf.Write(b) buf.PutSlice(b)
_, err = g.writer.Write(buf.Bytes()) _, err = g.writer.Write(buf.Bytes())
if err == io.ErrClosedPipe && g.err != nil { if err == io.ErrClosedPipe && g.err != nil {

View File

@ -1,7 +1,6 @@
package obfs package obfs
import ( import (
"bytes"
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"io" "io"
@ -9,6 +8,8 @@ import (
"time" "time"
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/protobytes"
) )
const ( const (
@ -99,11 +100,11 @@ func (to *TLSObfs) write(b []byte) (int, error) {
return len(b), err return len(b), err
} }
buf := pool.GetBuffer() buf := pool.GetBytesBuffer()
defer pool.PutBuffer(buf) defer pool.PutBytesBuffer(buf)
buf.Write([]byte{0x17, 0x03, 0x03}) buf.PutSlice([]byte{0x17, 0x03, 0x03})
binary.Write(buf, binary.BigEndian, uint16(len(b))) buf.PutUint16be(uint16(len(b)))
buf.Write(b) buf.PutSlice(b)
_, err := to.Conn.Write(buf.Bytes()) _, err := to.Conn.Write(buf.Bytes())
return len(b), err return len(b), err
} }
@ -119,35 +120,29 @@ func NewTLSObfs(conn net.Conn, server string) net.Conn {
} }
func makeClientHelloMsg(data []byte, server string) []byte { func makeClientHelloMsg(data []byte, server string) []byte {
random := make([]byte, 28) buf := protobytes.BytesWriter{}
sessionID := make([]byte, 32)
rand.Read(random)
rand.Read(sessionID)
buf := &bytes.Buffer{}
// handshake, TLS 1.0 version, length // handshake, TLS 1.0 version, length
buf.WriteByte(22) buf.PutUint8(22)
buf.Write([]byte{0x03, 0x01}) buf.PutSlice([]byte{0x03, 0x01})
length := uint16(212 + len(data) + len(server)) length := uint16(212 + len(data) + len(server))
buf.WriteByte(byte(length >> 8)) buf.PutUint16be(length)
buf.WriteByte(byte(length & 0xff))
// clientHello, length, TLS 1.2 version // clientHello, length, TLS 1.2 version
buf.WriteByte(1) buf.PutUint8(1)
buf.WriteByte(0) buf.PutUint8(0)
binary.Write(buf, binary.BigEndian, uint16(208+len(data)+len(server))) buf.PutUint16be(uint16(208 + len(data) + len(server)))
buf.Write([]byte{0x03, 0x03}) buf.PutSlice([]byte{0x03, 0x03})
// random with timestamp, sid len, sid // random with timestamp, sid len, sid
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix())) buf.PutUint32be(uint32(time.Now().Unix()))
buf.Write(random) buf.ReadFull(rand.Reader, 28)
buf.WriteByte(32) buf.PutUint8(32)
buf.Write(sessionID) buf.ReadFull(rand.Reader, 32)
// cipher suites // cipher suites
buf.Write([]byte{0x00, 0x38}) buf.PutSlice([]byte{0x00, 0x38})
buf.Write([]byte{ buf.PutSlice([]byte{
0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f,
0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a,
0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d, 0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d,
@ -155,42 +150,42 @@ func makeClientHelloMsg(data []byte, server string) []byte {
}) })
// compression // compression
buf.Write([]byte{0x01, 0x00}) buf.PutSlice([]byte{0x01, 0x00})
// extension length // extension length
binary.Write(buf, binary.BigEndian, uint16(79+len(data)+len(server))) buf.PutUint16be(uint16(79 + len(data) + len(server)))
// session ticket // session ticket
buf.Write([]byte{0x00, 0x23}) buf.PutSlice([]byte{0x00, 0x23})
binary.Write(buf, binary.BigEndian, uint16(len(data))) buf.PutUint16be(uint16(len(data)))
buf.Write(data) buf.PutSlice(data)
// server name // server name
buf.Write([]byte{0x00, 0x00}) buf.PutSlice([]byte{0x00, 0x00})
binary.Write(buf, binary.BigEndian, uint16(len(server)+5)) buf.PutUint16be(uint16(len(server) + 5))
binary.Write(buf, binary.BigEndian, uint16(len(server)+3)) buf.PutUint16be(uint16(len(server) + 3))
buf.WriteByte(0) buf.PutUint8(0)
binary.Write(buf, binary.BigEndian, uint16(len(server))) buf.PutUint16be(uint16(len(server)))
buf.Write([]byte(server)) buf.PutSlice([]byte(server))
// ec_point // ec_point
buf.Write([]byte{0x00, 0x0b, 0x00, 0x04, 0x03, 0x01, 0x00, 0x02}) buf.PutSlice([]byte{0x00, 0x0b, 0x00, 0x04, 0x03, 0x01, 0x00, 0x02})
// groups // groups
buf.Write([]byte{0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18}) buf.PutSlice([]byte{0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18})
// signature // signature
buf.Write([]byte{ buf.PutSlice([]byte{
0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05,
0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01,
0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
}) })
// encrypt then mac // encrypt then mac
buf.Write([]byte{0x00, 0x16, 0x00, 0x00}) buf.PutSlice([]byte{0x00, 0x16, 0x00, 0x00})
// extended master secret // extended master secret
buf.Write([]byte{0x00, 0x17, 0x00, 0x00}) buf.PutSlice([]byte{0x00, 0x17, 0x00, 0x00})
return buf.Bytes() return buf.Bytes()
} }

View File

@ -1,7 +1,6 @@
package snell package snell
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -83,22 +82,22 @@ func (s *Snell) Read(b []byte) (int, error) {
} }
func WriteHeader(conn net.Conn, host string, port uint, version int) error { func WriteHeader(conn net.Conn, host string, port uint, version int) error {
buf := pool.GetBuffer() buf := pool.GetBytesBuffer()
defer pool.PutBuffer(buf) defer pool.PutBytesBuffer(buf)
buf.WriteByte(Version) buf.PutUint8(Version)
if version == Version2 { if version == Version2 {
buf.WriteByte(CommandConnectV2) buf.PutUint8(CommandConnectV2)
} else { } else {
buf.WriteByte(CommandConnect) buf.PutUint8(CommandConnect)
} }
// clientID length & id // clientID length & id
buf.WriteByte(0) buf.PutUint8(0)
// host & port // host & port
buf.WriteByte(uint8(len(host))) buf.PutUint8(uint8(len(host)))
buf.WriteString(host) buf.PutString(host)
binary.Write(buf, binary.BigEndian, uint16(port)) buf.PutUint16be(uint16(port))
if _, err := conn.Write(buf.Bytes()); err != nil { if _, err := conn.Write(buf.Bytes()); err != nil {
return err return err
@ -146,25 +145,25 @@ func PacketConn(conn net.Conn) net.PacketConn {
} }
func writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) { func writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {
buf := pool.GetBuffer() buf := pool.GetBytesBuffer()
defer pool.PutBuffer(buf) defer pool.PutBytesBuffer(buf)
// compose snell UDP address format (refer: icpz/snell-server-reversed) // compose snell UDP address format (refer: icpz/snell-server-reversed)
// a brand new wheel to replace socks5 address format, well done Yachen // a brand new wheel to replace socks5 address format, well done Yachen
buf.WriteByte(CommondUDPForward) buf.PutUint8(CommondUDPForward)
switch socks5Addr[0] { switch socks5Addr[0] {
case socks5.AtypDomainName: case socks5.AtypDomainName:
hostLen := socks5Addr[1] hostLen := socks5Addr[1]
buf.Write(socks5Addr[1 : 1+1+hostLen+2]) buf.PutSlice(socks5Addr[1 : 1+1+hostLen+2])
case socks5.AtypIPv4: case socks5.AtypIPv4:
buf.Write([]byte{0x00, 0x04}) buf.PutSlice([]byte{0x00, 0x04})
buf.Write(socks5Addr[1 : 1+net.IPv4len+2]) buf.PutSlice(socks5Addr[1 : 1+net.IPv4len+2])
case socks5.AtypIPv6: case socks5.AtypIPv6:
buf.Write([]byte{0x00, 0x06}) buf.PutSlice([]byte{0x00, 0x06})
buf.Write(socks5Addr[1 : 1+net.IPv6len+2]) buf.PutSlice(socks5Addr[1 : 1+net.IPv6len+2])
} }
buf.Write(payload) buf.PutSlice(payload)
_, err := w.Write(buf.Bytes()) _, err := w.Write(buf.Bytes())
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -1,14 +1,15 @@
package socks4 package socks4
import ( import (
"bytes"
"encoding/binary"
"errors" "errors"
"io" "io"
"net" "net"
"net/netip"
"strconv" "strconv"
"github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/protobytes"
) )
const Version = 0x04 const Version = 0x04
@ -46,21 +47,17 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
return return
} }
if req[0] != Version { r := protobytes.BytesReader(req[:])
if r.ReadUint8() != Version {
err = errVersionMismatched err = errVersionMismatched
return return
} }
if command = req[1]; command != CmdConnect { if command = r.ReadUint8(); command != CmdConnect {
err = errCommandNotSupported err = errCommandNotSupported
return return
} }
var (
dstIP = req[4:8] // [4]byte
dstPort = req[2:4] // [2]byte
)
var ( var (
host string host string
port string port string
@ -71,7 +68,9 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
return return
} }
if isReservedIP(dstIP) { dstAddr := r.ReadIPv4()
dstPort := r.ReadUint16be()
if isReservedIP(dstAddr) {
var target []byte var target []byte
if target, err = readUntilNull(rw); err != nil { if target, err = readUntilNull(rw); err != nil {
return return
@ -79,11 +78,11 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
host = string(target) host = string(target)
} }
port = strconv.Itoa(int(binary.BigEndian.Uint16(dstPort))) port = strconv.Itoa(int(dstPort))
if host != "" { if host != "" {
addr = net.JoinHostPort(host, port) addr = net.JoinHostPort(host, port)
} else { } else {
addr = net.JoinHostPort(net.IP(dstIP).String(), port) addr = net.JoinHostPort(dstAddr.String(), port)
} }
// SOCKS4 only support USERID auth. // SOCKS4 only support USERID auth.
@ -94,13 +93,13 @@ func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr s
err = ErrRequestIdentdMismatched err = ErrRequestIdentdMismatched
} }
var reply [8]byte reply := protobytes.BytesWriter(make([]byte, 0, 8))
reply[0] = 0x00 // reply code reply.PutUint8(0) // reply code
reply[1] = code // result code reply.PutUint8(code) // result code
copy(reply[4:8], dstIP) reply.PutSlice(dstAddr.AsSlice())
copy(reply[2:4], dstPort) reply.PutUint16be(dstPort)
_, wErr := rw.Write(reply[:]) _, wErr := rw.Write(reply.Bytes())
if err == nil { if err == nil {
err = wErr err = wErr
} }
@ -118,26 +117,24 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
return err return err
} }
ip := net.ParseIP(host) ip, err := netip.ParseAddr(host)
if ip == nil /* HOST */ { if err != nil { // Host
ip = net.IPv4(0, 0, 0, 1).To4() ip = netip.AddrFrom4([4]byte{0, 0, 0, 1})
} else if ip.To4() == nil /* IPv6 */ { } else if ip.Is6() { // IPv6
return errIPv6NotSupported return errIPv6NotSupported
} }
dstIP := ip.To4() req := protobytes.BytesWriter{}
req.PutUint8(Version)
req.PutUint8(command)
req.PutUint16be(uint16(port))
req.Write(ip.AsSlice())
req.PutString(userID)
req.PutUint8(0) /* NULL */
req := &bytes.Buffer{} if isReservedIP(ip) /* SOCKS4A */ {
req.WriteByte(Version) req.PutString(host)
req.WriteByte(command) req.PutUint8(0) /* NULL */
binary.Write(req, binary.BigEndian, uint16(port))
req.Write(dstIP)
req.WriteString(userID)
req.WriteByte(0) /* NULL */
if isReservedIP(dstIP) /* SOCKS4A */ {
req.WriteString(host)
req.WriteByte(0) /* NULL */
} }
if _, err = rw.Write(req.Bytes()); err != nil { if _, err = rw.Write(req.Bytes()); err != nil {
@ -174,17 +171,17 @@ func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID stri
// Internet Assigned Numbers Authority -- such an address is inadmissible // Internet Assigned Numbers Authority -- such an address is inadmissible
// as a destination IP address and thus should never occur if the client // as a destination IP address and thus should never occur if the client
// can resolve the domain name.) // can resolve the domain name.)
func isReservedIP(ip net.IP) bool { func isReservedIP(ip netip.Addr) bool {
subnet := net.IPNet{ subnet := netip.PrefixFrom(
IP: net.IPv4zero, netip.AddrFrom4([4]byte{0, 0, 0, 0}),
Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00), 24,
} )
return !ip.IsUnspecified() && subnet.Contains(ip) return !ip.IsUnspecified() && subnet.Contains(ip)
} }
func readUntilNull(r io.Reader) ([]byte, error) { func readUntilNull(r io.Reader) ([]byte, error) {
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
var data [1]byte var data [1]byte
for { for {
@ -194,6 +191,6 @@ func readUntilNull(r io.Reader) ([]byte, error) {
if data[0] == 0 { if data[0] == 0 {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
buf.WriteByte(data[0]) buf.PutUint8(data[0])
} }
} }

View File

@ -10,6 +10,8 @@ import (
"strconv" "strconv"
"github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/protobytes"
) )
// Error represents a SOCKS error // Error represents a SOCKS error
@ -235,12 +237,12 @@ func ClientHandshake(rw io.ReadWriter, addr Addr, command Command, user *User) (
} }
// password protocol version // password protocol version
authMsg := &bytes.Buffer{} authMsg := protobytes.BytesWriter{}
authMsg.WriteByte(1) authMsg.PutUint8(1)
authMsg.WriteByte(uint8(len(user.Username))) authMsg.PutUint8(uint8(len(user.Username)))
authMsg.WriteString(user.Username) authMsg.PutString(user.Username)
authMsg.WriteByte(uint8(len(user.Password))) authMsg.PutUint8(uint8(len(user.Password)))
authMsg.WriteString(user.Password) authMsg.PutString(user.Password)
if _, err := rw.Write(authMsg.Bytes()); err != nil { if _, err := rw.Write(authMsg.Bytes()); err != nil {
return nil, err return nil, err
@ -330,29 +332,26 @@ func SplitAddr(b []byte) Addr {
// ParseAddr parses the address in string s. Returns nil if failed. // ParseAddr parses the address in string s. Returns nil if failed.
func ParseAddr(s string) Addr { func ParseAddr(s string) Addr {
var addr Addr buf := protobytes.BytesWriter{}
host, port, err := net.SplitHostPort(s) host, port, err := net.SplitHostPort(s)
if err != nil { if err != nil {
return nil return nil
} }
if ip := net.ParseIP(host); ip != nil { if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil { if ip4 := ip.To4(); ip4 != nil {
addr = make([]byte, 1+net.IPv4len+2) buf.PutUint8(AtypIPv4)
addr[0] = AtypIPv4 buf.PutSlice(ip4)
copy(addr[1:], ip4)
} else { } else {
addr = make([]byte, 1+net.IPv6len+2) buf.PutUint8(AtypIPv6)
addr[0] = AtypIPv6 buf.PutSlice(ip)
copy(addr[1:], ip)
} }
} else { } else {
if len(host) > 255 { if len(host) > 255 {
return nil return nil
} }
addr = make([]byte, 1+1+len(host)+2) buf.PutUint8(AtypDomainName)
addr[0] = AtypDomainName buf.PutUint8(byte(len(host)))
addr[1] = byte(len(host)) buf.PutString(host)
copy(addr[2:], host)
} }
portnum, err := strconv.ParseUint(port, 10, 16) portnum, err := strconv.ParseUint(port, 10, 16)
@ -360,9 +359,8 @@ func ParseAddr(s string) Addr {
return nil return nil
} }
addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum) buf.PutUint16be(uint16(portnum))
return Addr(buf.Bytes())
return addr
} }
// ParseAddrToSocksAddr parse a socks addr from net.addr // ParseAddrToSocksAddr parse a socks addr from net.addr
@ -383,20 +381,19 @@ func ParseAddrToSocksAddr(addr net.Addr) Addr {
return ParseAddr(addr.String()) return ParseAddr(addr.String())
} }
var parsed Addr var parsed protobytes.BytesWriter
if ip4 := hostip.To4(); ip4.DefaultMask() != nil { if ip4 := hostip.To4(); ip4.DefaultMask() != nil {
parsed = make([]byte, 1+net.IPv4len+2) parsed = make([]byte, 0, 1+net.IPv4len+2)
parsed[0] = AtypIPv4 parsed.PutUint8(AtypIPv4)
copy(parsed[1:], ip4) parsed.PutSlice(ip4)
binary.BigEndian.PutUint16(parsed[1+net.IPv4len:], uint16(port)) parsed.PutUint16be(uint16(port))
} else { } else {
parsed = make([]byte, 1+net.IPv6len+2) parsed = make([]byte, 0, 1+net.IPv6len+2)
parsed[0] = AtypIPv6 parsed.PutUint8(AtypIPv6)
copy(parsed[1:], hostip) parsed.PutSlice(hostip)
binary.BigEndian.PutUint16(parsed[1+net.IPv6len:], uint16(port)) parsed.PutUint16be(uint16(port))
} }
return parsed return Addr(parsed)
} }
func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr { func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {
@ -416,28 +413,31 @@ func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {
// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet` // DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`
func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) { func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {
if len(packet) < 5 { r := protobytes.BytesReader(packet)
if r.Len() < 5 {
err = errors.New("insufficient length of packet") err = errors.New("insufficient length of packet")
return return
} }
// packet[0] and packet[1] are reserved // packet[0] and packet[1] are reserved
if !bytes.Equal(packet[:2], []byte{0, 0}) { reserved, r := r.SplitAt(2)
if !bytes.Equal(reserved, []byte{0, 0}) {
err = errors.New("reserved fields should be zero") err = errors.New("reserved fields should be zero")
return return
} }
if packet[2] != 0 /* fragments */ { if r.ReadUint8() != 0 /* fragments */ {
err = errors.New("discarding fragmented payload") err = errors.New("discarding fragmented payload")
return return
} }
addr = SplitAddr(packet[3:]) addr = SplitAddr(r)
if addr == nil { if addr == nil {
err = errors.New("failed to read UDP header") err = errors.New("failed to read UDP header")
} }
payload = packet[3+len(addr):] _, payload = r.SplitAt(len(addr))
return return
} }
@ -446,6 +446,10 @@ func EncodeUDPPacket(addr Addr, payload []byte) (packet []byte, err error) {
err = errors.New("address is invalid") err = errors.New("address is invalid")
return return
} }
packet = bytes.Join([][]byte{{0, 0, 0}, addr, payload}, []byte{}) w := protobytes.BytesWriter{}
w.PutSlice([]byte{0, 0, 0})
w.PutSlice(addr)
w.PutSlice(payload)
packet = w.Bytes()
return return
} }

View File

@ -12,10 +12,11 @@ import (
"net/http" "net/http"
"sync" "sync"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/Dreamacro/clash/transport/vmess" "github.com/Dreamacro/clash/transport/vmess"
"github.com/Dreamacro/protobytes"
) )
const ( const (
@ -105,15 +106,13 @@ func (t *Trojan) StreamWebsocketConn(conn net.Conn, wsOptions *WebsocketOption)
} }
func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) error { func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) error {
buf := pool.GetBuffer() buf := protobytes.BytesWriter{}
defer pool.PutBuffer(buf) buf.PutSlice(t.hexPassword)
buf.PutSlice(crlf)
buf.Write(t.hexPassword) buf.PutUint8(command)
buf.Write(crlf) buf.PutSlice(socks5Addr)
buf.PutSlice(crlf)
buf.WriteByte(command)
buf.Write(socks5Addr)
buf.Write(crlf)
_, err := w.Write(buf.Bytes()) _, err := w.Write(buf.Bytes())
return err return err
@ -126,14 +125,11 @@ func (t *Trojan) PacketConn(conn net.Conn) net.PacketConn {
} }
func writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) { func writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {
buf := pool.GetBuffer() buf := protobytes.BytesWriter{}
defer pool.PutBuffer(buf) buf.PutSlice(socks5Addr)
buf.PutUint16be(uint16(len(payload)))
buf.Write(socks5Addr) buf.PutSlice(crlf)
binary.Write(buf, binary.BigEndian, uint16(len(payload))) buf.PutSlice(payload)
buf.Write(crlf)
buf.Write(payload)
return w.Write(buf.Bytes()) return w.Write(buf.Bytes())
} }

View File

@ -1,11 +1,13 @@
package obfs package obfs
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
"net" "net"
"net/netip"
"github.com/Dreamacro/protobytes"
) )
type SessionStatus = byte type SessionStatus = byte
@ -33,7 +35,7 @@ type MuxOption struct {
// Mux is an mux-compatible client for v2ray-plugin, not a complete implementation // Mux is an mux-compatible client for v2ray-plugin, not a complete implementation
type Mux struct { type Mux struct {
net.Conn net.Conn
buf bytes.Buffer buf protobytes.BytesWriter
id [2]byte id [2]byte
length [2]byte length [2]byte
status [2]byte status [2]byte
@ -112,11 +114,11 @@ func (m *Mux) Write(b []byte) (int, error) {
m.otb = nil m.otb = nil
} }
m.buf.Reset() m.buf.Reset()
binary.Write(&m.buf, binary.BigEndian, uint16(4)) m.buf.PutUint16be(4)
m.buf.Write(m.id[:]) m.buf.PutSlice(m.id[:])
m.buf.WriteByte(SessionStatusKeep) m.buf.PutUint8(SessionStatusKeep)
m.buf.WriteByte(OptionData) m.buf.PutUint8(OptionData)
binary.Write(&m.buf, binary.BigEndian, uint16(len(b))) m.buf.PutUint16be(uint16(len(b)))
m.buf.Write(b) m.buf.Write(b)
return m.Conn.Write(m.buf.Bytes()) return m.Conn.Write(m.buf.Bytes())
@ -131,35 +133,35 @@ func (m *Mux) Close() error {
} }
func NewMux(conn net.Conn, option MuxOption) *Mux { func NewMux(conn net.Conn, option MuxOption) *Mux {
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
// fill empty length // fill empty length
buf.Write([]byte{0x0, 0x0}) buf.PutSlice([]byte{0x0, 0x0})
buf.Write(option.ID[:]) buf.PutSlice(option.ID[:])
buf.WriteByte(SessionStatusNew) buf.PutUint8(SessionStatusNew)
buf.WriteByte(OptionNone) buf.PutUint8(OptionNone)
// tcp // tcp
netType := byte(0x1) netType := byte(0x1)
if option.Type == "udp" { if option.Type == "udp" {
netType = byte(0x2) netType = byte(0x2)
} }
buf.WriteByte(netType) buf.PutUint8(netType)
// port // port
binary.Write(buf, binary.BigEndian, option.Port) buf.PutUint16be(option.Port)
// address // address
ip := net.ParseIP(option.Host) ip, err := netip.ParseAddr(option.Host)
if ip == nil { if err != nil {
buf.WriteByte(0x2) buf.PutUint8(0x2)
buf.WriteString(option.Host) buf.PutString(option.Host)
} else if ipv4 := ip.To4(); ipv4 != nil { } else if ip.Is4() {
buf.WriteByte(0x1) buf.PutUint8(0x1)
buf.Write(ipv4) buf.PutSlice(ip.AsSlice())
} else { } else {
buf.WriteByte(0x3) buf.PutUint8(0x3)
buf.Write(ip.To16()) buf.PutSlice(ip.AsSlice())
} }
metadata := buf.Bytes() metadata := buf.Bytes()

View File

@ -1,7 +1,6 @@
package vmess package vmess
import ( import (
"bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/hmac" "crypto/hmac"
@ -16,6 +15,7 @@ import (
"net" "net"
"time" "time"
"github.com/Dreamacro/protobytes"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
) )
@ -57,48 +57,46 @@ func (vc *Conn) Read(b []byte) (int, error) {
func (vc *Conn) sendRequest() error { func (vc *Conn) sendRequest() error {
timestamp := time.Now() timestamp := time.Now()
mbuf := &bytes.Buffer{} mbuf := protobytes.BytesWriter{}
if !vc.isAead { if !vc.isAead {
h := hmac.New(md5.New, vc.id.UUID.Bytes()) h := hmac.New(md5.New, vc.id.UUID.Bytes())
binary.Write(h, binary.BigEndian, uint64(timestamp.Unix())) binary.Write(h, binary.BigEndian, uint64(timestamp.Unix()))
mbuf.Write(h.Sum(nil)) mbuf.PutSlice(h.Sum(nil))
} }
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
// Ver IV Key V Opt // Ver IV Key V Opt
buf.WriteByte(Version) buf.PutUint8(Version)
buf.Write(vc.reqBodyIV[:]) buf.PutSlice(vc.reqBodyIV[:])
buf.Write(vc.reqBodyKey[:]) buf.PutSlice(vc.reqBodyKey[:])
buf.WriteByte(vc.respV) buf.PutUint8(vc.respV)
buf.WriteByte(vc.option) buf.PutUint8(vc.option)
p := mathRand.Intn(16) p := mathRand.Intn(16)
// P Sec Reserve Cmd // P Sec Reserve Cmd
buf.WriteByte(byte(p<<4) | vc.security) buf.PutUint8(byte(p<<4) | vc.security)
buf.WriteByte(0) buf.PutUint8(0)
if vc.dst.UDP { if vc.dst.UDP {
buf.WriteByte(CommandUDP) buf.PutUint8(CommandUDP)
} else { } else {
buf.WriteByte(CommandTCP) buf.PutUint8(CommandTCP)
} }
// Port AddrType Addr // Port AddrType Addr
binary.Write(buf, binary.BigEndian, uint16(vc.dst.Port)) buf.PutUint16be(uint16(vc.dst.Port))
buf.WriteByte(vc.dst.AddrType) buf.PutUint8(vc.dst.AddrType)
buf.Write(vc.dst.Addr) buf.PutSlice(vc.dst.Addr)
// padding // padding
if p > 0 { if p > 0 {
padding := make([]byte, p) buf.ReadFull(rand.Reader, p)
rand.Read(padding)
buf.Write(padding)
} }
fnv1a := fnv.New32a() fnv1a := fnv.New32a()
fnv1a.Write(buf.Bytes()) fnv1a.Write(buf.Bytes())
buf.Write(fnv1a.Sum(nil)) buf.PutSlice(fnv1a.Sum(nil))
if !vc.isAead { if !vc.isAead {
block, err := aes.NewCipher(vc.id.CmdKey) block, err := aes.NewCipher(vc.id.CmdKey)
@ -108,7 +106,7 @@ func (vc *Conn) sendRequest() error {
stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp)) stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp))
stream.XORKeyStream(buf.Bytes(), buf.Bytes()) stream.XORKeyStream(buf.Bytes(), buf.Bytes())
mbuf.Write(buf.Bytes()) mbuf.PutSlice(buf.Bytes())
_, err = vc.Conn.Write(mbuf.Bytes()) _, err = vc.Conn.Write(mbuf.Bytes())
return err return err
} }

View File

@ -1,7 +1,6 @@
package vmess package vmess
import ( import (
"bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/hmac" "crypto/hmac"
@ -11,6 +10,8 @@ import (
"hash" "hash"
"hash/crc32" "hash/crc32"
"time" "time"
"github.com/Dreamacro/protobytes"
) )
const ( const (
@ -49,14 +50,11 @@ func (h *hMacCreator) Create() hash.Hash {
} }
func createAuthID(cmdKey []byte, time int64) [16]byte { func createAuthID(cmdKey []byte, time int64) [16]byte {
buf := &bytes.Buffer{} buf := protobytes.BytesWriter{}
binary.Write(buf, binary.BigEndian, time) buf.PutUint64be(uint64(time))
buf.ReadFull(rand.Reader, 4)
random := make([]byte, 4)
rand.Read(random)
buf.Write(random)
zero := crc32.ChecksumIEEE(buf.Bytes()) zero := crc32.ChecksumIEEE(buf.Bytes())
binary.Write(buf, binary.BigEndian, zero) buf.PutUint32be(zero)
aesBlock, _ := aes.NewCipher(kdf(cmdKey[:], kdfSaltConstAuthIDEncryptionKey)[:16]) aesBlock, _ := aes.NewCipher(kdf(cmdKey[:], kdfSaltConstAuthIDEncryptionKey)[:16])
var result [16]byte var result [16]byte
@ -92,12 +90,12 @@ func sealVMessAEADHeader(key [16]byte, data []byte, t time.Time) []byte {
payloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:]) payloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:])
} }
outputBuffer := &bytes.Buffer{} outputBuffer := protobytes.BytesWriter{}
outputBuffer.Write(generatedAuthID[:]) outputBuffer.PutSlice(generatedAuthID[:])
outputBuffer.Write(payloadHeaderLengthAEADEncrypted) outputBuffer.PutSlice(payloadHeaderLengthAEADEncrypted)
outputBuffer.Write(connectionNonce) outputBuffer.PutSlice(connectionNonce)
outputBuffer.Write(payloadHeaderAEADEncrypted) outputBuffer.PutSlice(payloadHeaderAEADEncrypted)
return outputBuffer.Bytes() return outputBuffer.Bytes()
} }