mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
344 lines
8.5 KiB
Go
344 lines
8.5 KiB
Go
package wireguard
|
|
|
|
import (
|
|
docvalues "config-lsp/doc-values"
|
|
"config-lsp/utils"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
)
|
|
|
|
func (p wireguardParser) analyze() []protocol.Diagnostic {
|
|
sectionsErrors := p.analyzeSections()
|
|
|
|
if len(sectionsErrors) > 0 {
|
|
return sectionsErrors
|
|
}
|
|
|
|
validCheckErrors := p.checkIfValuesAreValid()
|
|
|
|
if len(validCheckErrors) > 0 {
|
|
return validCheckErrors
|
|
}
|
|
|
|
diagnostics := []protocol.Diagnostic{}
|
|
diagnostics = append(diagnostics, p.checkForDuplicateProperties()...)
|
|
diagnostics = append(diagnostics, p.analyzeDNSContainsFallback()...)
|
|
diagnostics = append(diagnostics, p.analyzeKeepAliveIsSet()...)
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (p wireguardParser) analyzeSections() []protocol.Diagnostic {
|
|
diagnostics := []protocol.Diagnostic{}
|
|
|
|
for _, section := range p.Sections {
|
|
sectionDiagnostics := section.analyzeSection()
|
|
|
|
if len(sectionDiagnostics) > 0 {
|
|
diagnostics = append(diagnostics, sectionDiagnostics...)
|
|
}
|
|
}
|
|
|
|
if len(diagnostics) > 0 {
|
|
return diagnostics
|
|
}
|
|
|
|
return p.analyzeOnlyOneInterfaceSectionSpecified()
|
|
}
|
|
|
|
func (p wireguardParser) analyzeOnlyOneInterfaceSectionSpecified() []protocol.Diagnostic {
|
|
diagnostics := []protocol.Diagnostic{}
|
|
alreadyFound := false
|
|
|
|
for _, section := range p.Sections {
|
|
if *section.Name == "Interface" {
|
|
if alreadyFound {
|
|
severity := protocol.DiagnosticSeverityError
|
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
|
Message: "Only one [Interface] section is allowed",
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: section.StartLine,
|
|
Character: 0,
|
|
},
|
|
End: protocol.Position{
|
|
Line: section.StartLine,
|
|
Character: 99999999,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
alreadyFound = true
|
|
}
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (p wireguardParser) analyzeDNSContainsFallback() []protocol.Diagnostic {
|
|
lineNumber, property := p.fetchPropertyByName("DNS")
|
|
|
|
if property == nil {
|
|
return []protocol.Diagnostic{}
|
|
}
|
|
|
|
dnsAmount := len(strings.Split(property.Value.Value, ","))
|
|
|
|
if dnsAmount == 1 {
|
|
severity := protocol.DiagnosticSeverityHint
|
|
|
|
return []protocol.Diagnostic{
|
|
{
|
|
Message: "There is one DNS server specified. It is recommended to set up fallback DNS servers",
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: *lineNumber,
|
|
Character: property.Value.Location.Start,
|
|
},
|
|
End: protocol.Position{
|
|
Line: *lineNumber,
|
|
Character: property.Value.Location.End,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
return []protocol.Diagnostic{}
|
|
}
|
|
|
|
func (p wireguardParser) analyzeKeepAliveIsSet() []protocol.Diagnostic {
|
|
diagnostics := make([]protocol.Diagnostic, 0)
|
|
|
|
for _, section := range p.Sections {
|
|
if section.Name != nil && *section.Name == "Peer" {
|
|
// If an endpoint is set, then we should only check for the keepalive property
|
|
if section.fetchFirstProperty("Endpoint") != nil {
|
|
if section.fetchFirstProperty("PersistentKeepalive") == nil {
|
|
severity := protocol.DiagnosticSeverityHint
|
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
|
Message: "PersistentKeepalive is not set. It is recommended to set this property, as it helps to maintain the connection when users are behind NAT",
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: section.StartLine,
|
|
Character: 0,
|
|
},
|
|
End: protocol.Position{
|
|
Line: section.StartLine,
|
|
Character: 99999999,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
// Check if the values are valid.
|
|
// Assumes that sections have been analyzed already.
|
|
func (p wireguardParser) checkIfValuesAreValid() []protocol.Diagnostic {
|
|
diagnostics := []protocol.Diagnostic{}
|
|
|
|
for _, section := range p.Sections {
|
|
for lineNumber, property := range section.Properties {
|
|
diagnostics = append(
|
|
diagnostics,
|
|
property.analyzeProperty(section, lineNumber)...,
|
|
)
|
|
}
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (p wireguardSection) analyzeSection() []protocol.Diagnostic {
|
|
diagnostics := []protocol.Diagnostic{}
|
|
|
|
if p.Name == nil {
|
|
// No section name
|
|
severity := protocol.DiagnosticSeverityError
|
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
|
Message: "This section is missing a name",
|
|
Severity: &severity,
|
|
Range: p.getRange(),
|
|
})
|
|
return diagnostics
|
|
}
|
|
|
|
if _, found := optionsHeaderMap[*p.Name]; !found {
|
|
// Unknown section
|
|
severity := protocol.DiagnosticSeverityError
|
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
|
Message: fmt.Sprintf("Unknown section '%s'. It must be one of: [Interface], [Peer]", *p.Name),
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: p.StartLine,
|
|
Character: 0,
|
|
},
|
|
End: protocol.Position{
|
|
Line: p.StartLine,
|
|
Character: 99999999,
|
|
},
|
|
},
|
|
})
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
// Check if the property is valid.
|
|
// Returns a list of diagnostics.
|
|
// `belongingSection` is the section to which the property belongs. This value is
|
|
// expected to be non-nil and expected to be a valid Wireguard section.
|
|
func (p wireguardProperty) analyzeProperty(
|
|
belongingSection *wireguardSection,
|
|
propertyLine uint32,
|
|
) []protocol.Diagnostic {
|
|
sectionOptions := optionsHeaderMap[*belongingSection.Name]
|
|
option, found := sectionOptions[p.Key.Name]
|
|
|
|
if !found {
|
|
// Unknown property
|
|
severity := protocol.DiagnosticSeverityError
|
|
return []protocol.Diagnostic{
|
|
{
|
|
Message: fmt.Sprintf("Unknown property '%s'", p.Key.Name),
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: p.Key.Location.Start,
|
|
},
|
|
End: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: p.Key.Location.End,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
if p.Value == nil {
|
|
// No value to check
|
|
severity := protocol.DiagnosticSeverityWarning
|
|
return []protocol.Diagnostic{
|
|
{
|
|
Message: "Property is missing a value",
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: 0,
|
|
},
|
|
End: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: 99999999,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
errors := option.CheckIsValid(p.Value.Value)
|
|
|
|
return utils.Map(errors, func(err *docvalues.InvalidValue) protocol.Diagnostic {
|
|
severity := protocol.DiagnosticSeverityError
|
|
return protocol.Diagnostic{
|
|
Message: err.GetMessage(),
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: p.Value.Location.Start + err.Start,
|
|
},
|
|
End: protocol.Position{
|
|
Line: propertyLine,
|
|
Character: p.Value.Location.Start + err.End,
|
|
},
|
|
},
|
|
}
|
|
})
|
|
}
|
|
|
|
func (p wireguardParser) checkForDuplicateProperties() []protocol.Diagnostic {
|
|
diagnostics := make([]protocol.Diagnostic, 0)
|
|
|
|
for _, section := range p.Sections {
|
|
diagnostics = append(diagnostics, section.analyzeDuplicateProperties()...)
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (p wireguardSection) analyzeDuplicateProperties() []protocol.Diagnostic {
|
|
diagnostics := make([]protocol.Diagnostic, 0)
|
|
|
|
existingProperties := make(map[string]uint32)
|
|
|
|
lines := utils.KeysOfMap(p.Properties)
|
|
slices.Sort(lines)
|
|
|
|
for _, currentLineNumber := range lines {
|
|
property := p.Properties[currentLineNumber]
|
|
var skipCheck = false
|
|
|
|
if p.Name != nil {
|
|
switch *p.Name {
|
|
case "Interface":
|
|
if _, found := interfaceAllowedDuplicateFields[property.Key.Name]; found {
|
|
skipCheck = true
|
|
}
|
|
case "Peer":
|
|
if _, found := peerAllowedDuplicateFields[property.Key.Name]; found {
|
|
skipCheck = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if skipCheck {
|
|
continue
|
|
}
|
|
|
|
if existingLineNumber, found := existingProperties[property.Key.Name]; found {
|
|
severity := protocol.DiagnosticSeverityError
|
|
diagnostics = append(diagnostics, protocol.Diagnostic{
|
|
Message: fmt.Sprintf("Property '%s' is already defined on line %d", property.Key.Name, existingLineNumber+1),
|
|
Severity: &severity,
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{
|
|
Line: currentLineNumber,
|
|
Character: 0,
|
|
},
|
|
End: protocol.Position{
|
|
Line: currentLineNumber,
|
|
Character: 99999,
|
|
},
|
|
},
|
|
})
|
|
} else {
|
|
existingProperties[property.Key.Name] = currentLineNumber
|
|
}
|
|
}
|
|
|
|
return diagnostics
|
|
}
|
|
|
|
func (p wireguardParser) analyzeAllowedIPIsInRange() []protocol.Diagnostic {
|
|
diagnostics := make([]protocol.Diagnostic, 0)
|
|
|
|
return diagnostics
|
|
}
|