feat(lns): parse dns= records from alias comments as fallback

- When HSD sidechain returns null for a name, fall back to parsing
  dns= field from the main chain alias comment
- Format: dns=A:@:1.2.3.4,CNAME:www:lthn.io,TXT:@:value
- ParseDNSFromComment extracts records and adds to cache
- Names registered via Blesta now resolve through LNS
- Always sync (removed tree root skip) to catch new alias comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-04 02:37:13 +01:00
parent f917d3e994
commit 5b4d199442
No known key found for this signature in database
GPG key ID: AF404715446AEB41
3 changed files with 117 additions and 5 deletions

View file

@ -98,6 +98,79 @@ func getChainAliases() []ChainAlias {
return aliases
}
// ParseDNSFromComment extracts DNS records from an alias comment's dns= field.
// Format: dns=A:@:1.2.3.4,AAAA:@:2001:db8::1,CNAME:www:lthn.io
//
// records := ParseDNSFromComment("v=lthn1;type=user;dns=A:@:10.69.69.165,TXT:@:hello")
func ParseDNSFromComment(comment string) *NameRecord {
parsed := parseAliasComment(comment)
dnsField, hasDNS := parsed["dns"]
if !hasDNS || dnsField == "" {
return nil
}
record := &NameRecord{}
entries := splitComma(dnsField)
for _, entry := range entries {
parts := splitColon(entry, 3)
if len(parts) < 3 {
continue
}
recordType := parts[0]
value := parts[2]
switch recordType {
case "A":
record.A = append(record.A, value)
case "AAAA":
record.AAAA = append(record.AAAA, value)
case "CNAME":
record.A = append(record.A, value) // CNAME stored as A for resolution
case "TXT":
record.TXT = append(record.TXT, value)
case "NS":
record.NS = append(record.NS, value)
}
}
if len(record.A) == 0 && len(record.AAAA) == 0 && len(record.TXT) == 0 && len(record.NS) == 0 {
return nil
}
return record
}
func splitComma(input string) []string {
parts := make([]string, 0)
current := ""
for _, character := range input {
if character == ',' {
if current != "" {
parts = append(parts, current)
}
current = ""
} else {
current += string(character)
}
}
if current != "" {
parts = append(parts, current)
}
return parts
}
func splitColon(input string, maxParts int) []string {
parts := make([]string, 0, maxParts)
current := ""
for _, character := range input {
if character == ':' && len(parts) < maxParts-1 {
parts = append(parts, current)
current = ""
} else {
current += string(character)
}
}
parts = append(parts, current)
return parts
}
// parseAliasComment extracts key=value pairs from a v=lthn1 alias comment.
//
// parseAliasComment("v=lthn1;type=gateway;cap=vpn,dns")

View file

@ -121,18 +121,57 @@ var knownNames = []string{
func syncCache() {
root := getTreeRoot()
if root == lastRoot && root != "" {
return
}
// Discover names from the main chain, fall back to hardcoded list.
names := discoverNames()
aliases := getChainAliases()
names := make([]string, 0)
aliasMap := make(map[string]ChainAlias)
core.Println(core.Sprintf("[%s] Tree root changed — syncing %d names", time.Now().Format("15:04:05"), len(names)))
if len(aliases) > 0 {
seen := make(map[string]bool)
for _, alias := range aliases {
hnsName := alias.Name
comment := parseAliasComment(alias.Comment)
if hns, ok := comment["hns"]; ok && hns != "" {
stripped := core.TrimSuffix(hns, ".lthn")
if stripped != "" {
hnsName = stripped
}
}
if !seen[hnsName] {
seen[hnsName] = true
names = append(names, hnsName)
aliasMap[hnsName] = alias
}
}
} else {
names = knownNames
}
dnsCount := 0
for _, a := range aliases { if core.Contains(a.Comment, "dns=") { dnsCount++ } }
core.Println(core.Sprintf("[%s] Syncing %d names (%d with dns=, %d in aliasMap)", time.Now().Format("15:04:05"), len(names), dnsCount, len(aliasMap)))
for _, name := range names {
// Try HSD sidechain first
record := getNameRecords(name)
if record != nil {
cache[name] = record
continue
}
// Fall back to dns= entries in alias comment
if alias, exists := aliasMap[name]; exists && core.Contains(alias.Comment, "dns=") {
record = ParseDNSFromComment(alias.Comment)
core.Println(core.Sprintf("[dns=] %s comment=%s record=%v", name, alias.Comment, record != nil))
if record != nil {
record.Name = name
// Add the TXT metadata from the comment itself
comment := alias.Comment
if comment != "" && len(record.TXT) == 0 {
record.TXT = append(record.TXT, comment)
}
cache[name] = record
}
}
}
lastRoot = root

BIN
lns

Binary file not shown.