2017-12-12 02:51:45 +00:00
package quic
import (
"bytes"
2018-01-03 19:19:49 +00:00
"crypto/tls"
2017-12-12 02:51:45 +00:00
"errors"
"net"
"sync"
"time"
2018-01-03 19:19:49 +00:00
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/handshake"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
2017-12-12 02:51:45 +00:00
"github.com/lucas-clemente/quic-go/qerr"
)
// packetHandler handles packets
type packetHandler interface {
Session
2018-01-20 18:07:01 +00:00
getCryptoStream ( ) cryptoStreamI
handshakeStatus ( ) <- chan error
2017-12-12 02:51:45 +00:00
handlePacket ( * receivedPacket )
2018-01-03 19:19:49 +00:00
GetVersion ( ) protocol . VersionNumber
run ( ) error
closeRemote ( error )
2017-12-12 02:51:45 +00:00
}
// A Listener of QUIC
type server struct {
2018-01-03 19:19:49 +00:00
tlsConf * tls . Config
config * Config
2017-12-12 02:51:45 +00:00
conn net . PacketConn
2018-01-03 19:19:49 +00:00
supportsTLS bool
serverTLS * serverTLS
2017-12-12 02:51:45 +00:00
certChain crypto . CertChain
scfg * handshake . ServerConfig
2018-01-20 18:07:01 +00:00
sessionsMutex sync . RWMutex
sessions map [ protocol . ConnectionID ] packetHandler
closed bool
2017-12-12 02:51:45 +00:00
2018-01-03 19:19:49 +00:00
serverError error
sessionQueue chan Session
errorChan chan struct { }
2018-01-20 18:07:01 +00:00
// set as members, so they can be set in the tests
newSession func ( conn connection , v protocol . VersionNumber , connectionID protocol . ConnectionID , sCfg * handshake . ServerConfig , tlsConf * tls . Config , config * Config ) ( packetHandler , error )
deleteClosedSessionsAfter time . Duration
2017-12-12 02:51:45 +00:00
}
var _ Listener = & server { }
// ListenAddr creates a QUIC server listening on a given address.
// The listener is not active until Serve() is called.
2018-01-03 19:19:49 +00:00
// The tls.Config must not be nil, the quic.Config may be nil.
func ListenAddr ( addr string , tlsConf * tls . Config , config * Config ) ( Listener , error ) {
2017-12-12 02:51:45 +00:00
udpAddr , err := net . ResolveUDPAddr ( "udp" , addr )
if err != nil {
return nil , err
}
conn , err := net . ListenUDP ( "udp" , udpAddr )
if err != nil {
return nil , err
}
2018-01-03 19:19:49 +00:00
return Listen ( conn , tlsConf , config )
2017-12-12 02:51:45 +00:00
}
// Listen listens for QUIC connections on a given net.PacketConn.
// The listener is not active until Serve() is called.
2018-01-03 19:19:49 +00:00
// The tls.Config must not be nil, the quic.Config may be nil.
func Listen ( conn net . PacketConn , tlsConf * tls . Config , config * Config ) ( Listener , error ) {
certChain := crypto . NewCertChain ( tlsConf )
2017-12-12 02:51:45 +00:00
kex , err := crypto . NewCurve25519KEX ( )
if err != nil {
return nil , err
}
scfg , err := handshake . NewServerConfig ( kex , certChain )
if err != nil {
return nil , err
}
2018-01-03 19:19:49 +00:00
config = populateServerConfig ( config )
// check if any of the supported versions supports TLS
var supportsTLS bool
for _ , v := range config . Versions {
if v . UsesTLS ( ) {
supportsTLS = true
break
}
}
2017-12-12 02:51:45 +00:00
2018-01-03 19:19:49 +00:00
s := & server {
2017-12-12 02:51:45 +00:00
conn : conn ,
2018-01-03 19:19:49 +00:00
tlsConf : tlsConf ,
2017-12-12 02:51:45 +00:00
config : config ,
certChain : certChain ,
scfg : scfg ,
sessions : map [ protocol . ConnectionID ] packetHandler { } ,
newSession : newSession ,
deleteClosedSessionsAfter : protocol . ClosedSessionDeleteTimeout ,
2018-01-03 19:19:49 +00:00
sessionQueue : make ( chan Session , 5 ) ,
errorChan : make ( chan struct { } ) ,
supportsTLS : supportsTLS ,
}
if supportsTLS {
if err := s . setupTLS ( ) ; err != nil {
return nil , err
}
}
go s . serve ( )
utils . Debugf ( "Listening for %s connections on %s" , conn . LocalAddr ( ) . Network ( ) , conn . LocalAddr ( ) . String ( ) )
return s , nil
}
func ( s * server ) setupTLS ( ) error {
cookieHandler , err := handshake . NewCookieHandler ( s . config . AcceptCookie )
if err != nil {
return err
}
serverTLS , sessionChan , err := newServerTLS ( s . conn , s . config , cookieHandler , s . tlsConf )
if err != nil {
return err
}
s . serverTLS = serverTLS
// handle TLS connection establishment statelessly
go func ( ) {
for {
select {
case <- s . errorChan :
return
case sess := <- sessionChan :
// TODO: think about what to do with connection ID collisions
connID := sess . ( * session ) . connectionID
s . sessionsMutex . Lock ( )
s . sessions [ connID ] = sess
s . sessionsMutex . Unlock ( )
s . runHandshakeAndSession ( sess , connID )
}
}
} ( )
return nil
}
var defaultAcceptCookie = func ( clientAddr net . Addr , cookie * Cookie ) bool {
if cookie == nil {
return false
}
if time . Now ( ) . After ( cookie . SentTime . Add ( protocol . CookieExpiryTime ) ) {
return false
}
var sourceAddr string
if udpAddr , ok := clientAddr . ( * net . UDPAddr ) ; ok {
sourceAddr = udpAddr . IP . String ( )
} else {
sourceAddr = clientAddr . String ( )
}
return sourceAddr == cookie . RemoteAddr
}
// populateServerConfig populates fields in the quic.Config with their default values, if none are set
// it may be called with nil
func populateServerConfig ( config * Config ) * Config {
if config == nil {
config = & Config { }
}
versions := config . Versions
if len ( versions ) == 0 {
versions = protocol . SupportedVersions
}
vsa := defaultAcceptCookie
if config . AcceptCookie != nil {
vsa = config . AcceptCookie
}
handshakeTimeout := protocol . DefaultHandshakeTimeout
if config . HandshakeTimeout != 0 {
handshakeTimeout = config . HandshakeTimeout
}
idleTimeout := protocol . DefaultIdleTimeout
if config . IdleTimeout != 0 {
idleTimeout = config . IdleTimeout
}
maxReceiveStreamFlowControlWindow := config . MaxReceiveStreamFlowControlWindow
if maxReceiveStreamFlowControlWindow == 0 {
maxReceiveStreamFlowControlWindow = protocol . DefaultMaxReceiveStreamFlowControlWindowServer
}
maxReceiveConnectionFlowControlWindow := config . MaxReceiveConnectionFlowControlWindow
if maxReceiveConnectionFlowControlWindow == 0 {
maxReceiveConnectionFlowControlWindow = protocol . DefaultMaxReceiveConnectionFlowControlWindowServer
}
return & Config {
Versions : versions ,
HandshakeTimeout : handshakeTimeout ,
IdleTimeout : idleTimeout ,
AcceptCookie : vsa ,
KeepAlive : config . KeepAlive ,
MaxReceiveStreamFlowControlWindow : maxReceiveStreamFlowControlWindow ,
MaxReceiveConnectionFlowControlWindow : maxReceiveConnectionFlowControlWindow ,
}
2017-12-12 02:51:45 +00:00
}
2018-01-03 19:19:49 +00:00
// serve listens on an existing PacketConn
func ( s * server ) serve ( ) {
2017-12-12 02:51:45 +00:00
for {
data := getPacketBuffer ( )
data = data [ : protocol . MaxReceivePacketSize ]
// The packet size should not exceed protocol.MaxReceivePacketSize bytes
// If it does, we only read a truncated packet, which will then end up undecryptable
n , remoteAddr , err := s . conn . ReadFrom ( data )
if err != nil {
2018-01-03 19:19:49 +00:00
s . serverError = err
close ( s . errorChan )
_ = s . Close ( )
return
2017-12-12 02:51:45 +00:00
}
data = data [ : n ]
if err := s . handlePacket ( s . conn , remoteAddr , data ) ; err != nil {
utils . Errorf ( "error handling packet: %s" , err . Error ( ) )
}
}
}
2018-01-03 19:19:49 +00:00
// Accept returns newly openend sessions
func ( s * server ) Accept ( ) ( Session , error ) {
var sess Session
select {
case sess = <- s . sessionQueue :
return sess , nil
case <- s . errorChan :
return nil , s . serverError
}
}
2017-12-12 02:51:45 +00:00
// Close the server
func ( s * server ) Close ( ) error {
s . sessionsMutex . Lock ( )
2018-01-20 18:07:01 +00:00
if s . closed {
s . sessionsMutex . Unlock ( )
return nil
}
s . closed = true
2018-01-03 19:19:49 +00:00
var wg sync . WaitGroup
2017-12-12 02:51:45 +00:00
for _ , session := range s . sessions {
if session != nil {
2018-01-03 19:19:49 +00:00
wg . Add ( 1 )
go func ( sess packetHandler ) {
// session.Close() blocks until the CONNECTION_CLOSE has been sent and the run-loop has stopped
_ = sess . Close ( nil )
wg . Done ( )
} ( session )
2017-12-12 02:51:45 +00:00
}
}
s . sessionsMutex . Unlock ( )
2018-01-03 19:19:49 +00:00
wg . Wait ( )
2017-12-12 02:51:45 +00:00
2018-01-20 18:07:01 +00:00
err := s . conn . Close ( )
<- s . errorChan // wait for serve() to return
return err
2017-12-12 02:51:45 +00:00
}
// Addr returns the server's network address
func ( s * server ) Addr ( ) net . Addr {
return s . conn . LocalAddr ( )
}
func ( s * server ) handlePacket ( pconn net . PacketConn , remoteAddr net . Addr , packet [ ] byte ) error {
rcvTime := time . Now ( )
r := bytes . NewReader ( packet )
2018-01-03 19:19:49 +00:00
hdr , err := wire . ParseHeaderSentByClient ( r )
2017-12-12 02:51:45 +00:00
if err != nil {
return qerr . Error ( qerr . InvalidPacketHeader , err . Error ( ) )
}
hdr . Raw = packet [ : len ( packet ) - r . Len ( ) ]
2018-01-03 19:19:49 +00:00
packetData := packet [ len ( packet ) - r . Len ( ) : ]
connID := hdr . ConnectionID
if hdr . Type == protocol . PacketTypeInitial {
if s . supportsTLS {
go s . serverTLS . HandleInitial ( remoteAddr , hdr , packetData )
}
return nil
}
2017-12-12 02:51:45 +00:00
s . sessionsMutex . RLock ( )
2018-01-03 19:19:49 +00:00
session , sessionKnown := s . sessions [ connID ]
2017-12-12 02:51:45 +00:00
s . sessionsMutex . RUnlock ( )
2018-01-03 19:19:49 +00:00
if sessionKnown && session == nil {
// Late packet for closed session
return nil
}
2017-12-12 02:51:45 +00:00
// ignore all Public Reset packets
if hdr . ResetFlag {
2018-01-03 19:19:49 +00:00
if sessionKnown {
var pr * wire . PublicReset
pr , err = wire . ParsePublicReset ( r )
2017-12-12 02:51:45 +00:00
if err != nil {
utils . Infof ( "Received a Public Reset for connection %x. An error occurred parsing the packet." )
} else {
2018-01-03 19:19:49 +00:00
utils . Infof ( "Received a Public Reset for connection %x, rejected packet number: 0x%x." , hdr . ConnectionID , pr . RejectedPacketNumber )
2017-12-12 02:51:45 +00:00
}
} else {
utils . Infof ( "Received Public Reset for unknown connection %x." , hdr . ConnectionID )
}
return nil
}
2018-01-03 19:19:49 +00:00
// If we don't have a session for this connection, and this packet cannot open a new connection, send a Public Reset
// This should only happen after a server restart, when we still receive packets for connections that we lost the state for.
// TODO(#943): implement sending of IETF draft style stateless resets
if ! sessionKnown && ( ! hdr . VersionFlag && hdr . Type != protocol . PacketTypeInitial ) {
_ , err = pconn . WriteTo ( wire . WritePublicReset ( connID , 0 , 0 ) , remoteAddr )
return err
}
2017-12-12 02:51:45 +00:00
// a session is only created once the client sent a supported version
// if we receive a packet for a connection that already has session, it's probably an old packet that was sent by the client before the version was negotiated
// it is safe to drop it
2018-01-03 19:19:49 +00:00
if sessionKnown && hdr . VersionFlag && ! protocol . IsSupportedVersion ( s . config . Versions , hdr . Version ) {
2017-12-12 02:51:45 +00:00
return nil
}
2018-01-03 19:19:49 +00:00
// send a Version Negotiation Packet if the client is speaking a different protocol version
// since the client send a Public Header (only gQUIC has a Version Flag), we need to send a gQUIC Version Negotiation Packet
if hdr . VersionFlag && ! protocol . IsSupportedVersion ( s . config . Versions , hdr . Version ) {
2017-12-12 02:51:45 +00:00
// drop packets that are too small to be valid first packets
2018-01-03 19:19:49 +00:00
if len ( packet ) < protocol . MinClientHelloSize + len ( hdr . Raw ) {
2017-12-12 02:51:45 +00:00
return errors . New ( "dropping small packet with unknown version" )
}
2018-01-03 19:19:49 +00:00
utils . Infof ( "Client offered version %s, sending VersionNegotiationPacket" , hdr . Version )
if _ , err := pconn . WriteTo ( wire . ComposeGQUICVersionNegotiation ( hdr . ConnectionID , s . config . Versions ) , remoteAddr ) ; err != nil {
2017-12-12 02:51:45 +00:00
return err
}
2018-01-03 19:19:49 +00:00
}
// This is (potentially) a Client Hello.
// Make sure it has the minimum required size before spending any more ressources on it.
if ! sessionKnown && len ( packet ) < protocol . MinClientHelloSize + len ( hdr . Raw ) {
return errors . New ( "dropping small packet for unknown connection" )
}
if ! sessionKnown {
version := hdr . Version
if ! protocol . IsSupportedVersion ( s . config . Versions , version ) {
2017-12-12 02:51:45 +00:00
return errors . New ( "Server BUG: negotiated version not supported" )
}
2018-01-03 19:19:49 +00:00
utils . Infof ( "Serving new connection: %x, version %s from %v" , hdr . ConnectionID , version , remoteAddr )
2017-12-12 02:51:45 +00:00
session , err = s . newSession (
& conn { pconn : pconn , currentAddr : remoteAddr } ,
version ,
hdr . ConnectionID ,
s . scfg ,
2018-01-03 19:19:49 +00:00
s . tlsConf ,
s . config ,
2017-12-12 02:51:45 +00:00
)
if err != nil {
return err
}
s . sessionsMutex . Lock ( )
2018-01-03 19:19:49 +00:00
s . sessions [ connID ] = session
2017-12-12 02:51:45 +00:00
s . sessionsMutex . Unlock ( )
2018-01-03 19:19:49 +00:00
s . runHandshakeAndSession ( session , connID )
2017-12-12 02:51:45 +00:00
}
session . handlePacket ( & receivedPacket {
2018-01-03 19:19:49 +00:00
remoteAddr : remoteAddr ,
header : hdr ,
data : packetData ,
rcvTime : rcvTime ,
2017-12-12 02:51:45 +00:00
} )
return nil
}
2018-01-03 19:19:49 +00:00
func ( s * server ) runHandshakeAndSession ( session packetHandler , connID protocol . ConnectionID ) {
go func ( ) {
_ = session . run ( )
// session.run() returns as soon as the session is closed
s . removeConnection ( connID )
} ( )
go func ( ) {
2018-01-20 18:07:01 +00:00
if err := <- session . handshakeStatus ( ) ; err != nil {
return
2018-01-03 19:19:49 +00:00
}
s . sessionQueue <- session
} ( )
2017-12-12 02:51:45 +00:00
}
2018-01-03 19:19:49 +00:00
func ( s * server ) removeConnection ( id protocol . ConnectionID ) {
2017-12-12 02:51:45 +00:00
s . sessionsMutex . Lock ( )
s . sessions [ id ] = nil
s . sessionsMutex . Unlock ( )
time . AfterFunc ( s . deleteClosedSessionsAfter , func ( ) {
s . sessionsMutex . Lock ( )
delete ( s . sessions , id )
s . sessionsMutex . Unlock ( )
} )
}