istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/echo/server/forwarder/dns.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package forwarder
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"net"
    22  	"net/url"
    23  	"strings"
    24  	"time"
    25  
    26  	"istio.io/istio/pkg/test/echo"
    27  	"istio.io/istio/pkg/test/echo/proto"
    28  )
    29  
    30  var _ protocol = &dnsProtocol{}
    31  
    32  type dnsProtocol struct {
    33  	e *executor
    34  }
    35  
    36  func newDNSProtocol(e *executor) protocol {
    37  	return &dnsProtocol{e: e}
    38  }
    39  
    40  func (c *dnsProtocol) ForwardEcho(ctx context.Context, cfg *Config) (*proto.ForwardEchoResponse, error) {
    41  	return doForward(ctx, cfg, c.e, c.makeRequest)
    42  }
    43  
    44  type dnsRequest struct {
    45  	hostname  string
    46  	dnsServer string
    47  	query     string
    48  	protocol  string
    49  }
    50  
    51  func checkIn(got string, want ...string) error {
    52  	for _, w := range want {
    53  		if w == got {
    54  			return nil
    55  		}
    56  	}
    57  	return fmt.Errorf("got value %q, wanted one of %v", got, want)
    58  }
    59  
    60  func parseRequest(inputURL string) (dnsRequest, error) {
    61  	req := dnsRequest{}
    62  	u, err := url.Parse(inputURL)
    63  	if err != nil {
    64  		return req, err
    65  	}
    66  	qp, err := url.ParseQuery(u.RawQuery)
    67  	if err != nil {
    68  		return req, err
    69  	}
    70  	req.protocol = qp.Get("protocol")
    71  	if err := checkIn(req.protocol, "", "udp", "tcp"); err != nil {
    72  		return req, err
    73  	}
    74  	req.dnsServer = qp.Get("server")
    75  	if req.dnsServer != "" {
    76  		if _, _, err := net.SplitHostPort(req.dnsServer); err != nil && strings.Contains(err.Error(), "missing port in address") {
    77  			req.dnsServer += ":53"
    78  		}
    79  	}
    80  	req.hostname = u.Host
    81  	req.query = qp.Get("query")
    82  	if err := checkIn(req.query, "", "A", "AAAA"); err != nil {
    83  		return req, err
    84  	}
    85  	return req, nil
    86  }
    87  
    88  func (c *dnsProtocol) makeRequest(ctx context.Context, cfg *Config, requestID int) (string, error) {
    89  	req, err := parseRequest(cfg.Request.Url)
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  	r := newResolver(cfg.timeout, req.protocol, req.dnsServer)
    94  	nt := func() string {
    95  		switch req.query {
    96  		case "A":
    97  			return "ip4"
    98  		case "AAAA":
    99  			return "ip6"
   100  		default:
   101  			return "ip"
   102  		}
   103  	}()
   104  	ctx, cancel := context.WithTimeout(ctx, cfg.timeout)
   105  	defer cancel()
   106  
   107  	start := time.Now()
   108  	ips, err := r.LookupIP(ctx, nt, req.hostname)
   109  	if err != nil {
   110  		return "", err
   111  	}
   112  
   113  	var outBuffer bytes.Buffer
   114  	echo.LatencyField.WriteForRequest(&outBuffer, requestID, fmt.Sprintf("%v", time.Since(start)))
   115  	echo.ActiveRequestsField.WriteForRequest(&outBuffer, requestID, fmt.Sprintf("%d", c.e.ActiveRequests()))
   116  	echo.HostnameField.WriteForRequest(&outBuffer, requestID, req.hostname)
   117  	echo.DNSProtocolField.WriteForRequest(&outBuffer, requestID, req.protocol)
   118  	echo.DNSQueryField.WriteForRequest(&outBuffer, requestID, req.query)
   119  	echo.DNSServerField.WriteForRequest(&outBuffer, requestID, req.dnsServer)
   120  
   121  	for n, i := range ips {
   122  		echo.WriteBodyLine(&outBuffer, requestID, fmt.Sprintf("Response%d=%s", n, i.String()))
   123  	}
   124  	return outBuffer.String(), nil
   125  }
   126  
   127  func (c *dnsProtocol) Close() error {
   128  	return nil
   129  }