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