130 lines
3.4 KiB
Go
130 lines
3.4 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package socket
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"golang.org/x/net/publicsuffix"
|
|
)
|
|
|
|
type ptrRecord struct {
|
|
hostname string
|
|
error error
|
|
expires time.Time
|
|
}
|
|
|
|
func (r ptrRecord) IsExpired(now time.Time) bool {
|
|
return now.After(r.expires)
|
|
}
|
|
|
|
// ReverseLookupCache is a cache for storing and retrieving the results of
|
|
// reverse DNS queries. It caches the results of queries regardless of their
|
|
// outcome (success or failure). The result is cached for the amount of time
|
|
// specified by parameters and not based on the TTL from the PTR record.
|
|
type ReverseLookupCache struct {
|
|
data map[string]ptrRecord
|
|
successTTL, failureTTL time.Duration
|
|
}
|
|
|
|
// NewReverseLookupCache returns a new cache.
|
|
func NewReverseLookupCache(successTTL, failureTTL time.Duration) *ReverseLookupCache {
|
|
c := &ReverseLookupCache{
|
|
data: map[string]ptrRecord{},
|
|
successTTL: successTTL,
|
|
failureTTL: failureTTL,
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// Cleanup removes expired entries from the cache.
|
|
func (c *ReverseLookupCache) Cleanup() {
|
|
now := time.Now()
|
|
for k, ptr := range c.data {
|
|
if ptr.IsExpired(now) {
|
|
delete(c.data, k)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lookup performs a reverse lookup on the given IP address. A cached result
|
|
// will be returned if it is contained in the cache, otherwise a lookup is
|
|
// performed.
|
|
func (c ReverseLookupCache) Lookup(ip net.IP) (string, error) {
|
|
// Go doesn't expose a lookup method that accepts net.IP so
|
|
// unfortunately we must convert the IP to a string.
|
|
ipStr := ip.String()
|
|
|
|
// XXX: This could be implemented using common.Cache with a separate
|
|
// cleanup thread.
|
|
c.Cleanup()
|
|
|
|
// Check the cache.
|
|
now := time.Now()
|
|
if ptr, found := c.data[ipStr]; found && !ptr.IsExpired(now) {
|
|
return ptr.hostname, ptr.error
|
|
}
|
|
|
|
// Do a new lookup.
|
|
names, err := net.LookupAddr(ipStr)
|
|
now = time.Now()
|
|
|
|
var ptr ptrRecord
|
|
switch {
|
|
case err != nil:
|
|
ptr.expires = now.Add(c.failureTTL)
|
|
ptr.error = err
|
|
case len(names) == 0:
|
|
ptr.expires = now.Add(c.failureTTL)
|
|
ptr.error = fmt.Errorf("empty dns response")
|
|
default:
|
|
ptr.expires = now.Add(c.successTTL)
|
|
ptr.hostname = names[0]
|
|
}
|
|
|
|
c.data[ipStr] = ptr
|
|
return ptr.hostname, ptr.error
|
|
}
|
|
|
|
// etldPlusOne returns the effective top-level domain plus one domain for the
|
|
// given hostname.
|
|
func etldPlusOne(hostname string) (string, error) {
|
|
if hostname == "" {
|
|
return "", nil
|
|
}
|
|
|
|
trimmed := false
|
|
if hostname[len(hostname)-1] == '.' {
|
|
hostname = hostname[:len(hostname)-1]
|
|
trimmed = true
|
|
}
|
|
|
|
domain, err := publicsuffix.EffectiveTLDPlusOne(hostname)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if trimmed {
|
|
return domain + ".", nil
|
|
}
|
|
return domain, nil
|
|
}
|