feat(hosts): Add resolver analyzation

This commit is contained in:
Myzel394 2024-08-25 13:11:59 +02:00
parent 4fe2f46b1b
commit 960f365bc9
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
8 changed files with 270 additions and 6 deletions

View File

@ -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(`^.+?\..+$`),
}
}

View 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()

View 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
}

View 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)
}
}

View 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)
}

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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),
} }
} }