client.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package quic
  2. import (
  3. "context"
  4. "errors"
  5. "net"
  6. tls "github.com/Psiphon-Labs/psiphon-tls"
  7. "github.com/Psiphon-Labs/quic-go/internal/protocol"
  8. "github.com/Psiphon-Labs/quic-go/internal/utils"
  9. "github.com/Psiphon-Labs/quic-go/logging"
  10. )
  11. type client struct {
  12. sendConn sendConn
  13. use0RTT bool
  14. packetHandlers packetHandlerManager
  15. onClose func()
  16. tlsConf *tls.Config
  17. config *Config
  18. connIDGenerator ConnectionIDGenerator
  19. srcConnID protocol.ConnectionID
  20. destConnID protocol.ConnectionID
  21. initialPacketNumber protocol.PacketNumber
  22. hasNegotiatedVersion bool
  23. version protocol.VersionNumber
  24. handshakeChan chan struct{}
  25. conn quicConn
  26. tracer *logging.ConnectionTracer
  27. tracingID uint64
  28. logger utils.Logger
  29. }
  30. // make it possible to mock connection ID for initial generation in the tests
  31. var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial
  32. // DialAddr establishes a new QUIC connection to a server.
  33. // It resolves the address, and then creates a new UDP connection to dial the QUIC server.
  34. // When the QUIC connection is closed, this UDP connection is closed.
  35. // See Dial for more details.
  36. func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (Connection, error) {
  37. udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
  38. if err != nil {
  39. return nil, err
  40. }
  41. udpAddr, err := net.ResolveUDPAddr("udp", addr)
  42. if err != nil {
  43. return nil, err
  44. }
  45. tr, err := setupTransport(udpConn, tlsConf, true)
  46. if err != nil {
  47. return nil, err
  48. }
  49. return tr.dial(ctx, udpAddr, addr, tlsConf, conf, false)
  50. }
  51. // DialAddrEarly establishes a new 0-RTT QUIC connection to a server.
  52. // See DialAddr for more details.
  53. func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) {
  54. udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
  55. if err != nil {
  56. return nil, err
  57. }
  58. udpAddr, err := net.ResolveUDPAddr("udp", addr)
  59. if err != nil {
  60. return nil, err
  61. }
  62. tr, err := setupTransport(udpConn, tlsConf, true)
  63. if err != nil {
  64. return nil, err
  65. }
  66. conn, err := tr.dial(ctx, udpAddr, addr, tlsConf, conf, true)
  67. if err != nil {
  68. tr.Close()
  69. return nil, err
  70. }
  71. return conn, nil
  72. }
  73. // DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn.
  74. // See Dial for more details.
  75. func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) {
  76. dl, err := setupTransport(c, tlsConf, false)
  77. if err != nil {
  78. return nil, err
  79. }
  80. conn, err := dl.DialEarly(ctx, addr, tlsConf, conf)
  81. if err != nil {
  82. dl.Close()
  83. return nil, err
  84. }
  85. return conn, nil
  86. }
  87. // Dial establishes a new QUIC connection to a server using a net.PacketConn.
  88. // If the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn does),
  89. // ECN and packet info support will be enabled. In this case, ReadMsgUDP and WriteMsgUDP
  90. // will be used instead of ReadFrom and WriteTo to read/write packets.
  91. // The tls.Config must define an application protocol (using NextProtos).
  92. //
  93. // This is a convenience function. More advanced use cases should instantiate a Transport,
  94. // which offers configuration options for a more fine-grained control of the connection establishment,
  95. // including reusing the underlying UDP socket for multiple QUIC connections.
  96. func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) {
  97. dl, err := setupTransport(c, tlsConf, false)
  98. if err != nil {
  99. return nil, err
  100. }
  101. conn, err := dl.Dial(ctx, addr, tlsConf, conf)
  102. if err != nil {
  103. dl.Close()
  104. return nil, err
  105. }
  106. return conn, nil
  107. }
  108. func setupTransport(c net.PacketConn, tlsConf *tls.Config, createdPacketConn bool) (*Transport, error) {
  109. if tlsConf == nil {
  110. return nil, errors.New("quic: tls.Config not set")
  111. }
  112. return &Transport{
  113. Conn: c,
  114. createdConn: createdPacketConn,
  115. isSingleUse: true,
  116. }, nil
  117. }
  118. func dial(
  119. ctx context.Context,
  120. conn sendConn,
  121. connIDGenerator ConnectionIDGenerator,
  122. packetHandlers packetHandlerManager,
  123. tlsConf *tls.Config,
  124. config *Config,
  125. onClose func(),
  126. use0RTT bool,
  127. ) (quicConn, error) {
  128. c, err := newClient(conn, connIDGenerator, config, tlsConf, onClose, use0RTT)
  129. if err != nil {
  130. return nil, err
  131. }
  132. c.packetHandlers = packetHandlers
  133. c.tracingID = nextConnTracingID()
  134. if c.config.Tracer != nil {
  135. c.tracer = c.config.Tracer(context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID)
  136. }
  137. if c.tracer != nil && c.tracer.StartedConnection != nil {
  138. c.tracer.StartedConnection(c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID)
  139. }
  140. if err := c.dial(ctx); err != nil {
  141. return nil, err
  142. }
  143. return c.conn, nil
  144. }
  145. func newClient(sendConn sendConn, connIDGenerator ConnectionIDGenerator, config *Config, tlsConf *tls.Config, onClose func(), use0RTT bool) (*client, error) {
  146. srcConnID, err := connIDGenerator.GenerateConnectionID()
  147. if err != nil {
  148. return nil, err
  149. }
  150. destConnID, err := generateConnectionIDForInitial()
  151. if err != nil {
  152. return nil, err
  153. }
  154. c := &client{
  155. connIDGenerator: connIDGenerator,
  156. srcConnID: srcConnID,
  157. destConnID: destConnID,
  158. sendConn: sendConn,
  159. use0RTT: use0RTT,
  160. onClose: onClose,
  161. tlsConf: tlsConf,
  162. config: config,
  163. version: config.Versions[0],
  164. handshakeChan: make(chan struct{}),
  165. logger: utils.DefaultLogger.WithPrefix("client"),
  166. }
  167. return c, nil
  168. }
  169. func (c *client) dial(ctx context.Context) error {
  170. c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
  171. c.conn = newClientConnection(
  172. c.sendConn,
  173. c.packetHandlers,
  174. c.destConnID,
  175. c.srcConnID,
  176. c.connIDGenerator,
  177. c.config,
  178. c.tlsConf,
  179. c.initialPacketNumber,
  180. c.use0RTT,
  181. c.hasNegotiatedVersion,
  182. c.tracer,
  183. c.tracingID,
  184. c.logger,
  185. c.version,
  186. )
  187. c.packetHandlers.Add(c.srcConnID, c.conn)
  188. errorChan := make(chan error, 1)
  189. recreateChan := make(chan errCloseForRecreating)
  190. go func() {
  191. err := c.conn.run()
  192. var recreateErr *errCloseForRecreating
  193. if errors.As(err, &recreateErr) {
  194. recreateChan <- *recreateErr
  195. return
  196. }
  197. if c.onClose != nil {
  198. c.onClose()
  199. }
  200. errorChan <- err // returns as soon as the connection is closed
  201. }()
  202. // only set when we're using 0-RTT
  203. // Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever.
  204. var earlyConnChan <-chan struct{}
  205. if c.use0RTT {
  206. earlyConnChan = c.conn.earlyConnReady()
  207. }
  208. select {
  209. case <-ctx.Done():
  210. c.conn.shutdown()
  211. return context.Cause(ctx)
  212. case err := <-errorChan:
  213. return err
  214. case recreateErr := <-recreateChan:
  215. c.initialPacketNumber = recreateErr.nextPacketNumber
  216. c.version = recreateErr.nextVersion
  217. c.hasNegotiatedVersion = true
  218. return c.dial(ctx)
  219. case <-earlyConnChan:
  220. // ready to send 0-RTT data
  221. return nil
  222. case <-c.conn.HandshakeComplete():
  223. // handshake successfully completed
  224. return nil
  225. }
  226. }