Skip to content

Commit 869edd8

Browse files
feat: adding ttl support
1 parent 7881355 commit 869edd8

File tree

7 files changed

+222
-111
lines changed

7 files changed

+222
-111
lines changed

README.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,8 @@ import {
4646
"time"
4747
}
4848

49-
const dnsServerAddress = "1.1.1.1:53"
50-
dns := &net.Resolver{
51-
PreferGo: true,
52-
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
53-
d := net.Dialer{
54-
Timeout: time.Millisecond * time.Duration(10000),
55-
}
56-
return d.DialContext(ctx, network, dnsServerAddress)
57-
},
58-
}
5949
resolver := &dnslink.Resolver{
60-
LookupTXT: func(name string) (txt []string, err error) {
61-
return dns.LookupTXT(context.Background(), name)
62-
},
50+
LookupTXT: NewUDPLookup("1.1.1.1:53"),
6351
}
6452

6553
// The resolver will now use googles 1.1.1.1 dns server.

dnslink.go

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package dnslink
22

33
import (
4+
"context"
45
"encoding/json"
6+
"math/rand"
57
"net"
68
"net/url"
79
"sort"
810
"strings"
911

1012
isd "github.com/jbenet/go-is-domain"
13+
dns "github.com/miekg/dns"
1114
)
1215

