Pull request: home: imp whois parse

Updates #2646.

Squashed commit of the following:

commit 0a5ff6ae74c532a296c0594a598a99c7cfaccf8c
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu May 20 14:07:08 2021 +0300

    home: imp code

commit 2af0f463a77b81e827d9faca079a19c5437e1cd9
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu May 20 13:48:08 2021 +0300

    home: imp whois parse
This commit is contained in:
Ainar Garipov 2021-05-20 14:22:06 +03:00
parent 21972e49cb
commit 9c60aef637
2 changed files with 135 additions and 46 deletions

View File

@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/aghio" "github.com/AdguardTeam/AdGuardHome/internal/aghio"
"github.com/AdguardTeam/AdGuardHome/internal/aghstrings"
"github.com/AdguardTeam/golibs/cache" "github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
@ -66,55 +65,71 @@ func trimValue(s string) string {
return s[:maxValueLength-3] + "..." return s[:maxValueLength-3] + "..."
} }
// Parse plain-text data from the response // coalesceStr returns the first non-empty string.
func whoisParse(data string) map[string]string { //
m := map[string]string{} // TODO(a.garipov): Move to aghstrings?
descr := "" func coalesceStr(strs ...string) (res string) {
netname := "" for _, s := range strs {
for len(data) != 0 { if s != "" {
ln := aghstrings.SplitNext(&data, '\n') return s
if len(ln) == 0 || 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 "city", "country", "orgname":
m[k] = trimValue(v)
case "descr":
if len(descr) == 0 {
descr = v
}
case "netname":
netname = v
case "whois": // "whois: whois.arin.net"
m["whois"] = v
case "referralserver": // "ReferralServer: whois://whois.ripe.net"
if strings.HasPrefix(v, "whois://") {
m["whois"] = v[len("whois://"):]
}
} }
} }
_, ok := m["orgname"] return ""
if !ok { }
// Set orgname from either descr or netname for the frontent.
// // isWhoisComment returns true if the string is empty or is a WHOIS comment.
// TODO(a.garipov): Perhaps don't do that in the V1 HTTP API? func isWhoisComment(s string) (ok bool) {
if descr != "" { return len(s) == 0 || s[0] == '#' || s[0] == '%'
m["orgname"] = trimValue(descr) }
} else if netname != "" {
m["orgname"] = trimValue(netname) // strmap is an alias for convenience.
type strmap = map[string]string
// whoisParse parses a subset of plain-text data from the WHOIS response into
// a string map.
func whoisParse(data string) (m strmap) {
m = strmap{}
var orgname string
lines := strings.Split(data, "\n")
for _, l := range lines {
if isWhoisComment(l) {
continue
} }
kv := strings.SplitN(l, ":", 2)
if len(kv) != 2 {
continue
}
k := strings.ToLower(strings.TrimSpace(kv[0]))
v := strings.TrimSpace(kv[1])
if v == "" {
continue
}
switch k {
case "orgname", "org-name":
k = "orgname"
v = trimValue(v)
orgname = v
case "city", "country":
v = trimValue(v)
case "descr", "netname":
k = "orgname"
v = coalesceStr(orgname, v)
orgname = v
case "whois":
k = "whois"
case "referralserver":
k = "whois"
v = strings.TrimPrefix(v, "whois://")
default:
continue
}
m[k] = v
} }
return m return m

View File

@ -76,3 +76,77 @@ func TestWhois(t *testing.T) {
assert.Equal(t, "Imagiland", m["country"]) assert.Equal(t, "Imagiland", m["country"])
assert.Equal(t, "Nonreal", m["city"]) assert.Equal(t, "Nonreal", m["city"])
} }
func TestWhoisParse(t *testing.T) {
const (
city = "Nonreal"
country = "Imagiland"
orgname = "FakeOrgLLC"
whois = "whois.example.net"
)
testCases := []struct {
want strmap
name string
in string
}{{
want: strmap{},
name: "empty",
in: ``,
}, {
want: strmap{},
name: "comments",
in: "%\n#",
}, {
want: strmap{},
name: "no_colon",
in: "city",
}, {
want: strmap{},
name: "no_value",
in: "city:",
}, {
want: strmap{"city": city},
name: "city",
in: `city: ` + city,
}, {
want: strmap{"country": country},
name: "country",
in: `country: ` + country,
}, {
want: strmap{"orgname": orgname},
name: "orgname",
in: `orgname: ` + orgname,
}, {
want: strmap{"orgname": orgname},
name: "orgname_hyphen",
in: `org-name: ` + orgname,
}, {
want: strmap{"orgname": orgname},
name: "orgname_descr",
in: `descr: ` + orgname,
}, {
want: strmap{"orgname": orgname},
name: "orgname_netname",
in: `netname: ` + orgname,
}, {
want: strmap{"whois": whois},
name: "whois",
in: `whois: ` + whois,
}, {
want: strmap{"whois": whois},
name: "referralserver",
in: `referralserver: whois://` + whois,
}, {
want: strmap{},
name: "other",
in: `other: value`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := whoisParse(tc.in)
assert.Equal(t, tc.want, got)
})
}
}