AdGuardHome/home/whois.go

147 lines
2.6 KiB
Go

package home
import (
"strings"
"sync"
"github.com/AdguardTeam/golibs/log"
whois "github.com/likexian/whois-go"
)
const maxValueLength = 250
// Whois - module context
type Whois struct {
clients *clientsContainer
ips map[string]bool
lock sync.Mutex
ipChan chan string
}
// Create module context
func initWhois(clients *clientsContainer) *Whois {
w := Whois{}
w.clients = clients
w.ips = make(map[string]bool)
w.ipChan = make(chan string, 255)
go w.workerLoop()
return &w
}
// If the value is too large - cut it and append "..."
func trimValue(s string) string {
if len(s) <= maxValueLength {
return s
}
return s[:maxValueLength-3] + "..."
}
// Parse plain-text data from the response
func whoisParse(data string) map[string]string {
m := map[string]string{}
descr := ""
netname := ""
lines := strings.Split(data, "\n")
for _, ln := range lines {
ln = strings.TrimSpace(ln)
if len(ln) == 0 || ln[0] == '#' {
continue
}
kv := strings.SplitN(ln, ":", 2)
if len(kv) != 2 {
continue
}
k := strings.TrimSpace(kv[0])
k = strings.ToLower(k)
v := strings.TrimSpace(kv[1])
switch k {
case "org-name":
m["orgname"] = trimValue(v)
case "orgname":
fallthrough
case "city":
fallthrough
case "country":
m[k] = trimValue(v)
case "descr":
descr = v
case "netname":
netname = v
}
}
// descr or netname -> orgname
_, ok := m["orgname"]
if !ok && len(descr) != 0 {
m["orgname"] = trimValue(descr)
} else if !ok && len(netname) != 0 {
m["orgname"] = trimValue(netname)
}
return m
}
// Request WHOIS information
func whoisProcess(ip string) [][]string {
data := [][]string{}
resp, err := whois.Whois(ip)
if err != nil {
log.Debug("Whois: error: %s IP:%s", err, ip)
return data
}
log.Debug("Whois: IP:%s response: %d bytes", ip, len(resp))
m := whoisParse(resp)
keys := []string{"orgname", "country", "city"}
for _, k := range keys {
v, found := m[k]
if !found {
continue
}
pair := []string{k, v}
data = append(data, pair)
}
return data
}
// Begin - begin requesting WHOIS info
func (w *Whois) Begin(ip string) {
w.lock.Lock()
_, found := w.ips[ip]
if found {
w.lock.Unlock()
return
}
w.ips[ip] = true
w.lock.Unlock()
log.Debug("Whois: adding %s", ip)
select {
case w.ipChan <- ip:
//
default:
log.Debug("Whois: queue is full")
}
}
// Get IP address from channel; get WHOIS info; associate info with a client
func (w *Whois) workerLoop() {
for {
var ip string
ip = <-w.ipChan
info := whoisProcess(ip)
if len(info) == 0 {
continue
}
w.clients.SetWhoisInfo(ip, info)
}
}