getWithSubnet returns cached item for the req if it's found by n. expired is true if the item's TTL is expired. k is the resulting key for req. It's returned to avoid recalculating it afterwards. Note that a slow longest-prefix-match algorithm is used, so cache searches are performed up to mask+
(req *dns.Msg, n *net.IPNet)
| 253 | // Note that a slow longest-prefix-match algorithm is used, so cache searches |
| 254 | // are performed up to mask+1 times. |
| 255 | func (c *cache) getWithSubnet(req *dns.Msg, n *net.IPNet) (ci *cacheItem, expired bool, k []byte) { |
| 256 | c.itemsWithSubnetLock.RLock() |
| 257 | defer c.itemsWithSubnetLock.RUnlock() |
| 258 | |
| 259 | if !canLookUpInCache(c.itemsWithSubnet, req) { |
| 260 | return nil, false, nil |
| 261 | } |
| 262 | |
| 263 | ecsIP := n.IP.Mask(n.Mask) |
| 264 | ipLen := len(ecsIP) |
| 265 | m, _ := n.Mask.Size() |
| 266 | |
| 267 | k = msgToKeyWithSubnet(req, ecsIP, m) |
| 268 | data := c.itemsWithSubnet.Get(k) |
| 269 | |
| 270 | // In order to reduce allocations we apply mask on bits level. As the key |
| 271 | // k has ecsIP in bytes slice representation, each iteration we can just |
| 272 | // clear one bit in the end of it by applying the bitmask. |
| 273 | for bitmask := ^byte(0); m >= 0 && data == nil; m-- { |
| 274 | // Set mask identification byte in the key. |
| 275 | k[keyMaskIndex] = byte(m) |
| 276 | |
| 277 | // In case mask is zero, the key doesn't have IP in it. |
| 278 | if m == 0 { |
| 279 | k = slices.Delete(k, keyIPIndex, keyIPIndex+ipLen) |
| 280 | data = c.itemsWithSubnet.Get(k) |
| 281 | |
| 282 | continue |
| 283 | } |
| 284 | |
| 285 | // Shift or renew bitmask. |
| 286 | if m%8 == 0 { |
| 287 | bitmask = ^byte(0) |
| 288 | } else { |
| 289 | bitmask <<= 1 |
| 290 | } |
| 291 | |
| 292 | // Clear the last non-zero bit in the byte of the IP address. |
| 293 | k[keyIPIndex+m/8] &= bitmask |
| 294 | |
| 295 | data = c.itemsWithSubnet.Get(k) |
| 296 | } |
| 297 | |
| 298 | if data == nil { |
| 299 | return nil, false, k |
| 300 | } |
| 301 | |
| 302 | if ci, expired = c.unpackItem(data, req); ci == nil { |
| 303 | c.itemsWithSubnet.Del(k) |
| 304 | } |
| 305 | |
| 306 | return ci, expired, k |
| 307 | } |
| 308 | |
| 309 | // canLookUpInCache returns true if these parameters could be used to make a |
| 310 | // cache lookup. |