mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	plugin/metadata: add metadata plugin (#1894)
* plugin/metadata: add metadata plugin * plugin/metadata: Add MD struct, refactor code, fix doc * plugin/metadata: simplify metadata key * plugin/metadata: improve setup_test * Support of metadata by rewrite plugin. Move calculated variables to metadata. * Move variables from metadata to pkg, add UTs, READMEs change, metadata small fixes * Add client port validation to variables_test * plugin/metadata: improve README * plugin/metadata: rename methods * plugin/metadata: Update Metadataer interface, update doc, cosmetic code changes * plugin/metadata: move colllisions check to OnStartup(). Fix default variables metadataer. * plugin/metadata: Fix comment for method setValue * plugin/metadata: change variables order to fix linter warning * plugin/metadata: rename Metadataer to Provider
This commit is contained in:
		
				
					committed by
					
						 Miek Gieben
						Miek Gieben
					
				
			
			
				
	
			
			
			
						parent
						
							dae506b563
						
					
				
				
					commit
					17d807f05f
				
			
							
								
								
									
										109
									
								
								plugin/pkg/variables/variables.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								plugin/pkg/variables/variables.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| package variables | ||||
|  | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/coredns/coredns/request" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	queryName  = "qname" | ||||
| 	queryType  = "qtype" | ||||
| 	clientIP   = "client_ip" | ||||
| 	clientPort = "client_port" | ||||
| 	protocol   = "protocol" | ||||
| 	serverIP   = "server_ip" | ||||
| 	serverPort = "server_port" | ||||
| ) | ||||
|  | ||||
| // All is a list of available variables provided by GetMetadataValue | ||||
| var All = []string{queryName, queryType, clientIP, clientPort, protocol, serverIP, serverPort} | ||||
|  | ||||
| // GetValue calculates and returns the data specified by the variable name. | ||||
| // Supported varNames are listed in allProvidedVars. | ||||
| func GetValue(varName string, w dns.ResponseWriter, r *dns.Msg) ([]byte, error) { | ||||
| 	req := request.Request{W: w, Req: r} | ||||
| 	switch varName { | ||||
| 	case queryName: | ||||
| 		//Query name is written as ascii string | ||||
| 		return []byte(req.QName()), nil | ||||
|  | ||||
| 	case queryType: | ||||
| 		return uint16ToWire(req.QType()), nil | ||||
|  | ||||
| 	case clientIP: | ||||
| 		return ipToWire(req.Family(), req.IP()) | ||||
|  | ||||
| 	case clientPort: | ||||
| 		return portToWire(req.Port()) | ||||
|  | ||||
| 	case protocol: | ||||
| 		// Proto is written as ascii string | ||||
| 		return []byte(req.Proto()), nil | ||||
|  | ||||
| 	case serverIP: | ||||
| 		ip, _, err := net.SplitHostPort(w.LocalAddr().String()) | ||||
| 		if err != nil { | ||||
| 			ip = w.RemoteAddr().String() | ||||
| 		} | ||||
| 		return ipToWire(family(w.RemoteAddr()), ip) | ||||
|  | ||||
| 	case serverPort: | ||||
| 		_, port, err := net.SplitHostPort(w.LocalAddr().String()) | ||||
| 		if err != nil { | ||||
| 			port = "0" | ||||
| 		} | ||||
| 		return portToWire(port) | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("unable to extract data for variable %s", varName) | ||||
| } | ||||
|  | ||||
| // uint16ToWire writes unit16 to wire/binary format | ||||
| func uint16ToWire(data uint16) []byte { | ||||
| 	buf := make([]byte, 2) | ||||
| 	binary.BigEndian.PutUint16(buf, uint16(data)) | ||||
| 	return buf | ||||
| } | ||||
|  | ||||
| // ipToWire writes IP address to wire/binary format, 4 or 16 bytes depends on IPV4 or IPV6. | ||||
| func ipToWire(family int, ipAddr string) ([]byte, error) { | ||||
|  | ||||
| 	switch family { | ||||
| 	case 1: | ||||
| 		return net.ParseIP(ipAddr).To4(), nil | ||||
| 	case 2: | ||||
| 		return net.ParseIP(ipAddr).To16(), nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("invalid IP address family (i.e. version) %d", family) | ||||
| } | ||||
|  | ||||
| // portToWire writes port to wire/binary format, 2 bytes | ||||
| func portToWire(portStr string) ([]byte, error) { | ||||
|  | ||||
| 	port, err := strconv.ParseUint(portStr, 10, 16) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return uint16ToWire(uint16(port)), nil | ||||
| } | ||||
|  | ||||
| // Family returns the family of the transport, 1 for IPv4 and 2 for IPv6. | ||||
| func family(ip net.Addr) int { | ||||
| 	var a net.IP | ||||
| 	if i, ok := ip.(*net.UDPAddr); ok { | ||||
| 		a = i.IP | ||||
| 	} | ||||
| 	if i, ok := ip.(*net.TCPAddr); ok { | ||||
| 		a = i.IP | ||||
| 	} | ||||
| 	if a.To4() != nil { | ||||
| 		return 1 | ||||
| 	} | ||||
| 	return 2 | ||||
| } | ||||
							
								
								
									
										80
									
								
								plugin/pkg/variables/variables_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								plugin/pkg/variables/variables_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| package variables | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin/test" | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| func TestGetValue(t *testing.T) { | ||||
| 	// test.ResponseWriter has the following values: | ||||
| 	// 		The remote will always be 10.240.0.1 and port 40212. | ||||
| 	// 		The local address is always 127.0.0.1 and port 53. | ||||
| 	tests := []struct { | ||||
| 		varName       string | ||||
| 		expectedValue []byte | ||||
| 		shouldErr     bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			queryName, | ||||
| 			[]byte("example.com."), | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			queryType, | ||||
| 			[]byte{0x00, 0x01}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			clientIP, | ||||
| 			[]byte{10, 240, 0, 1}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			clientPort, | ||||
| 			[]byte{0x9D, 0x14}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			protocol, | ||||
| 			[]byte("udp"), | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			serverIP, | ||||
| 			[]byte{127, 0, 0, 1}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			serverPort, | ||||
| 			[]byte{0, 53}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"wrong_var", | ||||
| 			[]byte{}, | ||||
| 			true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, tc := range tests { | ||||
| 		m := new(dns.Msg) | ||||
| 		m.SetQuestion("example.com.", dns.TypeA) | ||||
| 		m.Question[0].Qclass = dns.ClassINET | ||||
|  | ||||
| 		value, err := GetValue(tc.varName, &test.ResponseWriter{}, m) | ||||
|  | ||||
| 		if tc.shouldErr && err == nil { | ||||
| 			t.Errorf("Test %d: Expected error, but didn't recieve", i) | ||||
| 		} | ||||
| 		if !tc.shouldErr && err != nil { | ||||
| 			t.Errorf("Test %d: Expected no error, but got error: %v", i, err.Error()) | ||||
| 		} | ||||
|  | ||||
| 		if !bytes.Equal(tc.expectedValue, value) { | ||||
| 			t.Errorf("Test %d: Expected %v but got %v", i, tc.expectedValue, value) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user