github.com/chwjbn/xclash@v0.2.0/transport/v2ray-plugin/mux.go (about)

     1  package obfs
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"io"
     8  	"net"
     9  )
    10  
    11  type SessionStatus = byte
    12  
    13  const (
    14  	SessionStatusNew       SessionStatus = 0x01
    15  	SessionStatusKeep      SessionStatus = 0x02
    16  	SessionStatusEnd       SessionStatus = 0x03
    17  	SessionStatusKeepAlive SessionStatus = 0x04
    18  )
    19  
    20  const (
    21  	OptionNone  = byte(0x00)
    22  	OptionData  = byte(0x01)
    23  	OptionError = byte(0x02)
    24  )
    25  
    26  type MuxOption struct {
    27  	ID   [2]byte
    28  	Port uint16
    29  	Host string
    30  	Type string
    31  }
    32  
    33  // Mux is an mux-compatible client for v2ray-plugin, not a complete implementation
    34  type Mux struct {
    35  	net.Conn
    36  	buf    bytes.Buffer
    37  	id     [2]byte
    38  	length [2]byte
    39  	status [2]byte
    40  	otb    []byte
    41  	remain int
    42  }
    43  
    44  func (m *Mux) Read(b []byte) (int, error) {
    45  	if m.remain != 0 {
    46  		length := m.remain
    47  		if len(b) < m.remain {
    48  			length = len(b)
    49  		}
    50  
    51  		n, err := m.Conn.Read(b[:length])
    52  		if err != nil {
    53  			return 0, err
    54  		}
    55  		m.remain -= n
    56  		return n, nil
    57  	}
    58  
    59  	for {
    60  		_, err := io.ReadFull(m.Conn, m.length[:])
    61  		if err != nil {
    62  			return 0, err
    63  		}
    64  		length := binary.BigEndian.Uint16(m.length[:])
    65  		if length > 512 {
    66  			return 0, errors.New("invalid metalen")
    67  		}
    68  
    69  		_, err = io.ReadFull(m.Conn, m.id[:])
    70  		if err != nil {
    71  			return 0, err
    72  		}
    73  
    74  		_, err = m.Conn.Read(m.status[:])
    75  		if err != nil {
    76  			return 0, err
    77  		}
    78  
    79  		opcode := m.status[0]
    80  		if opcode == SessionStatusKeepAlive {
    81  			continue
    82  		}
    83  
    84  		opts := m.status[1]
    85  
    86  		if opts != OptionData {
    87  			continue
    88  		}
    89  
    90  		_, err = io.ReadFull(m.Conn, m.length[:])
    91  		if err != nil {
    92  			return 0, err
    93  		}
    94  		dataLen := int(binary.BigEndian.Uint16(m.length[:]))
    95  		m.remain = dataLen
    96  		if dataLen > len(b) {
    97  			dataLen = len(b)
    98  		}
    99  
   100  		n, err := m.Conn.Read(b[:dataLen])
   101  		m.remain -= n
   102  		return n, err
   103  	}
   104  }
   105  
   106  func (m *Mux) Write(b []byte) (int, error) {
   107  	if m.otb != nil {
   108  		// create a sub connection
   109  		if _, err := m.Conn.Write(m.otb); err != nil {
   110  			return 0, err
   111  		}
   112  		m.otb = nil
   113  	}
   114  	m.buf.Reset()
   115  	binary.Write(&m.buf, binary.BigEndian, uint16(4))
   116  	m.buf.Write(m.id[:])
   117  	m.buf.WriteByte(SessionStatusKeep)
   118  	m.buf.WriteByte(OptionData)
   119  	binary.Write(&m.buf, binary.BigEndian, uint16(len(b)))
   120  	m.buf.Write(b)
   121  
   122  	return m.Conn.Write(m.buf.Bytes())
   123  }
   124  
   125  func (m *Mux) Close() error {
   126  	_, err := m.Conn.Write([]byte{0x0, 0x4, m.id[0], m.id[1], SessionStatusEnd, OptionNone})
   127  	if err != nil {
   128  		return err
   129  	}
   130  	return m.Conn.Close()
   131  }
   132  
   133  func NewMux(conn net.Conn, option MuxOption) *Mux {
   134  	buf := &bytes.Buffer{}
   135  
   136  	// fill empty length
   137  	buf.Write([]byte{0x0, 0x0})
   138  	buf.Write(option.ID[:])
   139  	buf.WriteByte(SessionStatusNew)
   140  	buf.WriteByte(OptionNone)
   141  
   142  	// tcp
   143  	netType := byte(0x1)
   144  	if option.Type == "udp" {
   145  		netType = byte(0x2)
   146  	}
   147  	buf.WriteByte(netType)
   148  
   149  	// port
   150  	binary.Write(buf, binary.BigEndian, option.Port)
   151  
   152  	// address
   153  	ip := net.ParseIP(option.Host)
   154  	if ip == nil {
   155  		buf.WriteByte(0x2)
   156  		buf.WriteString(option.Host)
   157  	} else if ipv4 := ip.To4(); ipv4 != nil {
   158  		buf.WriteByte(0x1)
   159  		buf.Write(ipv4)
   160  	} else {
   161  		buf.WriteByte(0x3)
   162  		buf.Write(ip.To16())
   163  	}
   164  
   165  	metadata := buf.Bytes()
   166  	binary.BigEndian.PutUint16(metadata[:2], uint16(len(metadata)-2))
   167  
   168  	return &Mux{
   169  		Conn: conn,
   170  		id:   option.ID,
   171  		otb:  metadata,
   172  	}
   173  }