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 }