github.com/MerlinKodo/quic-go@v0.39.2/packet_handler_map.go (about) 1 package quic 2 3 import ( 4 "crypto/hmac" 5 "crypto/rand" 6 "crypto/sha256" 7 "hash" 8 "io" 9 "net" 10 "sync" 11 "time" 12 13 "github.com/MerlinKodo/quic-go/internal/protocol" 14 "github.com/MerlinKodo/quic-go/internal/utils" 15 ) 16 17 type connCapabilities struct { 18 // This connection has the Don't Fragment (DF) bit set. 19 // This means it makes to run DPLPMTUD. 20 DF bool 21 // GSO (Generic Segmentation Offload) supported 22 GSO bool 23 // ECN (Explicit Congestion Notifications) supported 24 ECN bool 25 } 26 27 // rawConn is a connection that allow reading of a receivedPackeh. 28 type rawConn interface { 29 ReadPacket() (receivedPacket, error) 30 // WritePacket writes a packet on the wire. 31 // gsoSize is the size of a single packet, or 0 to disable GSO. 32 // It is invalid to set gsoSize if capabilities.GSO is not set. 33 WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) 34 LocalAddr() net.Addr 35 SetReadDeadline(time.Time) error 36 io.Closer 37 38 capabilities() connCapabilities 39 } 40 41 type closePacket struct { 42 payload []byte 43 addr net.Addr 44 info packetInfo 45 } 46 47 type packetHandlerMap struct { 48 mutex sync.Mutex 49 handlers map[protocol.ConnectionID]packetHandler 50 resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler 51 52 closed bool 53 closeChan chan struct{} 54 55 enqueueClosePacket func(closePacket) 56 57 deleteRetiredConnsAfter time.Duration 58 59 statelessResetMutex sync.Mutex 60 statelessResetHasher hash.Hash 61 62 logger utils.Logger 63 } 64 65 var _ packetHandlerManager = &packetHandlerMap{} 66 67 func newPacketHandlerMap(key *StatelessResetKey, enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { 68 h := &packetHandlerMap{ 69 closeChan: make(chan struct{}), 70 handlers: make(map[protocol.ConnectionID]packetHandler), 71 resetTokens: make(map[protocol.StatelessResetToken]packetHandler), 72 deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, 73 enqueueClosePacket: enqueueClosePacket, 74 logger: logger, 75 } 76 if key != nil { 77 h.statelessResetHasher = hmac.New(sha256.New, key[:]) 78 } 79 if h.logger.Debug() { 80 go h.logUsage() 81 } 82 return h 83 } 84 85 func (h *packetHandlerMap) logUsage() { 86 ticker := time.NewTicker(2 * time.Second) 87 var printedZero bool 88 for { 89 select { 90 case <-h.closeChan: 91 return 92 case <-ticker.C: 93 } 94 95 h.mutex.Lock() 96 numHandlers := len(h.handlers) 97 numTokens := len(h.resetTokens) 98 h.mutex.Unlock() 99 // If the number tracked handlers and tokens is zero, only print it a single time. 100 hasZero := numHandlers == 0 && numTokens == 0 101 if !hasZero || (hasZero && !printedZero) { 102 h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens) 103 printedZero = false 104 if hasZero { 105 printedZero = true 106 } 107 } 108 } 109 } 110 111 func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { 112 h.mutex.Lock() 113 defer h.mutex.Unlock() 114 115 handler, ok := h.handlers[id] 116 return handler, ok 117 } 118 119 func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { 120 h.mutex.Lock() 121 defer h.mutex.Unlock() 122 123 if _, ok := h.handlers[id]; ok { 124 h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) 125 return false 126 } 127 h.handlers[id] = handler 128 h.logger.Debugf("Adding connection ID %s.", id) 129 return true 130 } 131 132 func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, fn func() (packetHandler, bool)) bool { 133 h.mutex.Lock() 134 defer h.mutex.Unlock() 135 136 if _, ok := h.handlers[clientDestConnID]; ok { 137 h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) 138 return false 139 } 140 conn, ok := fn() 141 if !ok { 142 return false 143 } 144 h.handlers[clientDestConnID] = conn 145 h.handlers[newConnID] = conn 146 h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) 147 return true 148 } 149 150 func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { 151 h.mutex.Lock() 152 delete(h.handlers, id) 153 h.mutex.Unlock() 154 h.logger.Debugf("Removing connection ID %s.", id) 155 } 156 157 func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { 158 h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) 159 time.AfterFunc(h.deleteRetiredConnsAfter, func() { 160 h.mutex.Lock() 161 delete(h.handlers, id) 162 h.mutex.Unlock() 163 h.logger.Debugf("Removing connection ID %s after it has been retired.", id) 164 }) 165 } 166 167 // ReplaceWithClosed is called when a connection is closed. 168 // Depending on which side closed the connection, we need to: 169 // * remote close: absorb delayed packets 170 // * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost 171 func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, pers protocol.Perspective, connClosePacket []byte) { 172 var handler packetHandler 173 if connClosePacket != nil { 174 handler = newClosedLocalConn( 175 func(addr net.Addr, info packetInfo) { 176 h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) 177 }, 178 pers, 179 h.logger, 180 ) 181 } else { 182 handler = newClosedRemoteConn(pers) 183 } 184 185 h.mutex.Lock() 186 for _, id := range ids { 187 h.handlers[id] = handler 188 } 189 h.mutex.Unlock() 190 h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids) 191 192 time.AfterFunc(h.deleteRetiredConnsAfter, func() { 193 h.mutex.Lock() 194 handler.shutdown() 195 for _, id := range ids { 196 delete(h.handlers, id) 197 } 198 h.mutex.Unlock() 199 h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids) 200 }) 201 } 202 203 func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { 204 h.mutex.Lock() 205 h.resetTokens[token] = handler 206 h.mutex.Unlock() 207 } 208 209 func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) { 210 h.mutex.Lock() 211 delete(h.resetTokens, token) 212 h.mutex.Unlock() 213 } 214 215 func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { 216 h.mutex.Lock() 217 defer h.mutex.Unlock() 218 219 handler, ok := h.resetTokens[token] 220 return handler, ok 221 } 222 223 func (h *packetHandlerMap) CloseServer() { 224 h.mutex.Lock() 225 var wg sync.WaitGroup 226 for _, handler := range h.handlers { 227 if handler.getPerspective() == protocol.PerspectiveServer { 228 wg.Add(1) 229 go func(handler packetHandler) { 230 // blocks until the CONNECTION_CLOSE has been sent and the run-loop has stopped 231 handler.shutdown() 232 wg.Done() 233 }(handler) 234 } 235 } 236 h.mutex.Unlock() 237 wg.Wait() 238 } 239 240 func (h *packetHandlerMap) Close(e error) { 241 h.mutex.Lock() 242 243 if h.closed { 244 h.mutex.Unlock() 245 return 246 } 247 248 close(h.closeChan) 249 250 var wg sync.WaitGroup 251 for _, handler := range h.handlers { 252 wg.Add(1) 253 go func(handler packetHandler) { 254 handler.destroy(e) 255 wg.Done() 256 }(handler) 257 } 258 h.closed = true 259 h.mutex.Unlock() 260 wg.Wait() 261 } 262 263 func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { 264 var token protocol.StatelessResetToken 265 if h.statelessResetHasher == nil { 266 // Return a random stateless reset token. 267 // This token will be sent in the server's transport parameters. 268 // By using a random token, an off-path attacker won't be able to disrupt the connection. 269 rand.Read(token[:]) 270 return token 271 } 272 h.statelessResetMutex.Lock() 273 h.statelessResetHasher.Write(connID.Bytes()) 274 copy(token[:], h.statelessResetHasher.Sum(nil)) 275 h.statelessResetHasher.Reset() 276 h.statelessResetMutex.Unlock() 277 return token 278 }