| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | package proxy | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-02-11 16:56:04 +00:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	"net" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-21 22:51:47 -08:00
										 |  |  | 	"github.com/coredns/coredns/request" | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/miekg/dns" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | type dnsEx struct { | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	Timeout time.Duration | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | 	Options | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-14 09:37:10 -07:00
										 |  |  | // Options define the options understood by dns.Exchange. | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | type Options struct { | 
					
						
							|  |  |  | 	ForceTCP bool // If true use TCP for upstream no matter what | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-06 19:32:48 +00:00
										 |  |  | func newDNSEx() *dnsEx { | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | 	return newDNSExWithOption(Options{}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newDNSExWithOption(opt Options) *dnsEx { | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | 	return &dnsEx{Timeout: defaultTimeout * time.Second, Options: opt} | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 12:41:41 +02:00
										 |  |  | func (d *dnsEx) Transport() string { | 
					
						
							|  |  |  | 	if d.Options.ForceTCP { | 
					
						
							|  |  |  | 		return "tcp" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The protocol will be determined by `state.Proto()` during Exchange. | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-22 07:25:58 +00:00
										 |  |  | func (d *dnsEx) Protocol() string          { return "dns" } | 
					
						
							| 
									
										
										
										
											2017-02-06 19:32:48 +00:00
										 |  |  | func (d *dnsEx) OnShutdown(p *Proxy) error { return nil } | 
					
						
							|  |  |  | func (d *dnsEx) OnStartup(p *Proxy) error  { return nil } | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Exchange implements the Exchanger interface. | 
					
						
							| 
									
										
										
										
											2017-02-11 16:56:04 +00:00
										 |  |  | func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | 	proto := state.Proto() | 
					
						
							|  |  |  | 	if d.Options.ForceTCP { | 
					
						
							|  |  |  | 		proto = "tcp" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	co, err := net.DialTimeout(proto, addr, d.Timeout) | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 	reply, _, err := d.ExchangeConn(state.Req, co) | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	co.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if reply != nil && reply.Truncated { | 
					
						
							|  |  |  | 		// Suppress proxy error for truncated responses | 
					
						
							|  |  |  | 		err = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | 	// Make sure it fits in the DNS response. | 
					
						
							|  |  |  | 	reply, _ = state.Scrub(reply) | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	reply.Compress = true | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 	reply.Id = state.Req.Id | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return reply, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | func (d *dnsEx) ExchangeConn(m *dns.Msg, co net.Conn) (*dns.Msg, time.Duration, error) { | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	start := time.Now() | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | 	r, err := exchange(m, co) | 
					
						
							| 
									
										
										
										
											2016-11-23 19:04:37 +00:00
										 |  |  | 	rtt := time.Since(start) | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return r, rtt, err | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | func exchange(m *dns.Msg, co net.Conn) (*dns.Msg, error) { | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	opt := m.IsEdns0() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	udpsize := uint16(dns.MinMsgSize) | 
					
						
							|  |  |  | 	// If EDNS0 is used use that for size. | 
					
						
							|  |  |  | 	if opt != nil && opt.UDPSize() >= dns.MinMsgSize { | 
					
						
							|  |  |  | 		udpsize = opt.UDPSize() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dnsco := &dns.Conn{Conn: co, UDPSize: udpsize} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-11 21:23:57 +00:00
										 |  |  | 	writeDeadline := time.Now().Add(defaultTimeout) | 
					
						
							|  |  |  | 	dnsco.SetWriteDeadline(writeDeadline) | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	dnsco.WriteMsg(m) | 
					
						
							| 
									
										
										
										
											2017-01-11 21:23:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	readDeadline := time.Now().Add(defaultTimeout) | 
					
						
							|  |  |  | 	co.SetReadDeadline(readDeadline) | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	r, err := dnsco.ReadMsg() | 
					
						
							| 
									
										
										
										
											2017-01-11 21:23:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	dnsco.Close() | 
					
						
							|  |  |  | 	if r == nil { | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-08 16:06:14 +01:00
										 |  |  | 	return r, err | 
					
						
							| 
									
										
										
										
											2016-10-08 14:46:22 +01:00
										 |  |  | } |