mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	Pr 586 tweaks (#594)
* add proxy tcp * add truncated for tcp to udp response * move truncation to scrubbing * add test that executes upstream over tcp * middleware/proxy: some tweaks rename force-tcp to force_tcp to be inline with the rest and use a dnsOptions struct to put the options in to allow it to be extended. Add some parse tests as well. * Fix test and rename dnsOptions Options
This commit is contained in:
		| @@ -26,7 +26,7 @@ proxy FROM TO... { | |||||||
|     health_check PATH:PORT [DURATION] |     health_check PATH:PORT [DURATION] | ||||||
|     except IGNORED_NAMES... |     except IGNORED_NAMES... | ||||||
|     spray |     spray | ||||||
|     protocol [dns|https_google [bootstrap ADDRESS...]|grpc [insecure|CA-PEM|KEY-PEM CERT-PEM|KEY-PEM CERT-PEM CA-PEM]] |     protocol [dns [force_tcp]|https_google [bootstrap ADDRESS...]|grpc [insecure|CA-PEM|KEY-PEM CERT-PEM|KEY-PEM CERT-PEM CA-PEM]] | ||||||
| } | } | ||||||
| ~~~ | ~~~ | ||||||
|  |  | ||||||
| @@ -71,7 +71,8 @@ Currently `protocol` supports `dns` (i.e., standard DNS over UDP/TCP) and `https | |||||||
| payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and | payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and | ||||||
| *Google* can see your DNS activity. | *Google* can see your DNS activity. | ||||||
|  |  | ||||||
| * `dns`: no options can be given at the moment. | * `dns`: uses the standard DNS exchange. You can pass `force_tcp` to make sure that the proxied connection is performed | ||||||
|  |   over TCP, regardless of the inbound request's protocol. | ||||||
| * `https_google`: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com` to an address to | * `https_google`: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com` to an address to | ||||||
|   connect to. This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53. |   connect to. This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53. | ||||||
|   Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as |   Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as | ||||||
|   | |||||||
| @@ -14,10 +14,19 @@ import ( | |||||||
| type dnsEx struct { | type dnsEx struct { | ||||||
| 	Timeout time.Duration | 	Timeout time.Duration | ||||||
| 	group   *singleflight.Group | 	group   *singleflight.Group | ||||||
|  | 	Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Options struct { | ||||||
|  | 	ForceTCP bool // If true use TCP for upstream no matter what | ||||||
| } | } | ||||||
|  |  | ||||||
| func newDNSEx() *dnsEx { | func newDNSEx() *dnsEx { | ||||||
| 	return &dnsEx{group: new(singleflight.Group), Timeout: defaultTimeout * time.Second} | 	return newDNSExWithOption(Options{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newDNSExWithOption(opt Options) *dnsEx { | ||||||
|  | 	return &dnsEx{group: new(singleflight.Group), Timeout: defaultTimeout * time.Second, Options: opt} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (d *dnsEx) Protocol() string          { return "dns" } | func (d *dnsEx) Protocol() string          { return "dns" } | ||||||
| @@ -26,7 +35,11 @@ func (d *dnsEx) OnStartup(p *Proxy) error  { return nil } | |||||||
|  |  | ||||||
| // Exchange implements the Exchanger interface. | // Exchange implements the Exchanger interface. | ||||||
| func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { | func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { | ||||||
| 	co, err := net.DialTimeout(state.Proto(), addr, d.Timeout) | 	proto := state.Proto() | ||||||
|  | 	if d.Options.ForceTCP { | ||||||
|  | 		proto = "tcp" | ||||||
|  | 	} | ||||||
|  | 	co, err := net.DialTimeout(proto, addr, d.Timeout) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -43,7 +56,8 @@ func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | 	// Make sure it fits in the DNS response. | ||||||
|  | 	reply, _ = state.Scrub(reply) | ||||||
| 	reply.Compress = true | 	reply.Compress = true | ||||||
| 	reply.Id = state.Req.Id | 	reply.Id = state.Req.Id | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,11 @@ import ( | |||||||
|  |  | ||||||
| // NewLookup create a new proxy with the hosts in host and a Random policy. | // NewLookup create a new proxy with the hosts in host and a Random policy. | ||||||
| func NewLookup(hosts []string) Proxy { | func NewLookup(hosts []string) Proxy { | ||||||
|  | 	return NewLookupWithOption(hosts, Options{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewLookupWithForcedProto process creates a simple round robin forward with potentially forced proto for upstream. | ||||||
|  | func NewLookupWithOption(hosts []string, opts Options) Proxy { | ||||||
| 	p := Proxy{Next: nil} | 	p := Proxy{Next: nil} | ||||||
|  |  | ||||||
| 	upstream := &staticUpstream{ | 	upstream := &staticUpstream{ | ||||||
| @@ -23,7 +28,7 @@ func NewLookup(hosts []string) Proxy { | |||||||
| 		Spray:       nil, | 		Spray:       nil, | ||||||
| 		FailTimeout: 10 * time.Second, | 		FailTimeout: 10 * time.Second, | ||||||
| 		MaxFails:    3, // TODO(miek): disable error checking for simple lookups? | 		MaxFails:    3, // TODO(miek): disable error checking for simple lookups? | ||||||
| 		ex:          newDNSEx(), | 		ex:          newDNSExWithOption(opts), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i, host := range hosts { | 	for i, host := range hosts { | ||||||
|   | |||||||
| @@ -190,7 +190,16 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { | |||||||
| 		} | 		} | ||||||
| 		switch encArgs[0] { | 		switch encArgs[0] { | ||||||
| 		case "dns": | 		case "dns": | ||||||
| 			u.ex = newDNSEx() | 			if len(encArgs) > 1 { | ||||||
|  | 				if encArgs[1] == "force_tcp" { | ||||||
|  | 					opts := Options{ForceTCP: true} | ||||||
|  | 					u.ex = newDNSExWithOption(opts) | ||||||
|  | 				} else { | ||||||
|  | 					return fmt.Errorf("only force_tcp allowed as parameter to dns") | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				u.ex = newDNSEx() | ||||||
|  | 			} | ||||||
| 		case "https_google": | 		case "https_google": | ||||||
| 			boot := []string{"8.8.8.8:53", "8.8.4.4:53"} | 			boot := []string{"8.8.8.8:53", "8.8.4.4:53"} | ||||||
| 			if len(encArgs) > 2 && encArgs[1] == "bootstrap" { | 			if len(encArgs) > 2 && encArgs[1] == "bootstrap" { | ||||||
|   | |||||||
| @@ -198,6 +198,13 @@ proxy . 8.8.8.8:53 { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			` | 			` | ||||||
|  | proxy . 8.8.8.8:53 { | ||||||
|  | 	protocol dns force_tcp | ||||||
|  | }`, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			` | ||||||
| proxy . 8.8.8.8:53 { | proxy . 8.8.8.8:53 { | ||||||
| 	protocol grpc a b c d | 	protocol grpc a b c d | ||||||
| }`, | }`, | ||||||
| @@ -262,6 +269,13 @@ proxy . 8.8.8.8:53 { | |||||||
| 			` | 			` | ||||||
| proxy . 8.8.8.8:53 { | proxy . 8.8.8.8:53 { | ||||||
| 	health_check | 	health_check | ||||||
|  | }`, | ||||||
|  | 			true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			` | ||||||
|  | proxy . 8.8.8.8:53 { | ||||||
|  | 	protocol dns force | ||||||
| }`, | }`, | ||||||
| 			true, | 			true, | ||||||
| 		}, | 		}, | ||||||
|   | |||||||
| @@ -56,6 +56,50 @@ func TestLookupProxy(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestLookupDnsWithForcedTcp(t *testing.T) { | ||||||
|  | 	t.Parallel() | ||||||
|  | 	name, rm, err := test.TempFile(".", exampleOrg) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("failed to create zone: %s", err) | ||||||
|  | 	} | ||||||
|  | 	defer rm() | ||||||
|  |  | ||||||
|  | 	corefile := `example.org:0 { | ||||||
|  |        file ` + name + ` | ||||||
|  | } | ||||||
|  | ` | ||||||
|  |  | ||||||
|  | 	i, err := CoreDNSServer(corefile) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Could not get CoreDNS serving instance: %s", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, tcp := CoreDNSServerPorts(i, 0) | ||||||
|  | 	if tcp == "" { | ||||||
|  | 		t.Fatalf("Could not get TCP listening port") | ||||||
|  | 	} | ||||||
|  | 	defer i.Stop() | ||||||
|  |  | ||||||
|  | 	log.SetOutput(ioutil.Discard) | ||||||
|  |  | ||||||
|  | 	p := proxy.NewLookupWithOption([]string{tcp}, proxy.Options{ForceTCP: true}) | ||||||
|  | 	state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)} | ||||||
|  | 	resp, err := p.Lookup(state, "example.org.", dns.TypeA) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal("Expected to receive reply, but didn't") | ||||||
|  | 	} | ||||||
|  | 	// expect answer section with A record in it | ||||||
|  | 	if len(resp.Answer) == 0 { | ||||||
|  | 		t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp) | ||||||
|  | 	} | ||||||
|  | 	if resp.Answer[0].Header().Rrtype != dns.TypeA { | ||||||
|  | 		t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype) | ||||||
|  | 	} | ||||||
|  | 	if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" { | ||||||
|  | 		t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func BenchmarkLookupProxy(b *testing.B) { | func BenchmarkLookupProxy(b *testing.B) { | ||||||
| 	t := new(testing.T) | 	t := new(testing.T) | ||||||
| 	name, rm, err := test.TempFile(".", exampleOrg) | 	name, rm, err := test.TempFile(".", exampleOrg) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user