@@ -7,16 +7,32 @@ import (
77 "strings"
88
99 "github.com/jedisct1/dlog"
10+ "github.com/lifenjoiner/dhcpdns"
1011 "github.com/miekg/dns"
1112)
1213
13- type PluginForwardEntry struct {
14- domain string
14+ type SearchSequenceItemType int
15+
16+ const (
17+ Explicit SearchSequenceItemType = iota
18+ Bootstrap
19+ DHCP
20+ )
21+
22+ type SearchSequenceItem struct {
23+ typ SearchSequenceItemType
1524 servers []string
1625}
1726
27+ type PluginForwardEntry struct {
28+ domain string
29+ sequence []SearchSequenceItem
30+ }
31+
1832type PluginForward struct {
19- forwardMap []PluginForwardEntry
33+ forwardMap []PluginForwardEntry
34+ bootstrapResolvers []string
35+ dhcpdns []* dhcpdns.Detector
2036}
2137
2238func (plugin * PluginForward ) Name () string {
@@ -29,6 +45,11 @@ func (plugin *PluginForward) Description() string {
2945
3046func (plugin * PluginForward ) Init (proxy * Proxy ) error {
3147 dlog .Noticef ("Loading the set of forwarding rules from [%s]" , proxy .forwardFile )
48+
49+ if proxy .xTransport != nil {
50+ plugin .bootstrapResolvers = proxy .xTransport .bootstrapResolvers
51+ }
52+
3253 lines , err := ReadTextFile (proxy .forwardFile )
3354 if err != nil {
3455 return err
@@ -46,27 +67,77 @@ func (plugin *PluginForward) Init(proxy *Proxy) error {
4667 )
4768 }
4869 domain = strings .ToLower (domain )
49- var servers []string
70+ requiresDHCP := false
71+ var sequence []SearchSequenceItem
5072 for _ , server := range strings .Split (serversStr , "," ) {
5173 server = strings .TrimSpace (server )
52- server = strings .TrimPrefix (server , "[" )
53- server = strings .TrimSuffix (server , "]" )
54- if ip := net .ParseIP (server ); ip != nil {
55- if ip .To4 () != nil {
56- server = fmt .Sprintf ("%s:%d" , server , 53 )
74+ switch server {
75+ case "$BOOTSTRAP" :
76+ if len (plugin .bootstrapResolvers ) == 0 {
77+ return fmt .Errorf (
78+ "Syntax error for a forwarding rule at line %d. No bootstrap resolvers available" ,
79+ 1 + lineNo ,
80+ )
81+ }
82+ if len (sequence ) > 0 && sequence [len (sequence )- 1 ].typ == Bootstrap {
83+ // Ignore repetitions
5784 } else {
58- server = fmt .Sprintf ("[%s]:%d" , server , 53 )
85+ sequence = append (sequence , SearchSequenceItem {typ : Bootstrap })
86+ dlog .Infof ("Forwarding [%s] to the bootstrap servers" , domain )
5987 }
88+ case "$DHCP" :
89+ if len (sequence ) > 0 && sequence [len (sequence )- 1 ].typ == DHCP {
90+ // Ignore repetitions
91+ } else {
92+ sequence = append (sequence , SearchSequenceItem {typ : Bootstrap })
93+ dlog .Infof ("Forwarding [%s] to the DHCP servers" , domain )
94+ }
95+ requiresDHCP = true
96+ default :
97+ if strings .HasPrefix (server , "$" ) {
98+ dlog .Criticalf ("Unknown keyword [%s] at line %d" , server , 1 + lineNo )
99+ continue
100+ }
101+ server = strings .TrimPrefix (server , "[" )
102+ server = strings .TrimSuffix (server , "]" )
103+ if ip := net .ParseIP (server ); ip != nil {
104+ if ip .To4 () != nil {
105+ server = fmt .Sprintf ("%s:%d" , server , 53 )
106+ } else {
107+ server = fmt .Sprintf ("[%s]:%d" , server , 53 )
108+ }
109+ }
110+ idxServers := - 1
111+ for i , item := range sequence {
112+ if item .typ == Explicit {
113+ idxServers = i
114+ }
115+ }
116+ if idxServers == - 1 {
117+ sequence = append (sequence , SearchSequenceItem {typ : Explicit , servers : []string {server }})
118+ } else {
119+ sequence [idxServers ].servers = append (sequence [idxServers ].servers , server )
120+ }
121+ dlog .Infof ("Forwarding [%s] to [%s]" , domain , server )
60122 }
61- dlog .Infof ("Forwarding [%s] to %s" , domain , server )
62- servers = append (servers , server )
63123 }
64- if len (servers ) == 0 {
65- continue
124+ if requiresDHCP {
125+ if proxy .SourceIPv6 {
126+ dlog .Info ("Starting a DHCP/DNS detector for IPv6" )
127+ d6 := & dhcpdns.Detector {RemoteIPPort : "[2001:DB8::53]:80" }
128+ go d6 .Serve (9 , 10 )
129+ plugin .dhcpdns = append (plugin .dhcpdns , d6 )
130+ }
131+ if proxy .SourceIPv4 {
132+ dlog .Info ("Starting a DHCP/DNS detector for IPv4" )
133+ d4 := & dhcpdns.Detector {RemoteIPPort : "192.0.2.53:80" }
134+ go d4 .Serve (9 , 10 )
135+ plugin .dhcpdns = append (plugin .dhcpdns , d4 )
136+ }
66137 }
67138 plugin .forwardMap = append (plugin .forwardMap , PluginForwardEntry {
68- domain : domain ,
69- servers : servers ,
139+ domain : domain ,
140+ sequence : sequence ,
70141 })
71142 }
72143 return nil
@@ -83,7 +154,7 @@ func (plugin *PluginForward) Reload() error {
83154func (plugin * PluginForward ) Eval (pluginsState * PluginsState , msg * dns.Msg ) error {
84155 qName := pluginsState .qName
85156 qNameLen := len (qName )
86- var servers []string
157+ var sequence []SearchSequenceItem
87158 for _ , candidate := range plugin .forwardMap {
88159 candidateLen := len (candidate .domain )
89160 if candidateLen > qNameLen {
@@ -92,33 +163,78 @@ func (plugin *PluginForward) Eval(pluginsState *PluginsState, msg *dns.Msg) erro
92163 if (qName [qNameLen - candidateLen :] == candidate .domain &&
93164 (candidateLen == qNameLen || (qName [qNameLen - candidateLen - 1 ] == '.' ))) ||
94165 (candidate .domain == "." ) {
95- servers = candidate .servers
166+ sequence = candidate .sequence
96167 break
97168 }
98169 }
99- if len (servers ) == 0 {
170+ if len (sequence ) == 0 {
100171 return nil
101172 }
102- server := servers [rand .Intn (len (servers ))]
103- pluginsState .serverName = server
104- client := dns.Client {Net : pluginsState .serverProto , Timeout : pluginsState .timeout }
105- respMsg , _ , err := client .Exchange (msg , server )
106- if err != nil {
107- return err
108- }
109- if respMsg .Truncated {
110- client .Net = "tcp"
173+ var err error
174+ var respMsg * dns.Msg
175+ var tries = 4
176+ for _ , item := range sequence {
177+ var server string
178+ switch item .typ {
179+ case Explicit :
180+ server = item .servers [rand .Intn (len (item .servers ))]
181+ pluginsState .serverName = server
182+ case Bootstrap :
183+ server = plugin .bootstrapResolvers [rand .Intn (len (plugin .bootstrapResolvers ))]
184+ pluginsState .serverName = "[BOOTSTRAP]"
185+ case DHCP :
186+ const maxInconsistency = 9
187+ for _ , dhcpdns := range plugin .dhcpdns {
188+ inconsistency , ip , dhcpDNS , err := dhcpdns .Status ()
189+ if err != nil && ip != "" && inconsistency > maxInconsistency {
190+ dhcpDNS = nil
191+ }
192+ if len (dhcpDNS ) > 0 {
193+ server = net .JoinHostPort (dhcpDNS [rand .Intn (len (dhcpDNS ))].String (), "53" )
194+ break
195+ }
196+ }
197+ if len (server ) == 0 {
198+ dlog .Warn ("DHCP didn't provide any DNS server" )
199+ continue
200+ }
201+ pluginsState .serverName = "[DHCP]"
202+ }
203+ if len (server ) == 0 {
204+ continue
205+ }
206+
207+ if tries == 0 {
208+ break
209+ }
210+ tries --
211+ dlog .Debugf ("Forwarding [%s] to [%s]" , qName , server )
212+ client := dns.Client {Net : pluginsState .serverProto , Timeout : pluginsState .timeout }
111213 respMsg , _ , err = client .Exchange (msg , server )
112214 if err != nil {
113- return err
215+ continue
114216 }
217+ if respMsg .Truncated {
218+ client .Net = "tcp"
219+ respMsg , _ , err = client .Exchange (msg , server )
220+ if err != nil {
221+ continue
222+ }
223+ }
224+ if len (sequence ) > 0 {
225+ switch respMsg .Rcode {
226+ case dns .RcodeNameError , dns .RcodeRefused , dns .RcodeNotAuth :
227+ continue
228+ }
229+ }
230+ if edns0 := respMsg .IsEdns0 (); edns0 == nil || ! edns0 .Do () {
231+ respMsg .AuthenticatedData = false
232+ }
233+ respMsg .Id = msg .Id
234+ pluginsState .synthResponse = respMsg
235+ pluginsState .action = PluginsActionSynth
236+ pluginsState .returnCode = PluginsReturnCodeForward
237+ return nil
115238 }
116- if edns0 := respMsg .IsEdns0 (); edns0 == nil || ! edns0 .Do () {
117- respMsg .AuthenticatedData = false
118- }
119- respMsg .Id = msg .Id
120- pluginsState .synthResponse = respMsg
121- pluginsState .action = PluginsActionSynth
122- pluginsState .returnCode = PluginsReturnCodeForward
123- return nil
239+ return err
124240}
0 commit comments