1316
type PathEntry struct {
@@ -77,9 +80,9 @@ func (url *URLParts) MarshalJSON() ([]byte, error) {
7780
}
7881

7982
type Result struct {
80-
Links map[string][]string `json:"links"`
81-
Path []PathEntry `json:"path"`
82-
Log []LogStatement `json:"log"`
83+
Links map[string][]LookupEntry `json:"links"`
84+
Path []PathEntry `json:"path"`
85+
Log []LogStatement `json:"log"`
8386
}
8487

8588
type Resolver struct {
@@ -94,7 +97,54 @@ func (r *Resolver) ResolveN(domain string) (Result, error) {
9497
return resolve(r, domain, true)
9598
}
9699

97-
type LookupTXTFunc func(name string) (txt []string, err error)
100+
type LookupEntry struct {
101+
Value string `json:"value"`
102+
Ttl uint32 `json:"ttl"`
103+
}
104+
type LookupEntries []LookupEntry
105+
106+
func (l LookupEntries) Len() int { return len(l) }
107+
func (l LookupEntries) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
108+
109+
type ByValue struct{ LookupEntries }
110+
111+
func (s ByValue) Less(i, j int) bool { return s.LookupEntries[i].Value < s.LookupEntries[j].Value }
112+
113+
type LookupTXTFunc func(name string) (txt []LookupEntry, err error)
114+
115+
func NewUDPLookup(servers []string) LookupTXTFunc {
116+
return func(domain string) (entries []LookupEntry, err error) {
117+
if !strings.HasSuffix(domain, ".") {
118+
domain += "."
119+
}
120+
req := new(dns.Msg)
121+
req.Id = dns.Id()
122+
req.RecursionDesired = true
123+
req.Question = make([]dns.Question, 1)
124+
req.Question[0] = dns.Question{
125+
Name: domain,
126+
Qtype: dns.TypeTXT,
127+
Qclass: dns.ClassINET,
128+
}
129+
server := servers[rand.Intn(len(servers))]
130+
res, err := dns.Exchange(req, server)
131+
if err != nil {
132+
return nil, err
133+
}
134+
entries = make([]LookupEntry, len(res.Answer))
135+
for index, answer := range res.Answer {
136+
if answer.Header().Rrtype == dns.TypeTXT {
137+
txtAnswer := answer.(*dns.TXT)
138+
entries[index] = LookupEntry{
139+
Value: strings.Join(txtAnswer.Txt, ""),
140+
Ttl: txtAnswer.Header().Ttl,
141+
}
142+
}
143+
}
144+
sort.Sort(ByValue{entries})
145+
return entries, nil
146+
}
147+
}
98148

99149
const Version = "v0.0.1"
100150
const dnsPrefix = "_dnslink."
@@ -110,13 +160,33 @@ func ResolveN(domain string) (Result, error) {
110160
return defaultResolver.ResolveN(domain)
111161
}
112162

163+
func wrapLookup(r *net.Resolver, ttl uint32) LookupTXTFunc {
164+
return func(domain string) (res []LookupEntry, err error) {
165+
txt, err := r.LookupTXT(context.Background(), domain)
166+
if err != nil {
167+
return nil, err
168+
}
169+
res = make([]LookupEntry, len(txt))
170+
for index, txt := range txt {
171+
res[index] = LookupEntry{
172+
Value: txt,
173+
// net.LookupTXT doesn't support ttl :-(
174+
Ttl: ttl,
175+
}
176+
}
177+
return res, nil
178+
}
179+
}
180+
181+
var defaultLookupTXT = wrapLookup(net.DefaultResolver, 0)
182+
113183
func resolve(r *Resolver, domain string, recursive bool) (result Result, err error) {
114184
lookupTXT := r.LookupTXT
115185
if lookupTXT == nil {
116-
lookupTXT = net.LookupTXT
186+
lookupTXT = defaultLookupTXT
117187
}
118188
lookup, error := validateDomain(domain)
119-
result.Links = map[string][]string{}
189+
result.Links = map[string][]LookupEntry{}
120190
result.Path = []PathEntry{}
121191
result.Log = []LogStatement{}[:]
122192
if lookup == nil {
@@ -189,6 +259,7 @@ func validateDomain(input string) (*URLParts, *LogStatement) {
189259
type processedEntry struct {
190260
value string
191261
entry string
262+
ttl uint32
192263
}
193264

194265
func relevantURLParts(input string) URLParts {
@@ -248,8 +319,8 @@ func getPathFromLog(log []LogStatement) []PathEntry {
248319
return path
249320
}
250321

251-
func resolveTxtEntries(domain string, recursive bool, txtEntries []string) (links map[string][]string, log []LogStatement, redirect *URLParts) {
252-
links = make(map[string][]string)
322+
func resolveTxtEntries(domain string, recursive bool, txtEntries []LookupEntry) (links map[string][]LookupEntry, log []LogStatement, redirect *URLParts) {
323+
links = make(map[string][]LookupEntry)
253324
log = []LogStatement{}[:]
254325
if !hasDNSLinkEntry(txtEntries) && strings.HasPrefix(domain, dnsPrefix) {
255326
return links, log, &URLParts{Domain: domain[len(dnsPrefix):]}
@@ -290,40 +361,43 @@ func resolveTxtEntries(domain string, recursive bool, txtEntries []string) (link
290361
}
291362
}
292363
for key, foundEntries := range found {
293-
list := []string{}[:]
364+
list := []LookupEntry{}[:]
294365
for _, foundEntry := range foundEntries {
295-
list = append(list, foundEntry.value)
366+
list = append(list, LookupEntry{
367+
Value: foundEntry.value,
368+
Ttl: foundEntry.ttl,
369+
})
296370
}
297-
sort.Strings(list)
371+
sort.Sort(ByValue{list})
298372
links[key] = list
299373
}
300374
return links, log, nil
301375
}
302376

303-
func hasDNSLinkEntry(txtEntries []string) bool {
377+
func hasDNSLinkEntry(txtEntries []LookupEntry) bool {
304378
for _, txtEntry := range txtEntries {
305-
if strings.HasPrefix(txtEntry, txtPrefix) {
379+
if strings.HasPrefix(txtEntry.Value, txtPrefix) {
306380
return true
307381
}
308382
}
309383
return false
310384
}
311385

312-
func processEntries(dnslinkEntries []string) (map[string][]processedEntry, []LogStatement) {
386+
func processEntries(dnslinkEntries []LookupEntry) (map[string][]processedEntry, []LogStatement) {
313387
log := []LogStatement{}[:]
314388
found := make(map[string][]processedEntry)
315389
for _, entry := range dnslinkEntries {
316-
if !strings.HasPrefix(entry, txtPrefix) {
390+
if !strings.HasPrefix(entry.Value, txtPrefix) {
317391
continue
318392
}
319-
key, value, error := validateDNSLinkEntry(entry)
393+
key, value, error := validateDNSLinkEntry(entry.Value)
320394

321395
if error != "" {
322-
log = append(log, LogStatement{Code: "INVALID_ENTRY", Entry: entry, Reason: error})
396+
log = append(log, LogStatement{Code: "INVALID_ENTRY", Entry: entry.Value, Reason: error})
323397
continue
324398
}
325399
list, hasList := found[key]
326-
processed := processedEntry{value, entry}
400+
processed := processedEntry{value, entry.Value, entry.Ttl}
327401
if !hasList {
328402
found[key] = []processedEntry{processed}
329403
} else {

dnslink/main.go

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
package main
22

33
import (
4-
"context"
54
"encoding/json"
65
"fmt"
76
"log"
8-
"math/rand"
9-
"net"
107
"net/url"
118
"os"
129
"strings"
13-
"time"
1410

1511
dnslink "github.com/dnslink-std/go"
1612
)
@@ -142,9 +138,11 @@ func (write *WriteTXT) write(lookup string, result dnslink.Result) {
142138
prefix = lookup + ": "
143139
}
144140
for key, values := range result.Links {
145-
for _, value := range values {
141+
for _, entry := range values {
142+
value := entry.Value
143+
value += " [ttl=" + fmt.Sprint(entry.Ttl) + "]"
146144
for _, part := range result.Path {
147-
value += " [" + renderPath(part) + "]"
145+
value += " [path=" + renderPath(part) + "]"
148146
}
149147

150148
if write.options.searchKey != false {
@@ -228,14 +226,14 @@ func (write *WriteCSV) write(lookup string, result dnslink.Result) {
228226
err := write.options.err
229227
if write.firstOut {
230228
write.firstOut = false
231-
out.Println("lookup,key,value,path")
229+
out.Println("lookup,key,value,ttl,path")
232230
}
233231
for key, values := range result.Links {
234232
for _, value := range values {
235233
if write.options.searchKey != false && write.options.searchKey != key {
236234
continue
237235
}
238-
out.Println(csv(lookup, key, value, renderPaths(result.Path)))
236+
out.Println(csv(lookup, key, value.Value, value.Ttl, renderPaths(result.Path)))
239237
}
240238
}
241239
if write.options.debug {
@@ -256,6 +254,7 @@ func csv(rest ...interface{}) string {
256254
value := ""
257255
switch v := entry.(type) {
258256
case int:
257+
case uint32:
259258
value = fmt.Sprint(v)
260259
case bool:
261260
if v {
@@ -322,7 +321,7 @@ func main() {
322321
}
323322
resolver := dnslink.Resolver{}
324323
if options.has("dns") {
325-
resolver.LookupTXT = getLookup(options.get("dns"))
324+
resolver.LookupTXT = dnslink.NewUDPLookup(getServers(options.get("dns")))
326325
}
327326
for _, lookup := range lookups {
328327
var result dnslink.Result
@@ -340,23 +339,6 @@ func main() {
340339
output.end()
341340
}
342341

343-
func getLookup(raw []interface{}) dnslink.LookupTXTFunc {
344-
servers := getServers(raw)
345-
dns := &net.Resolver{
346-
PreferGo: true,
347-
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
348-
d := net.Dialer{
349-
Timeout: time.Millisecond * time.Duration(10000),
350-
}
351-
server := servers[rand.Intn(len(servers))]
352-
return d.DialContext(ctx, network, server)
353-
},
354-
}
355-
return func(domain string) (txt []string, err error) {
356-
return dns.LookupTXT(context.Background(), domain)
357-
}
358-
}
359-
360342
func getServers(raw []interface{}) []string {
361343
servers := []string{}
362344
for _, entry := range raw {

0 commit comments

Comments
 (0)