github.com/sagernet/quic-go@v0.43.1-beta.1/sys_conn.go (about)

     1  package quic
     2  
     3  import (
     4  	"net"
     5  	"syscall"
     6  	"time"
     7  
     8  	"github.com/sagernet/quic-go/internal/protocol"
     9  	"github.com/sagernet/quic-go/internal/utils"
    10  )
    11  
    12  // OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header.
    13  // If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it.
    14  // In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets.
    15  type OOBCapablePacketConn interface {
    16  	net.PacketConn
    17  	SyscallConn() (syscall.RawConn, error)
    18  	SetReadBuffer(int) error
    19  	ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
    20  	WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
    21  }
    22  
    23  var _ OOBCapablePacketConn = &net.UDPConn{}
    24  
    25  func wrapConn(pc net.PacketConn) (rawConn, error) {
    26  	_ = setReceiveBuffer(pc)
    27  	_ = setSendBuffer(pc)
    28  
    29  	conn, ok := pc.(interface {
    30  		SyscallConn() (syscall.RawConn, error)
    31  	})
    32  	var supportsDF bool
    33  	if ok {
    34  		rawConn, err := conn.SyscallConn()
    35  		if err != nil {
    36  			return nil, err
    37  		}
    38  
    39  		if _, ok := pc.LocalAddr().(*net.UDPAddr); ok {
    40  			// Only set DF on sockets that we expect to be able to handle that configuration.
    41  			var err error
    42  			supportsDF, err = setDF(rawConn)
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  		}
    47  	}
    48  	c, ok := pc.(OOBCapablePacketConn)
    49  	if !ok {
    50  		utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
    51  		return &basicConn{PacketConn: pc, supportsDF: supportsDF}, nil
    52  	}
    53  	return newConn(c, supportsDF)
    54  }
    55  
    56  // The basicConn is the most trivial implementation of a rawConn.
    57  // It reads a single packet from the underlying net.PacketConn.
    58  // It is used when
    59  // * the net.PacketConn is not a OOBCapablePacketConn, and
    60  // * when the OS doesn't support OOB.
    61  type basicConn struct {
    62  	net.PacketConn
    63  	supportsDF bool
    64  }
    65  
    66  var _ rawConn = &basicConn{}
    67  
    68  func (c *basicConn) ReadPacket() (receivedPacket, error) {
    69  	buffer := GetPacketBuffer()
    70  	// The packet size should not exceed protocol.MaxPacketBufferSize bytes
    71  	// If it does, we only read a truncated packet, which will then end up undecryptable
    72  	buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
    73  	n, addr, err := c.PacketConn.ReadFrom(buffer.Data)
    74  	if err != nil {
    75  		buffer.Release()
    76  		return receivedPacket{}, err
    77  	}
    78  	return receivedPacket{
    79  		remoteAddr: addr,
    80  		rcvTime:    time.Now(),
    81  		data:       buffer.Data[:n],
    82  		buffer:     buffer,
    83  	}, nil
    84  }
    85  
    86  func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte, gsoSize uint16, ecn protocol.ECN) (n int, err error) {
    87  	if gsoSize != 0 {
    88  		panic("cannot use GSO with a basicConn")
    89  	}
    90  	if ecn != protocol.ECNUnsupported {
    91  		panic("cannot use ECN with a basicConn")
    92  	}
    93  	return c.PacketConn.WriteTo(b, addr)
    94  }
    95  
    96  func (c *basicConn) capabilities() connCapabilities { return connCapabilities{DF: c.supportsDF} }