diff --git a/common/uot/client.go b/common/uot/client.go deleted file mode 100644 index d1c1f6c..0000000 --- a/common/uot/client.go +++ /dev/null @@ -1,115 +0,0 @@ -package uot - -import ( - "bufio" - "encoding/binary" - "io" - "net" - "sync" - - "github.com/sagernet/sing/common/buf" - M "github.com/sagernet/sing/common/metadata" -) - -type ClientConn struct { - net.Conn - reader *bufio.Reader - writer *bufio.Writer - readAccess sync.Mutex - writeAccess sync.Mutex -} - -func NewClientConn(conn net.Conn) *ClientConn { - return &ClientConn{ - Conn: conn, - reader: bufio.NewReader(conn), - writer: bufio.NewWriter(conn), - } -} - -func (c *ClientConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) { - c.readAccess.Lock() - defer c.readAccess.Unlock() - - destination, err := AddrParser.ReadAddrPort(c.reader) - if err != nil { - return M.Socksaddr{}, err - } - var length uint16 - err = binary.Read(c.reader, binary.BigEndian, &length) - if err != nil { - return M.Socksaddr{}, err - } - _, err = buffer.ReadFullFrom(c.reader, int(length)) - if err != nil { - return M.Socksaddr{}, err - } - return destination, nil -} - -func (c *ClientConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { - c.writeAccess.Lock() - defer c.writeAccess.Unlock() - - defer buffer.Release() - err := AddrParser.WriteAddrPort(c.writer, destination) - if err != nil { - return err - } - err = binary.Write(c.writer, binary.BigEndian, uint16(buffer.Len())) - if err != nil { - return err - } - _, err = c.writer.Write(buffer.Bytes()) - if err != nil { - return err - } - return c.writer.Flush() -} - -func (c *ClientConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - c.readAccess.Lock() - defer c.readAccess.Unlock() - - addrPort, err := AddrParser.ReadAddrPort(c.reader) - if err != nil { - return 0, nil, err - } - var length uint16 - err = binary.Read(c.reader, binary.BigEndian, &length) - if err != nil { - return 0, nil, err - } - if len(p) < int(length) { - return 0, nil, io.ErrShortBuffer - } - n, err = io.ReadFull(c.reader, p[:length]) - if err != nil { - return 0, nil, err - } - addr = addrPort.UDPAddr() - return -} - -func (c *ClientConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - c.writeAccess.Lock() - defer c.writeAccess.Unlock() - - err = AddrParser.WriteAddrPort(c.writer, M.SocksaddrFromNet(addr)) - if err != nil { - return - } - err = binary.Write(c.writer, binary.BigEndian, uint16(len(p))) - if err != nil { - return - } - _, err = c.writer.Write(p) - if err != nil { - return - } - err = c.writer.Flush() - if err != nil { - return - } - return len(p), nil -} diff --git a/common/uot/conn.go b/common/uot/conn.go new file mode 100644 index 0000000..f95057c --- /dev/null +++ b/common/uot/conn.go @@ -0,0 +1,144 @@ +package uot + +import ( + "encoding/binary" + "io" + "net" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +type Conn struct { + net.Conn + isConnect bool + destination M.Socksaddr + writer N.VectorisedWriter +} + +func NewConn(conn net.Conn, isConnect bool, destination M.Socksaddr) *Conn { + uConn := &Conn{ + Conn: conn, + isConnect: isConnect, + destination: destination, + } + uConn.writer, _ = bufio.CreateVectorisedWriter(conn) + return uConn +} + +func (c *Conn) Read(p []byte) (n int, err error) { + n, _, err = c.ReadFrom(p) + return +} + +func (c *Conn) Write(p []byte) (n int, err error) { + return c.WriteTo(p, c.destination) +} + +func (c *Conn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + var destination M.Socksaddr + if c.isConnect { + destination = c.destination + } else { + destination, err = AddrParser.ReadAddrPort(c.Conn) + if err != nil { + return + } + } + var length uint16 + err = binary.Read(c.Conn, binary.BigEndian, &length) + if err != nil { + return + } + if len(p) < int(length) { + err = E.Cause(io.ErrShortBuffer, "UoT read") + return + } + n, err = c.Conn.Read(p[:length]) + if err == nil { + addr = destination.UDPAddr() + } + return +} + +func (c *Conn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + destination := M.SocksaddrFromNet(addr) + var bufferLen int + if !c.isConnect { + bufferLen += AddrParser.AddrPortLen(destination) + } + bufferLen += 2 + if c.writer == nil { + bufferLen += len(p) + } + _buffer := buf.StackNewSize(bufferLen) + defer common.KeepAlive(_buffer) + buffer := common.Dup(_buffer) + defer buffer.Release() + if !c.isConnect { + common.Must(AddrParser.WriteAddrPort(buffer, destination)) + } + common.Must(binary.Write(buffer, binary.BigEndian, uint16(len(p)))) + if c.writer == nil { + common.Must1(buffer.Write(p)) + return c.Conn.Write(buffer.Bytes()) + } + err = c.writer.WriteVectorised([]*buf.Buffer{buffer, buf.As(p)}) + if err == nil { + n = len(p) + } + return +} + +func (c *Conn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) { + if c.isConnect { + destination = c.destination + } else { + destination, err = AddrParser.ReadAddrPort(c.Conn) + if err != nil { + return + } + } + var length uint16 + err = binary.Read(c.Conn, binary.BigEndian, &length) + if err != nil { + return + } + _, err = buffer.ReadFullFrom(c.Conn, int(length)) + if err != nil { + return M.Socksaddr{}, E.Cause(err, "UoT read") + } + return +} + +func (c *Conn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { + var headerLen int + if !c.isConnect { + headerLen += AddrParser.AddrPortLen(destination) + } + headerLen += 2 + if c.writer == nil { + headerLen += buffer.Len() + } + _header := buf.StackNewSize(headerLen) + defer common.KeepAlive(_header) + header := common.Dup(_header) + defer header.Release() + if !c.isConnect { + common.Must(AddrParser.WriteAddrPort(header, destination)) + } + common.Must(binary.Write(header, binary.BigEndian, uint16(buffer.Len()))) + if c.writer == nil { + common.Must1(header.Write(buffer.Bytes())) + return common.Error(c.Conn.Write(header.Bytes())) + } + return c.writer.WriteVectorised([]*buf.Buffer{header, buffer}) +} + +func (c *Conn) Upstream() any { + return c.Conn +} diff --git a/common/uot/protocol.go b/common/uot/protocol.go new file mode 100644 index 0000000..d3d379a --- /dev/null +++ b/common/uot/protocol.go @@ -0,0 +1,66 @@ +package uot + +import ( + "encoding/binary" + "io" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" +) + +const ( + Version = 2 + MagicAddress = "sp.v2.udp-over-tcp.arpa" + LegacyMagicAddress = "sp.udp-over-tcp.arpa" +) + +var AddrParser = M.NewSerializer( + M.AddressFamilyByte(0x00, M.AddressFamilyIPv4), + M.AddressFamilyByte(0x01, M.AddressFamilyIPv6), + M.AddressFamilyByte(0x02, M.AddressFamilyFqdn), +) + +type Request struct { + IsConnect bool + Destination M.Socksaddr +} + +func ReadRequest(reader io.Reader) (*Request, error) { + var version uint8 + err := binary.Read(reader, binary.BigEndian, &version) + if err != nil { + return nil, err + } + if version != Version { + return nil, E.New("unsupported version: ", version) + } + var request Request + err = binary.Read(reader, binary.BigEndian, &request.IsConnect) + if err != nil { + return nil, err + } + request.Destination, err = M.SocksaddrSerializer.ReadAddrPort(reader) + if err != nil { + return nil, err + } + return &request, nil +} + +func WriteRequest(writer io.Writer, request Request) error { + var requestLen int + requestLen += 1 // version + requestLen += 1 // isConnect + requestLen += M.SocksaddrSerializer.AddrPortLen(request.Destination) + _buffer := buf.StackNewSize(requestLen) + defer common.KeepAlive(_buffer) + buffer := common.Dup(_buffer) + defer buffer.Release() + common.Must( + binary.Write(buffer, binary.BigEndian, uint8(Version)), + binary.Write(buffer, binary.BigEndian, request.IsConnect), + M.SocksaddrSerializer.WriteAddrPort(buffer, request.Destination), + ) + return common.Error(writer.Write(buffer.Bytes())) +} diff --git a/common/uot/uot.go b/common/uot/uot.go deleted file mode 100644 index dc98fda..0000000 --- a/common/uot/uot.go +++ /dev/null @@ -1,13 +0,0 @@ -package uot - -import ( - M "github.com/sagernet/sing/common/metadata" -) - -const UOTMagicAddress = "sp.udp-over-tcp.arpa" - -var AddrParser = M.NewSerializer( - M.AddressFamilyByte(0x00, M.AddressFamilyIPv4), - M.AddressFamilyByte(0x01, M.AddressFamilyIPv6), - M.AddressFamilyByte(0x02, M.AddressFamilyFqdn), -)