| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | package kubernetes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	"strings"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnsutil"
 | 
					
						
							| 
									
										
										
										
											2021-01-24 18:28:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type recordRequest struct {
 | 
					
						
							|  |  |  | 	// The named port from the kubernetes DNS spec, this is the service part (think _https) from a well formed
 | 
					
						
							|  |  |  | 	// SRV record.
 | 
					
						
							|  |  |  | 	port string
 | 
					
						
							|  |  |  | 	// The protocol is usually _udp or _tcp (if set), and comes from the protocol part of a well formed
 | 
					
						
							|  |  |  | 	// SRV record.
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	protocol string
 | 
					
						
							|  |  |  | 	endpoint string
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	cluster  string
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	// The servicename used in Kubernetes.
 | 
					
						
							|  |  |  | 	service string
 | 
					
						
							|  |  |  | 	// The namespace used in Kubernetes.
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	namespace string
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	// A each name can be for a pod or a service, here we track what we've seen, either "pod" or "service".
 | 
					
						
							| 
									
										
										
										
											2017-08-14 08:49:26 +01:00
										 |  |  | 	podOrSvc string
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | // parseRequest parses the qname to find all the elements we need for querying k8s. Anything
 | 
					
						
							|  |  |  | // that is not parsed will have the wildcard "*" value (except r.endpoint).
 | 
					
						
							|  |  |  | // Potential underscores are stripped from _port and _protocol.
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | func parseRequest(name, zone string, multicluster bool) (r recordRequest, err error) {
 | 
					
						
							|  |  |  | 	// 4 Possible cases:
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	// 1. _port._protocol.service.namespace.pod|svc.zone
 | 
					
						
							|  |  |  | 	// 2. (endpoint): endpoint.service.namespace.pod|svc.zone
 | 
					
						
							|  |  |  | 	// 3. (service): service.namespace.pod|svc.zone
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	// 4. (endpoint multicluster): endpoint.cluster.service.namespace.pod|svc.zone
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-31 14:35:22 -05:00
										 |  |  | 	base, _ := dnsutil.TrimZone(name, zone)
 | 
					
						
							| 
									
										
										
										
											2018-04-18 12:12:28 -04:00
										 |  |  | 	// return NODATA for apex queries
 | 
					
						
							|  |  |  | 	if base == "" || base == Svc || base == Pod {
 | 
					
						
							|  |  |  | 		return r, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	segs := dns.SplitDomainName(base)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	last := len(segs) - 1
 | 
					
						
							| 
									
										
										
										
											2017-08-23 14:07:10 +01:00
										 |  |  | 	if last < 0 {
 | 
					
						
							|  |  |  | 		return r, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	r.podOrSvc = segs[last]
 | 
					
						
							|  |  |  | 	if r.podOrSvc != Pod && r.podOrSvc != Svc {
 | 
					
						
							|  |  |  | 		return r, errInvalidRequest
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	last--
 | 
					
						
							|  |  |  | 	if last < 0 {
 | 
					
						
							|  |  |  | 		return r, nil
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	r.namespace = segs[last]
 | 
					
						
							|  |  |  | 	last--
 | 
					
						
							|  |  |  | 	if last < 0 {
 | 
					
						
							|  |  |  | 		return r, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	r.service = segs[last]
 | 
					
						
							|  |  |  | 	last--
 | 
					
						
							|  |  |  | 	if last < 0 {
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 		return r, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	// Because of ambiguity we check the labels left: 1: an endpoint. 2: port and protocol or endpoint and clusterid.
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	// Anything else is a query that is too long to answer and can safely be delegated to return an nxdomain.
 | 
					
						
							|  |  |  | 	switch last {
 | 
					
						
							|  |  |  | 	case 0: // endpoint only
 | 
					
						
							|  |  |  | 		r.endpoint = segs[last]
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	case 1: // service and port or endpoint and clusterid
 | 
					
						
							|  |  |  | 		if !multicluster || strings.HasPrefix(segs[last], "_") || strings.HasPrefix(segs[last-1], "_") {
 | 
					
						
							|  |  |  | 			r.protocol = stripUnderscore(segs[last])
 | 
					
						
							|  |  |  | 			r.port = stripUnderscore(segs[last-1])
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			r.cluster = segs[last]
 | 
					
						
							|  |  |  | 			r.endpoint = segs[last-1]
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	default: // too long
 | 
					
						
							|  |  |  | 		return r, errInvalidRequest
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return r, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // stripUnderscore removes a prefixed underscore from s.
 | 
					
						
							|  |  |  | func stripUnderscore(s string) string {
 | 
					
						
							| 
									
										
										
										
											2022-02-09 13:52:53 -05:00
										 |  |  | 	if len(s) == 0 {
 | 
					
						
							|  |  |  | 		return s
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-22 20:44:42 +01:00
										 |  |  | 	if s[0] != '_' {
 | 
					
						
							|  |  |  | 		return s
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return s[1:]
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-01 15:10:45 +08:00
										 |  |  | // String returns a string representation of r, it just returns all fields concatenated with dots.
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | // This is mostly used in tests.
 | 
					
						
							|  |  |  | func (r recordRequest) String() string {
 | 
					
						
							|  |  |  | 	s := r.port
 | 
					
						
							|  |  |  | 	s += "." + r.protocol
 | 
					
						
							|  |  |  | 	s += "." + r.endpoint
 | 
					
						
							| 
									
										
										
										
											2025-05-19 07:58:16 +02:00
										 |  |  | 	s += "." + r.cluster
 | 
					
						
							| 
									
										
										
										
											2017-08-10 01:08:58 -07:00
										 |  |  | 	s += "." + r.service
 | 
					
						
							|  |  |  | 	s += "." + r.namespace
 | 
					
						
							|  |  |  | 	s += "." + r.podOrSvc
 | 
					
						
							|  |  |  | 	return s
 | 
					
						
							|  |  |  | }
 |