github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/send_conn.go (about)

     1  package quic
     2  
     3  import (
     4  	"net"
     5  	"sync/atomic"
     6  
     7  	"github.com/daeuniverse/quic-go/internal/protocol"
     8  	"github.com/daeuniverse/quic-go/internal/utils"
     9  )
    10  
    11  // A sendConn allows sending using a simple Write() on a non-connected packet conn.
    12  type sendConn interface {
    13  	Write(b []byte, gsoSize uint16, ecn protocol.ECN) error
    14  	Close() error
    15  	LocalAddr() net.Addr
    16  	RemoteAddr() net.Addr
    17  	SetRemoteAddr(net.Addr)
    18  
    19  	capabilities() connCapabilities
    20  }
    21  
    22  type sconn struct {
    23  	rawConn
    24  
    25  	localAddr  net.Addr
    26  	remoteAddr atomic.Value
    27  
    28  	logger utils.Logger
    29  
    30  	packetInfoOOB []byte
    31  	// If GSO enabled, and we receive a GSO error for this remote address, GSO is disabled.
    32  	gotGSOError bool
    33  	// Used to catch the error sometimes returned by the first sendmsg call on Linux,
    34  	// see https://github.com/golang/go/issues/63322.
    35  	wroteFirstPacket bool
    36  }
    37  
    38  var _ sendConn = &sconn{}
    39  
    40  func newSendConn(c rawConn, remote net.Addr, info packetInfo, logger utils.Logger) *sconn {
    41  	localAddr := c.LocalAddr()
    42  	if info.addr.IsValid() {
    43  		if udpAddr, ok := localAddr.(*net.UDPAddr); ok {
    44  			addrCopy := *udpAddr
    45  			addrCopy.IP = info.addr.AsSlice()
    46  			localAddr = &addrCopy
    47  		}
    48  	}
    49  
    50  	oob := info.OOB()
    51  	// increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating
    52  	l := len(oob)
    53  	oob = append(oob, make([]byte, 64)...)[:l]
    54  	sc := &sconn{
    55  		rawConn:       c,
    56  		localAddr:     localAddr,
    57  		remoteAddr:    atomic.Value{},
    58  		packetInfoOOB: oob,
    59  		logger:        logger,
    60  	}
    61  	sc.SetRemoteAddr(remote)
    62  	return sc
    63  }
    64  
    65  func (c *sconn) Write(p []byte, gsoSize uint16, ecn protocol.ECN) error {
    66  	remoteAddr := c.remoteAddr.Load().(net.Addr)
    67  	_, err := c.WritePacket(p, remoteAddr, c.packetInfoOOB, gsoSize, ecn)
    68  	if err != nil && isGSOError(err) {
    69  		// disable GSO for future calls
    70  		c.gotGSOError = true
    71  		if c.logger.Debug() {
    72  			c.logger.Debugf("GSO failed when sending to %s", remoteAddr)
    73  		}
    74  		// send out the packets one by one
    75  		for len(p) > 0 {
    76  			l := len(p)
    77  			if l > int(gsoSize) {
    78  				l = int(gsoSize)
    79  			}
    80  			if _, err := c.WritePacket(p[:l], remoteAddr, c.packetInfoOOB, 0, ecn); err != nil {
    81  				return err
    82  			}
    83  			p = p[l:]
    84  		}
    85  		return nil
    86  	}
    87  	return err
    88  }
    89  
    90  func (c *sconn) writePacket(p []byte, addr net.Addr, oob []byte, gsoSize uint16, ecn protocol.ECN) error {
    91  	_, err := c.WritePacket(p, addr, oob, gsoSize, ecn)
    92  	if err != nil && !c.wroteFirstPacket && isPermissionError(err) {
    93  		_, err = c.WritePacket(p, addr, oob, gsoSize, ecn)
    94  	}
    95  	c.wroteFirstPacket = true
    96  	return err
    97  }
    98  
    99  func (c *sconn) capabilities() connCapabilities {
   100  	capabilities := c.rawConn.capabilities()
   101  	if capabilities.GSO {
   102  		capabilities.GSO = !c.gotGSOError
   103  	}
   104  	return capabilities
   105  }
   106  
   107  func (c *sconn) RemoteAddr() net.Addr { return c.remoteAddr.Load().(net.Addr) }
   108  func (c *sconn) LocalAddr() net.Addr  { return c.localAddr }
   109  
   110  func (c *sconn) SetRemoteAddr(addr net.Addr) {
   111  	if addr == nil {
   112  		return
   113  	}
   114  	c.remoteAddr.Store(addr)
   115  }