youtubebeat/vendor/github.com/elastic/beats/metricbeat/module/system/socket/reverse_lookup.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
}