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 }