| 
									
										
										
										
											2017-07-11 18:05:32 -04:00
										 |  |  | package kubernetes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import "github.com/miekg/dns"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AutoPathWriter implements a ResponseWriter that also does the following:
 | 
					
						
							|  |  |  | // * reverts question section of a packet to its original state.
 | 
					
						
							|  |  |  | //   This is done to avoid the 'Question section mismatch:' error in client.
 | 
					
						
							|  |  |  | // * Defers write to the client if the response code is NXDOMAIN.  This is needed
 | 
					
						
							| 
									
										
										
										
											2017-08-04 09:06:06 -07:00
										 |  |  | //   to enable further search path probing if a search was not successful.
 | 
					
						
							| 
									
										
										
										
											2017-07-11 18:05:32 -04:00
										 |  |  | // * Allow forced write to client regardless of response code.  This is needed to
 | 
					
						
							|  |  |  | //   write the packet to the client if the final search in the path fails to
 | 
					
						
							|  |  |  | //   produce results.
 | 
					
						
							|  |  |  | // * Overwrites response code with AutoPathWriter.Rcode if the response code
 | 
					
						
							|  |  |  | //   is NXDOMAIN (NameError).  This is needed to support the AutoPath.OnNXDOMAIN
 | 
					
						
							|  |  |  | //   function, which returns a NOERROR to client instead of NXDOMAIN if the final
 | 
					
						
							|  |  |  | //   search in the path fails to produce results.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type AutoPathWriter struct {
 | 
					
						
							|  |  |  | 	dns.ResponseWriter
 | 
					
						
							|  |  |  | 	original dns.Question
 | 
					
						
							|  |  |  | 	Rcode    int
 | 
					
						
							|  |  |  | 	Sent     bool
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewAutoPathWriter returns a pointer to a new AutoPathWriter
 | 
					
						
							|  |  |  | func NewAutoPathWriter(w dns.ResponseWriter, r *dns.Msg) *AutoPathWriter {
 | 
					
						
							|  |  |  | 	return &AutoPathWriter{
 | 
					
						
							|  |  |  | 		ResponseWriter: w,
 | 
					
						
							|  |  |  | 		original:       r.Question[0],
 | 
					
						
							|  |  |  | 		Rcode:          dns.RcodeSuccess,
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WriteMsg writes to client, unless response will be NXDOMAIN
 | 
					
						
							|  |  |  | func (apw *AutoPathWriter) WriteMsg(res *dns.Msg) error {
 | 
					
						
							|  |  |  | 	return apw.overrideMsg(res, false)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ForceWriteMsg forces the write to client regardless of response code
 | 
					
						
							|  |  |  | func (apw *AutoPathWriter) ForceWriteMsg(res *dns.Msg) error {
 | 
					
						
							|  |  |  | 	return apw.overrideMsg(res, true)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // overrideMsg overrides rcode, reverts question, adds CNAME, and calls the
 | 
					
						
							|  |  |  | // underlying ResponseWriter's WriteMsg method unless the write is deferred,
 | 
					
						
							|  |  |  | // or force = true.
 | 
					
						
							|  |  |  | func (apw *AutoPathWriter) overrideMsg(res *dns.Msg, force bool) error {
 | 
					
						
							|  |  |  | 	if res.Rcode == dns.RcodeNameError {
 | 
					
						
							|  |  |  | 		res.Rcode = apw.Rcode
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	if res.Rcode != dns.RcodeSuccess && !force {
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, a := range res.Answer {
 | 
					
						
							|  |  |  | 		if apw.original.Name == a.Header().Name {
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		res.Answer = append(res.Answer, nil)
 | 
					
						
							|  |  |  | 		copy(res.Answer[1:], res.Answer)
 | 
					
						
							|  |  |  | 		res.Answer[0] = newCNAME(apw.original.Name, dns.Fqdn(a.Header().Name), a.Header().Ttl)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	res.Question[0] = apw.original
 | 
					
						
							|  |  |  | 	apw.Sent = true
 | 
					
						
							|  |  |  | 	return apw.ResponseWriter.WriteMsg(res)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write is a wrapper that records the size of the message that gets written.
 | 
					
						
							|  |  |  | func (apw *AutoPathWriter) Write(buf []byte) (int, error) {
 | 
					
						
							|  |  |  | 	n, err := apw.ResponseWriter.Write(buf)
 | 
					
						
							|  |  |  | 	return n, err
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Hijack implements dns.Hijacker. It simply wraps the underlying
 | 
					
						
							|  |  |  | // ResponseWriter's Hijack method if there is one, or returns an error.
 | 
					
						
							|  |  |  | func (apw *AutoPathWriter) Hijack() {
 | 
					
						
							|  |  |  | 	apw.ResponseWriter.Hijack()
 | 
					
						
							|  |  |  | 	return
 | 
					
						
							|  |  |  | }
 |