github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/send_conn.go (about) 1 package quic 2 3 import ( 4 "net" 5 "sync/atomic" 6 7 "github.com/metacubex/quic-go/internal/protocol" 8 "github.com/metacubex/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 }