377
transport/tuic/client.go
Normal file
377
transport/tuic/client.go
Normal file
@ -0,0 +1,377 @@
|
||||
package tuic
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
TlsConfig *tls.Config
|
||||
QuicConfig *quic.Config
|
||||
Host string
|
||||
Token [32]byte
|
||||
UdpRelayMode string
|
||||
ReduceRtt bool
|
||||
RequestTimeout int
|
||||
|
||||
quicConn quic.Connection
|
||||
connMutex sync.Mutex
|
||||
|
||||
udpInputMap sync.Map
|
||||
}
|
||||
|
||||
func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (quic.Connection, error) {
|
||||
t.connMutex.Lock()
|
||||
defer t.connMutex.Unlock()
|
||||
if t.quicConn != nil {
|
||||
return t.quicConn, nil
|
||||
}
|
||||
pc, addr, err := dialFn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var quicConn quic.Connection
|
||||
if t.ReduceRtt {
|
||||
quicConn, err = quic.DialEarlyContext(ctx, pc, addr, t.Host, t.TlsConfig, t.QuicConfig)
|
||||
} else {
|
||||
quicConn, err = quic.DialContext(ctx, pc, addr, t.Host, t.TlsConfig, t.QuicConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sendAuthentication := func(quicConn quic.Connection) (err error) {
|
||||
defer func() {
|
||||
t.deferQuicConn(quicConn, err)
|
||||
}()
|
||||
stream, err := quicConn.OpenUniStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
err = NewAuthenticate(t.Token).WriteTo(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = buf.WriteTo(stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stream.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
go sendAuthentication(quicConn)
|
||||
|
||||
go func(quicConn quic.Connection) (err error) {
|
||||
defer func() {
|
||||
t.deferQuicConn(quicConn, err)
|
||||
}()
|
||||
switch t.UdpRelayMode {
|
||||
case "quic":
|
||||
for {
|
||||
var stream quic.ReceiveStream
|
||||
stream, err = quicConn.AcceptUniStream(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() (err error) {
|
||||
var assocId uint32
|
||||
defer func() {
|
||||
t.deferQuicConn(quicConn, err)
|
||||
if err != nil && assocId != 0 {
|
||||
if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {
|
||||
if conn, ok := val.(net.Conn); ok {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
reader := bufio.NewReader(stream)
|
||||
packet, err := ReadPacket(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assocId = packet.ASSOC_ID
|
||||
if val, ok := t.udpInputMap.Load(assocId); ok {
|
||||
if conn, ok := val.(net.Conn); ok {
|
||||
writer := bufio.NewWriterSize(conn, packet.BytesLen())
|
||||
_ = packet.WriteTo(writer)
|
||||
_ = writer.Flush()
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
}
|
||||
default: // native
|
||||
for {
|
||||
var message []byte
|
||||
message, err = quicConn.ReceiveMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() (err error) {
|
||||
var assocId uint32
|
||||
defer func() {
|
||||
t.deferQuicConn(quicConn, err)
|
||||
if err != nil && assocId != 0 {
|
||||
if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {
|
||||
if conn, ok := val.(net.Conn); ok {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
buffer := bytes.NewBuffer(message)
|
||||
packet, err := ReadPacket(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assocId = packet.ASSOC_ID
|
||||
if val, ok := t.udpInputMap.Load(assocId); ok {
|
||||
if conn, ok := val.(net.Conn); ok {
|
||||
_, _ = conn.Write(message)
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
}
|
||||
}
|
||||
}(quicConn)
|
||||
|
||||
t.quicConn = quicConn
|
||||
return quicConn, nil
|
||||
}
|
||||
|
||||
func (t *Client) deferQuicConn(quicConn quic.Connection, err error) {
|
||||
var netError net.Error
|
||||
if err != nil && errors.As(err, &netError) {
|
||||
t.connMutex.Lock()
|
||||
defer t.connMutex.Unlock()
|
||||
if t.quicConn == quicConn {
|
||||
t.udpInputMap.Range(func(key, value any) bool {
|
||||
if conn, ok := value.(net.Conn); ok {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return true
|
||||
})
|
||||
t.udpInputMap = sync.Map{} // new one
|
||||
t.quicConn = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.Conn, error) {
|
||||
quicConn, err := t.getQuicConn(ctx, dialFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
t.deferQuicConn(quicConn, err)
|
||||
}()
|
||||
buf := &bytes.Buffer{}
|
||||
err = NewConnect(NewAddress(metadata)).WriteTo(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stream, err := quicConn.OpenStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = buf.WriteTo(stream)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.RequestTimeout > 0 {
|
||||
_ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond))
|
||||
}
|
||||
conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr()})
|
||||
response, err := ReadResponse(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if response.IsFailed() {
|
||||
_ = stream.Close()
|
||||
return nil, errors.New("connect failed")
|
||||
}
|
||||
_ = stream.SetReadDeadline(time.Time{})
|
||||
return conn, err
|
||||
}
|
||||
|
||||
type quicStreamConn struct {
|
||||
quic.Stream
|
||||
lAddr net.Addr
|
||||
rAddr net.Addr
|
||||
}
|
||||
|
||||
func (q *quicStreamConn) LocalAddr() net.Addr {
|
||||
return q.lAddr
|
||||
}
|
||||
|
||||
func (q *quicStreamConn) RemoteAddr() net.Addr {
|
||||
return q.rAddr
|
||||
}
|
||||
|
||||
var _ net.Conn = &quicStreamConn{}
|
||||
|
||||
func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.PacketConn, error) {
|
||||
quicConn, err := t.getQuicConn(ctx, dialFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pipe1, pipe2 := net.Pipe()
|
||||
inputCh := make(chan udpData)
|
||||
var connId uint32
|
||||
for {
|
||||
connId = rand.Uint32()
|
||||
_, loaded := t.udpInputMap.LoadOrStore(connId, pipe1)
|
||||
if !loaded {
|
||||
break
|
||||
}
|
||||
}
|
||||
pc := &quicStreamPacketConn{
|
||||
connId: connId,
|
||||
quicConn: quicConn,
|
||||
lAddr: quicConn.LocalAddr(),
|
||||
client: t,
|
||||
inputConn: N.NewBufferedConn(pipe2),
|
||||
inputCh: inputCh,
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
type udpData struct {
|
||||
data []byte
|
||||
addr net.Addr
|
||||
err error
|
||||
}
|
||||
|
||||
type quicStreamPacketConn struct {
|
||||
connId uint32
|
||||
quicConn quic.Connection
|
||||
lAddr net.Addr
|
||||
client *Client
|
||||
inputConn *N.BufferedConn
|
||||
inputCh chan udpData
|
||||
|
||||
closeOnce sync.Once
|
||||
closeErr error
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) Close() error {
|
||||
q.closeOnce.Do(func() {
|
||||
q.closeErr = q.close()
|
||||
})
|
||||
return q.closeErr
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) close() (err error) {
|
||||
defer func() {
|
||||
q.client.deferQuicConn(q.quicConn, err)
|
||||
}()
|
||||
buf := &bytes.Buffer{}
|
||||
err = NewDissociate(q.connId).WriteTo(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stream, err := q.quicConn.OpenUniStream()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buf.WriteTo(stream)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = stream.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) SetDeadline(t time.Time) error {
|
||||
//TODO implement me
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error {
|
||||
return q.inputConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
//TODO implement me
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
packet, err := ReadPacket(q.inputConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = copy(p, packet.DATA)
|
||||
addr = packet.ADDR.UDPAddr()
|
||||
return
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
defer func() {
|
||||
q.client.deferQuicConn(q.quicConn, err)
|
||||
}()
|
||||
addr.String()
|
||||
buf := &bytes.Buffer{}
|
||||
addrPort, err := netip.ParseAddrPort(addr.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = NewPacket(q.connId, uint16(len(p)), NewAddressAddrPort(addrPort), p).WriteTo(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch q.client.UdpRelayMode {
|
||||
case "quic":
|
||||
var stream quic.SendStream
|
||||
stream, err = q.quicConn.OpenUniStream()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buf.WriteTo(stream)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = stream.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default: // native
|
||||
err = q.quicConn.SendMessage(buf.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n = len(p)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (q *quicStreamPacketConn) LocalAddr() net.Addr {
|
||||
return q.lAddr
|
||||
}
|
||||
|
||||
var _ net.PacketConn = &quicStreamPacketConn{}
|
468
transport/tuic/protocol.go
Normal file
468
transport/tuic/protocol.go
Normal file
@ -0,0 +1,468 @@
|
||||
package tuic
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"lukechampine.com/blake3"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
type BufferedReader interface {
|
||||
io.Reader
|
||||
io.ByteReader
|
||||
}
|
||||
|
||||
type BufferedWriter interface {
|
||||
io.Writer
|
||||
io.ByteWriter
|
||||
}
|
||||
|
||||
type CommandType byte
|
||||
|
||||
const (
|
||||
AuthenticateType = CommandType(0x00)
|
||||
ConnectType = CommandType(0x01)
|
||||
PacketType = CommandType(0x02)
|
||||
DissociateType = CommandType(0x03)
|
||||
HeartbeatType = CommandType(0x04)
|
||||
ResponseType = CommandType(0x05)
|
||||
)
|
||||
|
||||
func (c CommandType) String() string {
|
||||
switch c {
|
||||
case AuthenticateType:
|
||||
return "Authenticate"
|
||||
case ConnectType:
|
||||
return "Connect"
|
||||
case PacketType:
|
||||
return "Packet"
|
||||
case DissociateType:
|
||||
return "Dissociate"
|
||||
case HeartbeatType:
|
||||
return "Heartbeat"
|
||||
case ResponseType:
|
||||
return "Response"
|
||||
default:
|
||||
return fmt.Sprintf("UnknowCommand: %#x", byte(c))
|
||||
}
|
||||
}
|
||||
|
||||
func (c CommandType) BytesLen() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
type CommandHead struct {
|
||||
VER byte
|
||||
TYPE CommandType
|
||||
}
|
||||
|
||||
func NewCommandHead(TYPE CommandType) CommandHead {
|
||||
return CommandHead{
|
||||
VER: 0x04,
|
||||
TYPE: TYPE,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadCommandHead(reader BufferedReader) (c CommandHead, err error) {
|
||||
c.VER, err = reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
TYPE, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.TYPE = CommandType(TYPE)
|
||||
return
|
||||
}
|
||||
|
||||
func (c CommandHead) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = writer.WriteByte(c.VER)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = writer.WriteByte(byte(c.TYPE))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c CommandHead) BytesLen() int {
|
||||
return 1 + c.TYPE.BytesLen()
|
||||
}
|
||||
|
||||
type Authenticate struct {
|
||||
CommandHead
|
||||
TKN [32]byte
|
||||
}
|
||||
|
||||
func NewAuthenticate(TKN [32]byte) Authenticate {
|
||||
return Authenticate{
|
||||
CommandHead: NewCommandHead(AuthenticateType),
|
||||
TKN: TKN,
|
||||
}
|
||||
}
|
||||
|
||||
func GenTKN(token string) [32]byte {
|
||||
return blake3.Sum256([]byte(token))
|
||||
}
|
||||
|
||||
func (c Authenticate) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = c.CommandHead.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = writer.Write(c.TKN[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Authenticate) BytesLen() int {
|
||||
return c.CommandHead.BytesLen() + 32
|
||||
}
|
||||
|
||||
type Connect struct {
|
||||
CommandHead
|
||||
ADDR Address
|
||||
}
|
||||
|
||||
func NewConnect(ADDR Address) Connect {
|
||||
return Connect{
|
||||
CommandHead: NewCommandHead(ConnectType),
|
||||
ADDR: ADDR,
|
||||
}
|
||||
}
|
||||
|
||||
func (c Connect) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = c.CommandHead.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.ADDR.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Connect) BytesLen() int {
|
||||
return c.CommandHead.BytesLen() + c.ADDR.BytesLen()
|
||||
}
|
||||
|
||||
type Packet struct {
|
||||
CommandHead
|
||||
ASSOC_ID uint32
|
||||
LEN uint16
|
||||
ADDR Address
|
||||
DATA []byte
|
||||
}
|
||||
|
||||
func NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet {
|
||||
return Packet{
|
||||
CommandHead: NewCommandHead(PacketType),
|
||||
ASSOC_ID: ASSOC_ID,
|
||||
LEN: LEN,
|
||||
ADDR: ADDR,
|
||||
DATA: DATA,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadPacket(reader BufferedReader) (c Packet, err error) {
|
||||
c.CommandHead, err = ReadCommandHead(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c.CommandHead.TYPE != PacketType {
|
||||
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Read(reader, binary.BigEndian, &c.LEN)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.ADDR, err = ReadAddress(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.DATA = make([]byte, c.LEN)
|
||||
_, err = io.ReadFull(reader, c.DATA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Packet) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = c.CommandHead.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, c.LEN)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.ADDR.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = writer.Write(c.DATA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Packet) BytesLen() int {
|
||||
return c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA)
|
||||
}
|
||||
|
||||
type Dissociate struct {
|
||||
CommandHead
|
||||
ASSOC_ID uint32
|
||||
}
|
||||
|
||||
func NewDissociate(ASSOC_ID uint32) Dissociate {
|
||||
return Dissociate{
|
||||
CommandHead: NewCommandHead(DissociateType),
|
||||
ASSOC_ID: ASSOC_ID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c Dissociate) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = c.CommandHead.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Dissociate) BytesLen() int {
|
||||
return c.CommandHead.BytesLen() + 4
|
||||
}
|
||||
|
||||
type Heartbeat struct {
|
||||
CommandHead
|
||||
}
|
||||
|
||||
func NewHeartbeat() Heartbeat {
|
||||
return Heartbeat{
|
||||
CommandHead: NewCommandHead(HeartbeatType),
|
||||
}
|
||||
}
|
||||
|
||||
func ReadHeartbeat(reader BufferedReader) (c Response, err error) {
|
||||
c.CommandHead, err = ReadCommandHead(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c.CommandHead.TYPE != HeartbeatType {
|
||||
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
CommandHead
|
||||
REP byte
|
||||
}
|
||||
|
||||
func NewResponse(REP byte) Response {
|
||||
return Response{
|
||||
CommandHead: NewCommandHead(ResponseType),
|
||||
REP: REP,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadResponse(reader BufferedReader) (c Response, err error) {
|
||||
c.CommandHead, err = ReadCommandHead(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c.CommandHead.TYPE != ResponseType {
|
||||
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
|
||||
}
|
||||
c.REP, err = reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Response) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = c.CommandHead.WriteTo(writer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = writer.WriteByte(c.REP)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Response) IsSucceed() bool {
|
||||
return c.REP == 0x00
|
||||
}
|
||||
|
||||
func (c Response) IsFailed() bool {
|
||||
return c.REP == 0xff
|
||||
}
|
||||
|
||||
func (c Response) BytesLen() int {
|
||||
return c.CommandHead.BytesLen() + 1
|
||||
}
|
||||
|
||||
// Addr types
|
||||
const (
|
||||
AtypDomainName byte = 0
|
||||
AtypIPv4 byte = 1
|
||||
AtypIPv6 byte = 2
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
TYPE byte
|
||||
ADDR []byte
|
||||
PORT uint16
|
||||
}
|
||||
|
||||
func NewAddress(metadata *C.Metadata) Address {
|
||||
var addrType byte
|
||||
var addr []byte
|
||||
switch metadata.AddrType() {
|
||||
case socks5.AtypIPv4:
|
||||
addrType = AtypIPv4
|
||||
addr = make([]byte, net.IPv4len)
|
||||
copy(addr[:], metadata.DstIP.AsSlice())
|
||||
case socks5.AtypIPv6:
|
||||
addrType = AtypIPv6
|
||||
addr = make([]byte, net.IPv6len)
|
||||
copy(addr[:], metadata.DstIP.AsSlice())
|
||||
case socks5.AtypDomainName:
|
||||
addrType = AtypDomainName
|
||||
addr = make([]byte, len(metadata.Host)+1)
|
||||
addr[0] = byte(len(metadata.Host))
|
||||
copy(addr[1:], metadata.Host)
|
||||
}
|
||||
|
||||
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
|
||||
|
||||
return Address{
|
||||
TYPE: addrType,
|
||||
ADDR: addr,
|
||||
PORT: uint16(port),
|
||||
}
|
||||
}
|
||||
|
||||
func NewAddressAddrPort(addrPort netip.AddrPort) Address {
|
||||
var addrType byte
|
||||
var addr []byte
|
||||
if addrPort.Addr().Is4() {
|
||||
addrType = AtypIPv4
|
||||
addr = make([]byte, net.IPv4len)
|
||||
} else {
|
||||
addrType = AtypIPv6
|
||||
addr = make([]byte, net.IPv6len)
|
||||
}
|
||||
copy(addr[:], addrPort.Addr().AsSlice())
|
||||
return Address{
|
||||
TYPE: addrType,
|
||||
ADDR: addr,
|
||||
PORT: addrPort.Port(),
|
||||
}
|
||||
}
|
||||
|
||||
func ReadAddress(reader BufferedReader) (c Address, err error) {
|
||||
c.TYPE, err = reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch c.TYPE {
|
||||
case AtypIPv4:
|
||||
c.ADDR = make([]byte, net.IPv4len)
|
||||
_, err = io.ReadFull(reader, c.ADDR)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case AtypIPv6:
|
||||
c.ADDR = make([]byte, net.IPv6len)
|
||||
_, err = io.ReadFull(reader, c.ADDR)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case AtypDomainName:
|
||||
var addrLen byte
|
||||
addrLen, err = reader.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.ADDR = make([]byte, addrLen+1)
|
||||
c.ADDR[0] = addrLen
|
||||
_, err = io.ReadFull(reader, c.ADDR[1:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &c.PORT)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Address) WriteTo(writer BufferedWriter) (err error) {
|
||||
err = writer.WriteByte(c.TYPE)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = writer.Write(c.ADDR[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, c.PORT)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c Address) UDPAddr() *net.UDPAddr {
|
||||
return &net.UDPAddr{
|
||||
IP: c.ADDR,
|
||||
Port: int(c.PORT),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (c Address) BytesLen() int {
|
||||
return 1 + len(c.ADDR) + 2
|
||||
}
|
||||
|
||||
const (
|
||||
ProtocolError = quic.ApplicationErrorCode(0xfffffff0)
|
||||
AuthenticationFailed = quic.ApplicationErrorCode(0xfffffff1)
|
||||
AuthenticationTimeout = quic.ApplicationErrorCode(0xfffffff2)
|
||||
BadCommand = quic.ApplicationErrorCode(0xfffffff3)
|
||||
)
|
Reference in New Issue
Block a user