github.com/MerlinKodo/quic-go@v0.39.2/send_conn.go (about)

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