Browse Source

Initial revision.

Bernd Fix 3 months ago
parent
commit
2cdf5497e4
1 changed files with 170 additions and 0 deletions
  1. 170
    0
      src/gnunet/service/gns/dns.go

+ 170
- 0
src/gnunet/service/gns/dns.go View File

@@ -0,0 +1,170 @@
1
+package gns
2
+
3
+import (
4
+	"fmt"
5
+	"net"
6
+	"strings"
7
+	"time"
8
+
9
+	"gnunet/enums"
10
+	"gnunet/message"
11
+	"gnunet/util"
12
+
13
+	"github.com/bfix/gospel/crypto/ed25519"
14
+	"github.com/bfix/gospel/logger"
15
+	"github.com/miekg/dns"
16
+)
17
+
18
+// Error codes
19
+var (
20
+	ErrDNSTimedOut  = fmt.Errorf("DNS query timed out")
21
+	ErrNoDNSQueries = fmt.Errorf("No valid DNS queries")
22
+	ErrNoDNSResults = fmt.Errorf("No valid DNS results")
23
+)
24
+
25
+// queryDNS resolves a name on a given nameserver and delivers all matching
26
+// resource record (of type 'kind') to the result channel.
27
+func queryDNS(id int, name string, server net.IP, kind int, res chan *GNSRecordSet) {
28
+	logger.Printf(logger.DBG, "[dns][%d] Starting query for '%s' on '%s'...", id, name, server.String())
29
+
30
+	// assemble query
31
+	m := &dns.Msg{
32
+		MsgHdr: dns.MsgHdr{
33
+			Authoritative:     true,
34
+			AuthenticatedData: false,
35
+			CheckingDisabled:  false,
36
+			RecursionDesired:  true,
37
+			Opcode:            dns.OpcodeQuery,
38
+		},
39
+		Question: make([]dns.Question, 1),
40
+	}
41
+	m.Question[0] = dns.Question{
42
+		dns.Fqdn(name),
43
+		dns.TypeANY,
44
+		dns.ClassINET,
45
+	}
46
+
47
+	// perform query in retry-loop
48
+	for retry := 0; retry < 5; retry++ {
49
+		// send query with new ID when retrying
50
+		m.Id = dns.Id()
51
+		in, err := dns.Exchange(m, net.JoinHostPort(server.String(), "53"))
52
+		// handle DNS fails
53
+		if err != nil {
54
+			errMsg := err.Error()
55
+			if strings.HasSuffix(errMsg, "i/o timeout") {
56
+				logger.Printf(logger.WARN, "[dns][%d] Query timed-out -- retrying (%d/5)", id, retry+1)
57
+				continue
58
+			}
59
+			logger.Printf(logger.ERROR, "[dns][%d] Error: %s", id, errMsg)
60
+			res <- nil
61
+		}
62
+		// process results
63
+		if in == nil {
64
+			logger.Printf(logger.ERROR, "[dns][%d] No results", id)
65
+			res <- nil
66
+			return
67
+		}
68
+		set := NewGNSRecordSet()
69
+		for _, record := range in.Answer {
70
+			// create a new GNS resource record
71
+			rr := new(message.GNSResourceRecord)
72
+			rr.Expires = util.AbsoluteTimeNever()
73
+			rr.Flags = 0
74
+			rr.Type = uint32(record.Header().Rrtype)
75
+			rr.Size = uint32(record.Header().Rdlength)
76
+			rr.Data = make([]byte, rr.Size)
77
+
78
+			// get wire-format of resource record
79
+			buf := make([]byte, 2048)
80
+			n, err := dns.PackRR(record, buf, 0, nil, false)
81
+			if err != nil {
82
+				logger.Printf(logger.WARN, "[dns][%d] Failed to get RR data for %s", id, err.Error())
83
+				continue
84
+			}
85
+			if n < int(rr.Size) {
86
+				logger.Printf(logger.WARN, "[dns][%d] Nit enough data in RR (%d != %d)", id, n, rr.Size)
87
+				continue
88
+			}
89
+			copy(rr.Data, buf[n-int(rr.Size):])
90
+			set.AddRecord(rr)
91
+		}
92
+		res <- set
93
+		return
94
+	}
95
+	logger.Printf(logger.WARN, "[dns][%d] Resolution failed -- giving up", id)
96
+	res <- nil
97
+}
98
+
99
+// ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in
100
+// parallel; the first result delivered by any of the servers is returned
101
+// as the result list of matching resource records.
102
+func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey *ed25519.PublicKey) (set *GNSRecordSet, err error) {
103
+	logger.Printf(logger.DBG, "[dns] Resolution of '%s' starting...", name)
104
+
105
+	// start DNS queries concurrently
106
+	res := make(chan *GNSRecordSet)
107
+	running := 0
108
+	for idx, srv := range servers {
109
+		// check if srv is an IPv4/IPv6 address
110
+		addr := net.ParseIP(srv)
111
+		if addr == nil {
112
+			// no; resolve server name in GNS
113
+			if strings.HasSuffix(srv, ".+") {
114
+				// resolve server name relative to current zone
115
+				zone := util.EncodeBinaryToString(pkey.Bytes())
116
+				srv = strings.TrimSuffix(srv, ".+")
117
+				set, err = gns.Resolve(srv, pkey, enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
118
+				if err != nil {
119
+					logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s' in '%s'", srv, zone)
120
+					continue
121
+				}
122
+			} else {
123
+				// resolve absolute GNS name (MUST end in a PKEY)
124
+				set, err = gns.Resolve(srv, nil, enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
125
+				if err != nil {
126
+					logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s'", srv)
127
+					continue
128
+				}
129
+			}
130
+			// traverse resource records for 'A' and 'AAAA' records.
131
+		rec_loop:
132
+			for _, rec := range set.Records {
133
+				switch int(rec.Type) {
134
+				case enums.GNS_TYPE_DNS_AAAA:
135
+					addr = net.IP(rec.Data)
136
+					break rec_loop
137
+				case enums.GNS_TYPE_DNS_A:
138
+					addr = net.IP(rec.Data)
139
+				}
140
+			}
141
+		}
142
+		// query DNS concurrently
143
+		go queryDNS(idx, name, addr, kind, res)
144
+		running++
145
+	}
146
+	// check if we started some queries at all.
147
+	if running == 0 {
148
+		return nil, ErrNoDNSQueries
149
+	}
150
+	// wait for query results
151
+	timeout := time.Tick(10 * time.Second)
152
+	for {
153
+		select {
154
+		case set = <-res:
155
+			running--
156
+			if set != nil {
157
+				// we have a result.
158
+				return
159
+			}
160
+			if running == 0 {
161
+				// no results
162
+				return nil, ErrNoDNSResults
163
+			}
164
+
165
+		case <-timeout:
166
+			// no results
167
+			return nil, ErrNoDNSResults
168
+		}
169
+	}
170
+}

Loading…
Cancel
Save