mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-19 07:25:27 +02:00
feat(hosts): Add resolver analyzation
This commit is contained in:
parent
4fe2f46b1b
commit
960f365bc9
@ -3,6 +3,7 @@ package docvalues
|
|||||||
import (
|
import (
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -173,3 +174,9 @@ func SingleEnumValue(value string) EnumValue {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DomainValue() Value {
|
||||||
|
return RegexValue{
|
||||||
|
Regex: *regexp.MustCompile(`^.+?\..+$`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
10
handlers/hosts/fields/documentation-fields.go
Normal file
10
handlers/hosts/fields/documentation-fields.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package fields
|
||||||
|
|
||||||
|
import docvalues "config-lsp/doc-values"
|
||||||
|
|
||||||
|
var IPAddressField = docvalues.IPAddressValue{
|
||||||
|
AllowIPv4: true,
|
||||||
|
AllowIPv6: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var HostnameField = docvalues.DomainValue()
|
93
handlers/hosts/handlers/analyzer.go
Normal file
93
handlers/hosts/handlers/analyzer.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/hosts/tree"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResolverEntry struct {
|
||||||
|
IPv4Address net.IP
|
||||||
|
IPv6Address net.IP
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resolver struct {
|
||||||
|
Entries map[string]ResolverEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func createEntry(
|
||||||
|
line uint32,
|
||||||
|
ip net.IP,
|
||||||
|
) ResolverEntry {
|
||||||
|
entry := ResolverEntry{
|
||||||
|
Line: line,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv4 := ip.To4(); ipv4 != nil {
|
||||||
|
entry.IPv4Address = ipv4
|
||||||
|
} else if ipv6 := ip.To16(); ipv6 != nil {
|
||||||
|
entry.IPv6Address = ipv6
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
type hostnameEntry struct {
|
||||||
|
Location common.LocationRange
|
||||||
|
HostName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateResolverFromParser(p tree.HostsParser) (Resolver, []common.LSPError) {
|
||||||
|
errors := make([]common.LSPError, 0)
|
||||||
|
resolver := Resolver{
|
||||||
|
Entries: make(map[string]ResolverEntry),
|
||||||
|
}
|
||||||
|
|
||||||
|
for lineNumber, entry := range p.Tree.Entries {
|
||||||
|
if entry.IPAddress != nil && entry.Hostname != nil {
|
||||||
|
hostNames := append(
|
||||||
|
[]hostnameEntry{
|
||||||
|
{
|
||||||
|
Location: entry.Hostname.Location,
|
||||||
|
HostName: entry.Hostname.Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils.Map(
|
||||||
|
entry.Aliases,
|
||||||
|
func(alias *tree.HostsHostname) hostnameEntry {
|
||||||
|
return hostnameEntry{
|
||||||
|
Location: alias.Location,
|
||||||
|
HostName: alias.Value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)...,
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, hostName := range hostNames {
|
||||||
|
entry := createEntry(
|
||||||
|
lineNumber,
|
||||||
|
entry.IPAddress.Value.IP,
|
||||||
|
)
|
||||||
|
|
||||||
|
if resolv, found := resolver.Entries[hostName.HostName]; found {
|
||||||
|
errors = append(
|
||||||
|
errors,
|
||||||
|
common.LSPError{
|
||||||
|
Range: hostName.Location,
|
||||||
|
Err: DuplicateHostEntry{
|
||||||
|
AlreadyFoundAt: resolv.Line,
|
||||||
|
Hostname: hostName.HostName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resolver.Entries[hostName.HostName] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolver, errors
|
||||||
|
}
|
117
handlers/hosts/handlers/analyzer_test.go
Normal file
117
handlers/hosts/handlers/analyzer_test.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/hosts/tree"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResolverEntriesWorksWithNonOverlapping(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
1.2.3.4 hello.com
|
||||||
|
5.5.5.5 world.com
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := tree.CreateNewHostsParser()
|
||||||
|
errors := parser.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver, errors := CreateResolverFromParser(parser)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
t.Errorf("Expected no errors, but got %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resolver.Entries) != 2 {
|
||||||
|
t.Errorf("Expected 2 entries, but got %v", len(resolver.Entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") {
|
||||||
|
t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["world.com"].IPv4Address.String() == "5.5.5.5") {
|
||||||
|
t.Errorf("Expected world.com to be 5.5.5.5, but got %v", resolver.Entries["world.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["hello.com"].IPv6Address == nil) {
|
||||||
|
t.Errorf("Expected hello.com to have no IPv6 address, but got %v", resolver.Entries["hello.com"].IPv6Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["world.com"].IPv6Address == nil) {
|
||||||
|
t.Errorf("Expected world.com to have no IPv6 address, but got %v", resolver.Entries["world.com"].IPv6Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolverEntriesWithSimpleOverlapping(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
1.2.3.4 hello.com
|
||||||
|
5.5.5.5 hello.com
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := tree.CreateNewHostsParser()
|
||||||
|
errors := parser.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver, errors := CreateResolverFromParser(parser)
|
||||||
|
|
||||||
|
if !(len(errors) == 1) {
|
||||||
|
t.Errorf("Expected 1 error, but got %v", len(errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resolver.Entries) != 1 {
|
||||||
|
t.Errorf("Expected 1 entry, but got %v", len(resolver.Entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") {
|
||||||
|
t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolverEntriesWithComplexOverlapping(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
input := utils.Dedent(`
|
||||||
|
1.2.3.4 hello.com test.com
|
||||||
|
5.5.5.5 check.com test.com
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := tree.CreateNewHostsParser()
|
||||||
|
errors := parser.Parse(input)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver, errors := CreateResolverFromParser(parser)
|
||||||
|
|
||||||
|
if !(len(errors) == 1) {
|
||||||
|
t.Errorf("Expected 1 error, but got %v", len(errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resolver.Entries) != 3 {
|
||||||
|
t.Errorf("Expected 3 entries, but got %v", len(resolver.Entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["hello.com"].IPv4Address.String() == "1.2.3.4") {
|
||||||
|
t.Errorf("Expected hello.com to be 1.2.3.4, but got %v", resolver.Entries["hello.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["check.com"].IPv4Address.String() == "5.5.5.5") {
|
||||||
|
t.Errorf("Expected check.com to be 5.5.5.5, but got %v", resolver.Entries["check.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(resolver.Entries["test.com"].IPv4Address.String() == "1.2.3.4") {
|
||||||
|
t.Errorf("Expected test.com to have no IPv4 address, but got %v", resolver.Entries["test.com"].IPv4Address)
|
||||||
|
}
|
||||||
|
}
|
12
handlers/hosts/handlers/errors.go
Normal file
12
handlers/hosts/handlers/errors.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type DuplicateHostEntry struct {
|
||||||
|
AlreadyFoundAt uint32
|
||||||
|
Hostname string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DuplicateHostEntry) Error() string {
|
||||||
|
return fmt.Sprintf("'%s' already defined on line %d", d.Hostname, d.AlreadyFoundAt)
|
||||||
|
}
|
@ -2,6 +2,7 @@ package tree
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ func TestValidSimpleExampleWorks(
|
|||||||
t.Errorf("Expected IP address to be present, but got nil")
|
t.Errorf("Expected IP address to be present, but got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(parser.Tree.Entries[1].IPAddress.Value == "1.2.3.4") {
|
if !(parser.Tree.Entries[1].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
|
||||||
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[1].IPAddress.Value)
|
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[1].IPAddress.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ func TestValidComplexExampleWorks(
|
|||||||
t.Errorf("Expected IP address to be present, but got nil")
|
t.Errorf("Expected IP address to be present, but got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(parser.Tree.Entries[3].IPAddress.Value == "1.2.3.4") {
|
if !(parser.Tree.Entries[3].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
|
||||||
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[2].IPAddress.Value)
|
t.Errorf("Expected IP address to be 1.2.3.4, but got %v", parser.Tree.Entries[2].IPAddress.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ func TestInvalidExampleWorks(
|
|||||||
t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines))
|
t.Errorf("Expected no comment lines, but got %v", len(parser.CommentLines))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(parser.Tree.Entries[1].IPAddress.Value == "1.2.3.4") {
|
if !(parser.Tree.Entries[1].IPAddress.Value.String() == net.ParseIP("1.2.3.4").String()) {
|
||||||
t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[1].IPAddress)
|
t.Errorf("Expected IP address to be nil, but got %v", parser.Tree.Entries[1].IPAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package tree
|
|||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/antlr4-go/antlr/v4"
|
"github.com/antlr4-go/antlr/v4"
|
||||||
)
|
)
|
||||||
@ -48,7 +49,7 @@ func (p HostsEntry) String() string {
|
|||||||
str := fmt.Sprintf("HostsEntry(%v)", p.Location)
|
str := fmt.Sprintf("HostsEntry(%v)", p.Location)
|
||||||
|
|
||||||
if p.IPAddress != nil {
|
if p.IPAddress != nil {
|
||||||
str += " " + p.IPAddress.Value
|
str += " " + p.IPAddress.Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Hostname != nil {
|
if p.Hostname != nil {
|
||||||
@ -64,7 +65,7 @@ func (p HostsEntry) String() string {
|
|||||||
|
|
||||||
type HostsIPAddress struct {
|
type HostsIPAddress struct {
|
||||||
Location common.LocationRange
|
Location common.LocationRange
|
||||||
Value string
|
Value net.IPAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
type HostsHostname struct {
|
type HostsHostname struct {
|
||||||
|
@ -2,7 +2,9 @@ package tree
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
"config-lsp/handlers/hosts/parser"
|
"config-lsp/handlers/hosts/parser"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/antlr4-go/antlr/v4"
|
"github.com/antlr4-go/antlr/v4"
|
||||||
)
|
)
|
||||||
@ -14,6 +16,7 @@ type hostsListenerContext struct {
|
|||||||
type hostsParserListener struct {
|
type hostsParserListener struct {
|
||||||
*parser.BaseHostsListener
|
*parser.BaseHostsListener
|
||||||
Parser *HostsParser
|
Parser *HostsParser
|
||||||
|
Errors []common.LSPError
|
||||||
hostsContext hostsListenerContext
|
hostsContext hostsListenerContext
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,11 +38,30 @@ func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) {
|
|||||||
location := characterRangeFromCtx(ctx.BaseParserRuleContext)
|
location := characterRangeFromCtx(ctx.BaseParserRuleContext)
|
||||||
location.ChangeBothLines(s.hostsContext.line)
|
location.ChangeBothLines(s.hostsContext.line)
|
||||||
|
|
||||||
|
ip := net.ParseIP(ctx.GetText())
|
||||||
|
|
||||||
|
if ip == nil {
|
||||||
|
s.Errors = append(s.Errors, common.LSPError{
|
||||||
|
Range: location,
|
||||||
|
Err: docvalues.InvalidIPAddress{},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr, err := net.ResolveIPAddr("ip", ip.String())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.Errors = append(s.Errors, common.LSPError{
|
||||||
|
Range: location,
|
||||||
|
Err: docvalues.InvalidIPAddress{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
entry := s.Parser.Tree.Entries[location.Start.Line]
|
entry := s.Parser.Tree.Entries[location.Start.Line]
|
||||||
|
|
||||||
entry.IPAddress = &HostsIPAddress{
|
entry.IPAddress = &HostsIPAddress{
|
||||||
Location: location,
|
Location: location,
|
||||||
Value: ctx.GetText(),
|
Value: *ipAddr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +113,7 @@ func createHostsFileListener(
|
|||||||
hostsContext: hostsListenerContext{
|
hostsContext: hostsListenerContext{
|
||||||
line: line,
|
line: line,
|
||||||
},
|
},
|
||||||
|
Errors: make([]common.LSPError, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user