aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Benjamin <davidben@google.com>2021-06-16 21:30:35 +0000
committerBoringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>2021-06-16 21:30:48 +0000
commit9a5abe05cc999177bc55949c9243b3bc360595cb (patch)
treeb39e5f88fa111e370073d42b2263a3e87860a6b1
parent160a8891ae9a1d03f29aec079a67d97bc773990e (diff)
downloadboringssl-9a5abe05cc999177bc55949c9243b3bc360595cb.zip
boringssl-9a5abe05cc999177bc55949c9243b3bc360595cb.tar.gz
boringssl-9a5abe05cc999177bc55949c9243b3bc360595cb.tar.bz2
Revert "Add util/fetch_ech_config_list.go"
This reverts commit 160a8891ae9a1d03f29aec079a67d97bc773990e. Reason for revert: This broke go.sum on CI for some reason. Will fix and reland. Original change's description: > Add util/fetch_ech_config_list.go > > I wrote this tool to make it easier to test the ECH client against > real-world servers with the bssl client tool. I found that manually > extracting an ECHConfigList from a raw HTTPS record is unnecessarily > painful. > > The tool queries DNS over UDP for HTTPS records. If it finds any HTTPS > records in the response, it attempts to extract an ECHConfigList from > the "ech" SvcParam. It can write each extracted ECHConfigList to a file > in a given directory. Once the ECH client implementation lands, the bssl > client tool should have a new flag that that takes the path to an > ECHConfigList file. > > I am using golang.org/x/net/dns/dnsmessage to parse the DNS response. I > recently added the |UnknownResource| type to this library to enable > callers (like us) to extract the bytes of otherwise-unsupported records > (like HTTPS). I updated the dependency with `go get -u golang.org/x/net`. > > Although the bssl client tool knows how to resolve the address of its > "-connect" parameter, it is difficult to query HTTPS records in a > platform-agnostic way. If we decide the bssl client should directly > query HTTPS rather than leaning on fetch_ech_config_list.go, we should > look into libresolv. Specifically, the |res_query| function enables the > caller to query arbitrary record types. This may open its own can of > cross-platform worms; macOS and Linux typically ship with different > implementations and it is not available on Windows. For more info, see > `man 3 resolver`. > > Bug: 275 > Change-Id: I705591658921f60a958164a18b68ffb697c2ea4b > Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44104 > Reviewed-by: David Benjamin <davidben@google.com> TBR=davidben@google.com,dmcardle@google.com Change-Id: Iec36265dfa3b7c59eb811ed708219bfebb07a710 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: 275 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48105 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
-rw-r--r--go.mod5
-rw-r--r--go.sum7
-rw-r--r--util/fetch_ech_config_list.go389
3 files changed, 1 insertions, 400 deletions
diff --git a/go.mod b/go.mod
index e3184cb..17f9468 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,4 @@ module boringssl.googlesource.com/boringssl
go 1.13
-require (
- golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
- golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
-)
+require golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
diff --git a/go.sum b/go.sum
index 684fb56..8b7d318 100644
--- a/go.sum
+++ b/go.sum
@@ -2,14 +2,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/util/fetch_ech_config_list.go b/util/fetch_ech_config_list.go
deleted file mode 100644
index 03b2f87..0000000
--- a/util/fetch_ech_config_list.go
+++ /dev/null
@@ -1,389 +0,0 @@
-// Copyright (c) 2021, Google Inc.
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-package main
-
-import (
- "errors"
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "net"
- "os"
- "path"
- "strings"
-
- "golang.org/x/crypto/cryptobyte"
- "golang.org/x/net/dns/dnsmessage"
-)
-
-const (
- httpsType = 65 // RRTYPE for HTTPS records.
-
- // SvcParamKey codepoints defined in draft-ietf-dnsop-svcb-https-06.
- httpsKeyMandatory = 0
- httpsKeyALPN = 1
- httpsKeyNoDefaultALPN = 2
- httpsKeyPort = 3
- httpsKeyIPV4Hint = 4
- httpsKeyECH = 5
- httpsKeyIPV6Hint = 6
-)
-
-var (
- name = flag.String("name", "", "The name to look up in DNS. Required.")
- server = flag.String("server", "8.8.8.8:53", "Comma-separated host and UDP port that defines the DNS server to query.")
- outDir = flag.String("out-dir", "", "The directory where ECHConfigList values will be written. If unspecified, bytes are hexdumped to stdout.")
-)
-
-type httpsRecord struct {
- priority uint16
- targetName string
-
- // SvcParams:
- mandatory []uint16
- alpn []string
- noDefaultALPN bool
- hasPort bool
- port uint16
- ipv4hint []net.IP
- ech []byte
- ipv6hint []net.IP
- unknownParams map[uint16][]byte
-}
-
-// String pretty-prints |h| as a multi-line string with bullet points.
-func (h httpsRecord) String() string {
- var b strings.Builder
- fmt.Fprintf(&b, "HTTPS SvcPriority:%d TargetName:%q", h.priority, h.targetName)
-
- if len(h.mandatory) != 0 {
- fmt.Fprintf(&b, "\n * mandatory: %v", h.mandatory)
- }
- if len(h.alpn) != 0 {
- fmt.Fprintf(&b, "\n * alpn: %q", h.alpn)
- }
- if h.noDefaultALPN {
- fmt.Fprint(&b, "\n * no-default-alpn")
- }
- if h.hasPort {
- fmt.Fprintf(&b, "\n * port: %d", h.port)
- }
- if len(h.ipv4hint) != 0 {
- fmt.Fprintf(&b, "\n * ipv4hint:")
- for _, address := range h.ipv4hint {
- fmt.Fprintf(&b, "\n - %s", address)
- }
- }
- if len(h.ech) != 0 {
- fmt.Fprintf(&b, "\n * ech: %x", h.ech)
- }
- if len(h.ipv6hint) != 0 {
- fmt.Fprintf(&b, "\n * ipv6hint:")
- for _, address := range h.ipv6hint {
- fmt.Fprintf(&b, "\n - %s", address)
- }
- }
- if len(h.unknownParams) != 0 {
- fmt.Fprint(&b, "\n * unknown SvcParams:")
- for key, value := range h.unknownParams {
- fmt.Fprintf(&b, "\n - %d: %x", key, value)
- }
- }
- return b.String()
-}
-
-// dnsQueryForHTTPS queries the DNS server over UDP for any HTTPS records
-// associated with |domain|. It scans the response's answers and returns all the
-// HTTPS records it finds. It returns an error if any connection steps fail.
-func dnsQueryForHTTPS(domain string) ([][]byte, error) {
- udpAddr, err := net.ResolveUDPAddr("udp", *server)
- if err != nil {
- return nil, err
- }
- conn, err := net.DialUDP("udp", nil, udpAddr)
- if err != nil {
- return nil, fmt.Errorf("failed to dial: %s", err)
- }
- defer conn.Close()
-
- // Domain name must be canonical or message packing will fail.
- if domain[len(domain)-1] != '.' {
- domain += "."
- }
- dnsName, err := dnsmessage.NewName(domain)
- if err != nil {
- return nil, fmt.Errorf("failed to create DNS name from %q: %s", domain, err)
- }
- question := dnsmessage.Question{
- Name: dnsName,
- Type: httpsType,
- Class: dnsmessage.ClassINET,
- }
- msg := dnsmessage.Message{
- Header: dnsmessage.Header{
- RecursionDesired: true,
- },
- Questions: []dnsmessage.Question{question},
- }
- packedMsg, err := msg.Pack()
- if err != nil {
- return nil, fmt.Errorf("failed to pack msg: %s", err)
- }
-
- if _, err = conn.Write(packedMsg); err != nil {
- return nil, fmt.Errorf("failed to send the DNS query: %s", err)
- }
-
- for {
- response := make([]byte, 512)
- n, err := conn.Read(response)
- if err != nil {
- return nil, fmt.Errorf("failed to read the DNS response: %s", err)
- }
- response = response[:n]
-
- var p dnsmessage.Parser
- header, err := p.Start(response)
- if err != nil {
- return nil, err
- }
- if !header.Response {
- return nil, errors.New("received DNS message is not a response")
- }
- if header.RCode != dnsmessage.RCodeSuccess {
- return nil, fmt.Errorf("response from DNS has non-success RCode: %s", header.RCode.String())
- }
- if header.ID != 0 {
- return nil, errors.New("received a DNS response with the wrong ID")
- }
- if !header.RecursionAvailable {
- return nil, errors.New("server does not support recursion")
- }
- // Verify that this response answers the question that we asked in the
- // query. If the resolver encountered any CNAMEs, it's not guaranteed
- // that the response will contain a question with the same QNAME as our
- // query. However, RFC8499 Section 4 indicates that in general use, the
- // response's QNAME should match the query, so we will make that
- // assumption.
- q, err := p.Question()
- if err != nil {
- return nil, err
- }
- if q != question {
- return nil, fmt.Errorf("response answers the wrong question: %v", q)
- }
- if q, err = p.Question(); err != dnsmessage.ErrSectionDone {
- return nil, fmt.Errorf("response contains an unexpected question: %v", q)
- }
-
- var httpsRecords [][]byte
- for {
- h, err := p.AnswerHeader()
- if err == dnsmessage.ErrSectionDone {
- break
- }
- if err != nil {
- return nil, err
- }
-
- switch h.Type {
- case httpsType:
- // This should continue to work when golang.org/x/net/dns/dnsmessage
- // adds support for HTTPS records.
- r, err := p.UnknownResource()
- if err != nil {
- return nil, err
- }
- httpsRecords = append(httpsRecords, r.Data)
- default:
- if _, err := p.UnknownResource(); err != nil {
- return nil, err
- }
- }
- }
- return httpsRecords, nil
- }
-}
-
-// parseHTTPSRecord parses an HTTPS record (draft-ietf-dnsop-svcb-https-06,
-// Section 2.2) from |raw|. If there are syntax errors, it returns an error.
-func parseHTTPSRecord(raw []byte) (httpsRecord, error) {
- reader := cryptobyte.String(raw)
-
- var priority uint16
- if !reader.ReadUint16(&priority) {
- return httpsRecord{}, errors.New("failed to parse HTTPS record priority")
- }
-
- // Read the TargetName.
- var dottedDomain string
- for {
- var label cryptobyte.String
- if !reader.ReadUint8LengthPrefixed(&label) {
- return httpsRecord{}, errors.New("failed to parse HTTPS record TargetName")
- }
- if label.Empty() {
- break
- }
- dottedDomain += string(label) + "."
- }
-
- if priority == 0 {
- // TODO(dmcardle) Recursively follow AliasForm records.
- return httpsRecord{}, fmt.Errorf("received an AliasForm HTTPS record with TargetName=%q", dottedDomain)
- }
-
- record := httpsRecord{
- priority: priority,
- targetName: dottedDomain,
- unknownParams: make(map[uint16][]byte),
- }
-
- // Read the SvcParams.
- var lastSvcParamKey uint16
- for svcParamCount := 0; !reader.Empty(); svcParamCount++ {
- var svcParamKey uint16
- var svcParamValue cryptobyte.String
- if !reader.ReadUint16(&svcParamKey) ||
- !reader.ReadUint16LengthPrefixed(&svcParamValue) {
- return httpsRecord{}, errors.New("failed to parse HTTPS record SvcParam")
- }
- if svcParamCount > 0 && svcParamKey <= lastSvcParamKey {
- return httpsRecord{}, errors.New("malformed HTTPS record contains out-of-order SvcParamKey")
- }
- lastSvcParamKey = svcParamKey
-
- switch svcParamKey {
- case httpsKeyMandatory:
- if svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed mandatory SvcParamValue")
- }
- var lastKey uint16
- for !svcParamValue.Empty() {
- // |httpsKeyMandatory| may not appear in the mandatory list.
- // |httpsKeyMandatory| is zero, so checking against the initial
- // value of |lastKey| handles ordering and the invalid code point.
- var key uint16
- if !svcParamValue.ReadUint16(&key) ||
- key <= lastKey {
- return httpsRecord{}, errors.New("malformed mandatory SvcParamValue")
- }
- lastKey = key
- record.mandatory = append(record.mandatory, key)
- }
- case httpsKeyALPN:
- if svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed alpn SvcParamValue")
- }
- for !svcParamValue.Empty() {
- var alpn cryptobyte.String
- if !svcParamValue.ReadUint8LengthPrefixed(&alpn) || alpn.Empty() {
- return httpsRecord{}, errors.New("malformed alpn SvcParamValue")
- }
- record.alpn = append(record.alpn, string(alpn))
- }
- case httpsKeyNoDefaultALPN:
- if !svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed no-default-alpn SvcParamValue")
- }
- record.noDefaultALPN = true
- case httpsKeyPort:
- if !svcParamValue.ReadUint16(&record.port) ||
- !svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed port SvcParamValue")
- }
- record.hasPort = true
- case httpsKeyIPV4Hint:
- if svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed ipv4hint SvcParamValue")
- }
- for !svcParamValue.Empty() {
- var address []byte
- if !svcParamValue.ReadBytes(&address, 4) {
- return httpsRecord{}, errors.New("malformed ipv4hint SvcParamValue")
- }
- record.ipv4hint = append(record.ipv4hint, address)
- }
- case httpsKeyECH:
- if svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed ech SvcParamValue")
- }
- record.ech = svcParamValue
- case httpsKeyIPV6Hint:
- if svcParamValue.Empty() {
- return httpsRecord{}, errors.New("malformed ipv6hint SvcParamValue")
- }
- for !svcParamValue.Empty() {
- var address []byte
- if !svcParamValue.ReadBytes(&address, 16) {
- return httpsRecord{}, errors.New("malformed ipv6hint SvcParamValue")
- }
- record.ipv6hint = append(record.ipv6hint, address)
- }
- default:
- record.unknownParams[svcParamKey] = svcParamValue
- }
- }
- return record, nil
-}
-
-func main() {
- flag.Parse()
- log.SetFlags(log.Lshortfile | log.LstdFlags)
-
- if len(*name) == 0 {
- flag.Usage()
- os.Exit(1)
- }
-
- httpsRecords, err := dnsQueryForHTTPS(*name)
- if err != nil {
- log.Printf("Error querying %q: %s\n", *name, err)
- os.Exit(1)
- }
- if len(httpsRecords) == 0 {
- log.Println("No HTTPS records found in DNS response.")
- os.Exit(1)
- }
-
- if len(*outDir) > 0 {
- if err = os.Mkdir(*outDir, 0755); err != nil && !os.IsExist(err) {
- log.Printf("Failed to create out directory %q: %s\n", *outDir, err)
- os.Exit(1)
- }
- }
-
- var echConfigListCount int
- for _, httpsRecord := range httpsRecords {
- record, err := parseHTTPSRecord(httpsRecord)
- if err != nil {
- log.Printf("Failed to parse HTTPS record: %s", err)
- os.Exit(1)
- }
- fmt.Printf("%s\n", record)
- if len(*outDir) == 0 {
- continue
- }
-
- outFile := path.Join(*outDir, fmt.Sprintf("ech-config-list-%d", echConfigListCount))
- if err = ioutil.WriteFile(outFile, record.ech, 0644); err != nil {
- log.Printf("Failed to write file: %s\n", err)
- os.Exit(1)
- }
- fmt.Printf("Wrote ECHConfigList to %q\n", outFile)
- echConfigListCount++
- }
-}