sys_conn.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package quic
  2. import (
  3. "net"
  4. "os"
  5. "strconv"
  6. "strings"
  7. "syscall"
  8. "time"
  9. "github.com/Psiphon-Labs/quic-go/internal/protocol"
  10. "github.com/Psiphon-Labs/quic-go/internal/utils"
  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. var _ OOBCapablePacketConn = &net.UDPConn{}
  23. // [Psiphon]
  24. var logger = utils.DefaultLogger
  25. func wrapConn(pc net.PacketConn) (rawConn, error) {
  26. if err := setReceiveBuffer(pc); err != nil {
  27. if !strings.Contains(err.Error(), "use of closed network connection") {
  28. setBufferWarningOnce.Do(func() {
  29. if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable {
  30. return
  31. }
  32. // [Psiphon]
  33. // Do not emit alert to stderr (was log.Printf).
  34. logger.Errorf("%s. See https://github.com/Psiphon-Labs/quic-go/wiki/UDP-Buffer-Sizes for details.", err)
  35. })
  36. }
  37. }
  38. if err := setSendBuffer(pc); err != nil {
  39. if !strings.Contains(err.Error(), "use of closed network connection") {
  40. setBufferWarningOnce.Do(func() {
  41. if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable {
  42. return
  43. }
  44. // [Psiphon]
  45. // Do not emit alert to stderr (was log.Printf).
  46. logger.Errorf("%s. See https://github.com/Psiphon-Labs/quic-go/wiki/UDP-Buffer-Sizes for details.", err)
  47. })
  48. }
  49. }
  50. conn, ok := pc.(interface {
  51. SyscallConn() (syscall.RawConn, error)
  52. })
  53. var supportsDF bool
  54. if ok {
  55. rawConn, err := conn.SyscallConn()
  56. if err != nil {
  57. return nil, err
  58. }
  59. if _, ok := pc.LocalAddr().(*net.UDPAddr); ok {
  60. // Only set DF on sockets that we expect to be able to handle that configuration.
  61. var err error
  62. supportsDF, err = setDF(rawConn)
  63. if err != nil {
  64. return nil, err
  65. }
  66. }
  67. }
  68. c, ok := pc.(OOBCapablePacketConn)
  69. if !ok {
  70. utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.")
  71. return &basicConn{PacketConn: pc, supportsDF: supportsDF}, nil
  72. }
  73. return newConn(c, supportsDF)
  74. }
  75. // The basicConn is the most trivial implementation of a rawConn.
  76. // It reads a single packet from the underlying net.PacketConn.
  77. // It is used when
  78. // * the net.PacketConn is not a OOBCapablePacketConn, and
  79. // * when the OS doesn't support OOB.
  80. type basicConn struct {
  81. net.PacketConn
  82. supportsDF bool
  83. }
  84. var _ rawConn = &basicConn{}
  85. func (c *basicConn) ReadPacket() (receivedPacket, error) {
  86. buffer := getPacketBuffer()
  87. // The packet size should not exceed protocol.MaxPacketBufferSize bytes
  88. // If it does, we only read a truncated packet, which will then end up undecryptable
  89. buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
  90. n, addr, err := c.PacketConn.ReadFrom(buffer.Data)
  91. if err != nil {
  92. return receivedPacket{}, err
  93. }
  94. return receivedPacket{
  95. remoteAddr: addr,
  96. rcvTime: time.Now(),
  97. data: buffer.Data[:n],
  98. buffer: buffer,
  99. }, nil
  100. }
  101. func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte, gsoSize uint16, ecn protocol.ECN) (n int, err error) {
  102. if gsoSize != 0 {
  103. panic("cannot use GSO with a basicConn")
  104. }
  105. if ecn != protocol.ECNUnsupported {
  106. panic("cannot use ECN with a basicConn")
  107. }
  108. return c.PacketConn.WriteTo(b, addr)
  109. }
  110. func (c *basicConn) capabilities() connCapabilities { return connCapabilities{DF: c.supportsDF} }