├── .gitignore ├── Makefile ├── README.md ├── btlink proxy root ca windows.PNG ├── btlink proxy settings windows.PNG ├── confluence.go ├── domains.go ├── gateway.go ├── gateway_test.go ├── gencert.go ├── go.mod ├── go.sum ├── logging.go ├── main.go ├── proxy.go ├── proxy.pac ├── templates.go └── templates ├── dir.html ├── gateway-root.html └── pac.tmpl /.gitignore: -------------------------------------------------------------------------------- 1 | ca.key 2 | ca.pem 3 | wildcard.bt.pem 4 | localhost.pem 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: ca.key ca.pem wildcard.bt.pem localhost.pem 2 | 3 | wildcard.bt.pem: ca.key ca.pem 4 | # Due to https://bugzilla.mozilla.org/show_bug.cgi?id=1728009 we need to put the least specific 5 | # wildcards last. 6 | go run -race . gencert '*.ih.bt' '*.pk.bt' '*.ih.bt' '*.bt' > $@ 7 | 8 | ca.key: 9 | openssl genrsa -out $@ 10 | 11 | ca.pem: ca.key 12 | openssl req -x509 -new -key $< -out $@ -subj '/CN=btlink root CA' \ 13 | # Firefox doesn't like name constraints 14 | # -addext 'nameConstraints=critical, permitted;DNS:bt,permitted;DNS:localhost' 15 | 16 | .PHONY: add-trusted-cert 17 | add-trusted-cert: ca.pem 18 | sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" ca.pem 19 | 20 | # Generates a certificate for an arbitrary domain 21 | %.pem: ca.key ca.pem 22 | godo -race . gencert $* $* > $@ 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # btlink 2 | 3 | btlink is a peer-to-peer web hosting implementation. It uses constructs provided by BitTorrent and the BitTorrent DHT to handle DNS and serve websites. It supports domain certificates, subdomains, origin isolation and CORS. btlink can be progressively integrated, starting from no user integration at all, to having users run their own private gateway on their device for maximum control, with a range of options between. The current implementation makes use of the ubiquitous HTTP(S) proxy and proxy auto-config support to extend the web experience at the system level to include native btlink handling. 4 | 5 | btlink defines a HTTP(s) addressing scheme for BitTorrent. This means no special cases for each application: All applications on a configured device will work transparently with btlink without any changes in code, and without knowing that some domains are served from a peer-to-peer network. 6 | 7 | ## Naming 8 | 9 | btlink is the working name, due to the combination of BitTorrent, HTTP URL "links" and the plan to implement something similar to [DNSLink](https://dnslink.io/). Alternative names might invoke something to do with swarms and webs, due to the overlap in terminology there in the use of BitTorrent and HTTP. Shelob, or the Flood for example. Less BitTorrent-specific names like distweb are an option if other backing data platforms are supported (like IPFS, or blockchains). 10 | 11 | ## Architecture 12 | 13 | Dotted lines represent flows that aren't implemented yet. In some cases that may mean there are multiple paths for the same question, the dotted one is intended in the future. 14 | 15 | ```mermaid 16 | flowchart TD 17 | regularWeb[Send request to regular web] 18 | gatewayDomain[Is gateway domain?] 19 | subgraph user [User HTTP Client] 20 | start[User initiates HTTP request] 21 | checkPAC[User has proxy auto-configuration?] 22 | btlinkTLD 23 | dnslinkHost 24 | proxyDomain[Host matches a proxy domain?] 25 | proxy[Send request to proxy] 26 | end 27 | start --> checkPAC 28 | checkPAC --> |No| proxyDomain 29 | checkPAC --> |Yes| btlinkTLD 30 | btlinkTLD ==> |No| proxyDomain 31 | btlinkTLD[Host ends with .btlink?] -.-> |No| dnslinkHost[Domain has btlink DNSLink entry?] 32 | dnslinkHost --> |No| proxyDomain 33 | dnslinkHost --> |Yes| proxy 34 | btlinkTLD --> |Yes| proxy 35 | proxyDomain --> |No| gatewayDomain 36 | proxyDomain --> |Yes| proxy 37 | gatewayDomain --> |No| regularWeb 38 | gatewayDomain --> |Yes| gateway 39 | proxy --> proxyReceive 40 | subgraph proxySubgraph [proxy] 41 | proxyReceive[Proxy receives request] 42 | proxySpecialPaths[Request path contains special proxy path?] 43 | proxyBtlinkDomain[Request host ends in .btlink?] 44 | proxyDnslinkHost[Host domain has DNSLink entry?] 45 | serveProxySpecialPath[Serve special proxy path] 46 | end 47 | proxyReceive --> proxySpecialPaths 48 | proxySpecialPaths --> |Yes| serveProxySpecialPath 49 | proxySpecialPaths --> |No| proxyBtlinkDomain 50 | proxyBtlinkDomain --> |No| proxyDnslinkHost 51 | proxyBtlinkDomain --> |Yes| gateway 52 | proxyDnslinkHost --> |No| gatewayDomain 53 | proxyDnslinkHost --> |Yes| gateway 54 | subgraph gatewaySubgraph [gateway] 55 | gateway[Request sent to gateway] 56 | serveBep44Value[Serve BEP44 value] 57 | btlinkSubdomain[Resolve btlink domain schema] 58 | gatewayResolveDnslink[Resolve DNSLink] 59 | gatewayCheckBtlinkSubdomain[Is btlink subdomain?] 60 | gatewayRootDomain[Is gateway or btlink root domain?] 61 | serveGatewayRootUploader[Serve gateway root domain page] 62 | checkSpecialTorrentPath[Does path require special handling?] 63 | serveSpecialTorrentPath[Serve special torrent path] 64 | gatewayServeTorrentFile[Serve torrent file over HTTP] 65 | subgraph torrentClient ["BitTorrent client"] 66 | fetchBep44Value[Get DHT item using BEP44] 67 | dhtBep46Lookup 68 | getTorrentInfo 69 | openTorrentFileReader[Open torrent file reader] 70 | end 71 | end 72 | gateway --> gatewayRootDomain --> |Yes| serveGatewayRootUploader 73 | gatewayRootDomain --> |No| gatewayCheckBtlinkSubdomain 74 | gatewayCheckBtlinkSubdomain --> |No| gatewayResolveDnslink --> btlinkSubdomain 75 | gatewayCheckBtlinkSubdomain --> |Yes| btlinkSubdomain 76 | btlinkSubdomain --> |pk| dhtBep46Lookup(Resolve salt and public key to infohash using BEP46) --> getTorrentInfo(Get info for infohash) 77 | btlinkSubdomain --> |ih| getTorrentInfo --> checkSpecialTorrentPath --> serveSpecialTorrentPath 78 | checkSpecialTorrentPath --> openTorrentFileReader --> gatewayServeTorrentFile 79 | btlinkSubdomain -.-> |44| fetchBep44Value -.-> serveBep44Value 80 | ``` 81 | 82 | ### Domain schema 83 | 84 | #### Top-level domain 85 | 86 | The top-level domain to trigger proxy intervention, and for general identification of btlink domain addressing (referred to as `tld`) was `.bt`. However that may be incompatible with the [ccTLD of Bhutan](https://en.wikipedia.org/wiki/.bt) (See issue [#2](https://github.com/anacrolix/btlink/issues/2)). The current `tld` in use is `btlink`. 87 | 88 | Where possible, separate domains are used to reference different torrents to provide [origin isolation]. 89 | 90 | [origin isolation]: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy 91 | 92 | #### {infohash}-ih.{tld}/{[torrent path schema](#torrent-path-schema)}} 93 | 94 | Files within a torrent are exposed via the URL path being the path components of the files join with `/`. Suitable headers may be included as determined. How is the info name field handled? Perhaps it is ignored, except for single-file torrents? 95 | 96 | #### ({salt..}[.-])?{base36(public key)}-pk.{tld}/{[torrent path schema](#torrent-path-schema)}} 97 | 98 | Serves torrent corresponding to lookup of mutable DHT item. Salt is optional per [BEP 46]. This means that owners of a public key can also manage cookies for their salted subdomains (and potentially other resources that support a subdomain relationship like this). The salt may or may not be part of the same label as the public key for wildcard certificate reasons. 99 | 100 | #### {target}-44.{tld} 101 | Fetches an immutable item from the DHT. `44` is a reference to [BEP 44]. The returned item is an encoded bencode value. Various path and query values might support conversion into other formats. Currently unimplemented. 102 | 103 | [BEP 44]: http://bittorrent.org/beps/bep_0044.html 104 | [BEP 46]: http://bittorrent.org/beps/bep_0046.html 105 | 106 | ### Path schema 107 | 108 | #### Torrent path schema 109 | 110 | Files within a torrent are exposed with a path equal to the components of the `path` list in the `info` `files` field joined with `/`. There may be additional special paths exposed within at `/.btlink/` and `/.well-known/` as found appropriate. Currently the info `name` is ignored, however for compatibility with webseeding, it may be exposed in a special directory for that purpose (possibly in `/.btlink/`). 111 | 112 | #### Proxy btlink schema 113 | 114 | Proxies can serve a dynamic PAC file from `/.btlink/proxy.pac`, and their CA certificate from `/.btlink/rootca.pem`. 115 | 116 | ## Link Records 117 | 118 | Domains may link/alias into the `.{tld}` address scheme by use of a `_btlink` DNS record on the linked domain. For example `chromecast.link` might be hosted on btlink, by way of a `_btlink.chromecast.link` TXT record. The `_btlink` record contains a [magnet link] (or just the btlink domain) where the content will be found. 119 | 120 | If a link record exists, it is possible to CNAME or ALIAS the parent domain to a btlink gateway to allow use by non-proxy-using clients. 121 | 122 | ### Magnet links 123 | 124 | Per [BEP 46], `magnet:?xs=urn:btpk:[ Public Key (Hex) ]&s=[ Salt (Hex) ]`, corresponding to the `.pk.{tld}` domain space. 125 | 126 | Regular BitTorrent magnet links correspond to the `.ih.{tld}` domain space. 127 | 128 | ## Infrastructure 129 | 130 | Here proxies and gateways are described separately, but it's likely that in initial implementations they will be combined. There could be good reasons to separate them for more advanced use. 131 | 132 | ```mermaid 133 | flowchart TD 134 | user --> proxy 135 | user --> gateway 136 | subgraph btlink proxy 137 | proxy 138 | gateway 139 | end 140 | proxy --> gateway 141 | subgraph confluence 142 | gateway --> client 143 | client --> bittorrent 144 | end 145 | ``` 146 | 147 | ### Proxies 148 | 149 | Proxies are used to transparently provide a mapping from URLs to BitTorrent without modifying HTTP client software. Support for configuring HTTP proxies is well-supported and common due to ubiquitous use on corporate and government systems, as well as by anti-censorship and privacy advocates. 150 | 151 | Proxies route `.{tld}` and domains with `_btlink` records to a gateway. They may also expose [special paths](#proxy-btlink-schema) for configuration. 152 | 153 | Proxies can set a header to tell origin servers that their clients support btlink. This means that dynamic sites can provide generic btlink URLs without explicitly selecting a public gateway as a fallback. 154 | 155 | [PAC]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file 156 | 157 | ### Gateways 158 | 159 | Gateways are HTTP servers that serve according to the [Addressing schema](#addressing-schema). They will also intercept requests to domains with `_btlink` records and serve those via BitTorrent if possible. Gateways can run behind reverse proxies and CDNs as they serve immutable, long-lived content. 160 | 161 | ## Browsers 162 | 163 | ### Chrome 164 | 165 | Chrome must be restarted on Windows after installing root CA certificate. 166 | You can manually re-apply proxy settings via . 167 | 168 | ### Firefox 169 | 170 | Firefox will reject certificates with wildcard domains with less than 2 fixed labels (https://bugzilla.mozilla.org/show_bug.cgi?id=1728009). It seems to require ownership information (subject O and OU fields?) on issuer and leaf certificate. It also appears to need restarting to load certificates from the system keychain. It will establish http2 connections to proxies if possible, which doesn't work. 171 | 172 | ### Safari 173 | 174 | Safari will hang on proxy CONNECT attempts if they respond with `Transfer-Encoding: chunked` (https://feedbackassistant.apple.com/feedback/9578066, possibly not publicly viewable). Safari does not seem to support HTTPS proxies. 175 | 176 | ### curl 177 | 178 | Does not appear to use the system keychain on MacOS. Passing `--proxy-cacert` and `--cacert` will let you specify a btlink CA file. 179 | 180 | ## TODO 181 | 182 | * Implement a secure channel to proxies for Safari. 183 | * See if Firefox supports name constraints on intermediate CAs. 184 | * Separate gateways and proxies. 185 | 186 | ## Open questions 187 | 188 | * Can we have a common root certificate shared by all gateways/proxies? 189 | * Does Safari support SOCKS? Via PAC? 190 | * Should the domain schema be collapsed so that a single wildcard certificate is sufficient? 191 | -------------------------------------------------------------------------------- /btlink proxy root ca windows.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anacrolix/btlink/6f0f28703650d60c55d9077cd9246f07c6a729d8/btlink proxy root ca windows.PNG -------------------------------------------------------------------------------- /btlink proxy settings windows.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anacrolix/btlink/6f0f28703650d60c55d9077cd9246f07c6a729d8/btlink proxy settings windows.PNG -------------------------------------------------------------------------------- /confluence.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | "net/http/httputil" 10 | "net/url" 11 | "strings" 12 | ) 13 | 14 | type confluenceHandler struct { 15 | confluenceHost string 16 | confluenceScheme string 17 | confluenceTransport http.RoundTripper 18 | } 19 | 20 | func (ch *confluenceHandler) data(w http.ResponseWriter, incomingRequest *http.Request, ih string, path string, gatewayDomains []string) { 21 | (&httputil.ReverseProxy{ 22 | Director: func(r *http.Request) { 23 | r.URL.Host = ch.confluenceHost 24 | r.URL.Scheme = ch.confluenceScheme 25 | r.URL.Path = "/data" 26 | r.URL.RawQuery = url.Values{"ih": {ih}, "path": {path}}.Encode() 27 | }, 28 | ModifyResponse: func(r *http.Response) error { 29 | // Looks like it's sufficient to set a good Content-Type. 30 | if false { 31 | // Confluence sets filename=, we also want to ensure inline. It might be sufficient for 32 | // this project to just set inline and let the browser infer filename because we're 33 | // routing torrent file paths using the URL path component. 34 | r.Header.Set("Content-Disposition", "inline") 35 | } 36 | // Copied from anacrolix/webtorrent for streaming in Chrome: 37 | if r.Header.Get("Content-Type") == "video/x-matroska" { 38 | r.Header.Set("Content-Type", "video/webm") 39 | } 40 | if requestHasBtlinkQueryFlag(incomingRequest, "src only") { 41 | r.Header.Set( 42 | "Content-Security-Policy", 43 | strings.Join( 44 | append( 45 | []string{"default-src", "'self'"}, 46 | func() (hostSources []string) { 47 | for _, domain := range gatewayDomains { 48 | hostSources = append(hostSources, domain, "*."+domain) 49 | } 50 | return 51 | }()...), 52 | " ")) 53 | } 54 | return nil 55 | }, 56 | Transport: ch.confluenceTransport, 57 | }).ServeHTTP(w, incomingRequest) 58 | } 59 | 60 | func (ch *confluenceHandler) newRequest(ctx context.Context, method string, ref *url.URL, body io.Reader) *http.Request { 61 | base := url.URL{ 62 | Scheme: ch.confluenceScheme, 63 | Host: ch.confluenceHost, 64 | } 65 | u := base.ResolveReference(ref) 66 | log.Printf("%q", u.String()) 67 | req, err := http.NewRequestWithContext(ctx, method, u.String(), body) 68 | if err != nil { 69 | panic(err) 70 | } 71 | return req 72 | } 73 | 74 | func (ch *confluenceHandler) do(req *http.Request) (resp *http.Response, err error) { 75 | // TODO: Reuse 76 | hc := http.Client{ 77 | Transport: ch.confluenceTransport, 78 | } 79 | resp, err = hc.Do(req) 80 | return 81 | } 82 | 83 | func (ch *confluenceHandler) get(ctx context.Context, path string, q url.Values) (resp *http.Response, err error) { 84 | req := ch.newRequest(ctx, http.MethodGet, &url.URL{ 85 | Path: path, 86 | RawQuery: q.Encode(), 87 | }, nil) 88 | return ch.do(req) 89 | } 90 | 91 | func (ch *confluenceHandler) dhtGet(ctx context.Context, target, salt string) (b []byte, err error) { 92 | resp, err := ch.get(ctx, "/bep44", url.Values{"target": {target}, "salt": {salt}}) 93 | if err != nil { 94 | return 95 | } 96 | defer resp.Body.Close() 97 | if resp.StatusCode != http.StatusOK { 98 | err = fmt.Errorf("unexpected response status code: %v", resp.StatusCode) 99 | return 100 | } 101 | b, err = io.ReadAll(resp.Body) 102 | return 103 | } 104 | -------------------------------------------------------------------------------- /domains.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | rootDomain = "btlink" 5 | oldRootDomain = "bt" 6 | ) 7 | -------------------------------------------------------------------------------- /gateway.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | _ "embed" 7 | "encoding/gob" 8 | "encoding/hex" 9 | "fmt" 10 | "html/template" 11 | "io" 12 | "log" 13 | "net" 14 | "net/http" 15 | "net/url" 16 | "os" 17 | "strings" 18 | "time" 19 | 20 | "github.com/anacrolix/generics" 21 | "github.com/davecgh/go-spew/spew" 22 | "github.com/huandu/xstrings" 23 | 24 | "github.com/anacrolix/dht/v2/bep44" 25 | "github.com/anacrolix/dht/v2/krpc" 26 | "github.com/anacrolix/torrent/bencode" 27 | "github.com/anacrolix/torrent/metainfo" 28 | "github.com/dgraph-io/ristretto" 29 | "github.com/dustin/go-humanize" 30 | "github.com/multiformats/go-base36" 31 | "golang.org/x/exp/slices" 32 | "golang.org/x/sync/singleflight" 33 | "golang.org/x/text/collate" 34 | "golang.org/x/text/language" 35 | ) 36 | 37 | type dhtItemCacheValue struct { 38 | target bep44.Target 39 | updated time.Time 40 | updating bool 41 | payload krpc.Bep46Payload 42 | } 43 | 44 | type gatewayHandler struct { 45 | dirPageTemplate *template.Template 46 | confluence confluenceHandler 47 | dhtItemCache *ristretto.Cache 48 | dhtItemCacheGetDedup singleflight.Group 49 | dhtGetDedup singleflight.Group 50 | metainfoCache *ristretto.Cache 51 | gatewayDomains []string 52 | } 53 | 54 | func reverse(ss []string) { 55 | for i := 0; i < len(ss)/2; i++ { 56 | j := len(ss) - i - 1 57 | ss[i], ss[j] = ss[j], ss[i] 58 | } 59 | } 60 | 61 | type gatewayRootPageData struct { 62 | JustUploaded *uploadedPageData 63 | } 64 | 65 | type uploadedPageData struct { 66 | Infohash string 67 | Magnet template.URL 68 | GatewayUrl *url.URL 69 | Debug string 70 | } 71 | 72 | func (h *gatewayHandler) serveRoot(w http.ResponseWriter, r *http.Request) { 73 | pageData := gatewayRootPageData{} 74 | if r.Method != http.MethodGet { 75 | pageData.JustUploaded = h.doUpload(w, r) 76 | } 77 | err := htmlTemplates.ExecuteTemplate(w, "gateway-root.html", pageData) 78 | if err != nil { 79 | panic(err) 80 | } 81 | } 82 | 83 | func (h *gatewayHandler) doUpload(w http.ResponseWriter, r *http.Request) *uploadedPageData { 84 | if false { 85 | err := r.ParseMultipartForm(420) 86 | if err != nil { 87 | err = fmt.Errorf("parsing multipart upload form: %w", err) 88 | log.Print(err) 89 | http.Error(w, "error parsing multipart form", http.StatusBadRequest) 90 | return nil 91 | } 92 | spew.Fdump(w, r.MultipartForm) 93 | return nil 94 | } 95 | confluenceRequest := h.confluence.newRequest(r.Context(), r.Method, &url.URL{Path: "/upload"}, r.Body) 96 | for _, h := range []string{"Content-Type", "Content-Length"} { 97 | confluenceRequest.Header[h] = r.Header[h] 98 | } 99 | confluenceResponse, err := h.confluence.do(confluenceRequest) 100 | if err != nil { 101 | panic(err) 102 | } 103 | defer confluenceResponse.Body.Close() 104 | if confluenceResponse.StatusCode != http.StatusOK { 105 | io.Copy(os.Stderr, confluenceResponse.Body) 106 | panic(confluenceResponse.StatusCode) 107 | } 108 | mi, err := metainfo.Load(confluenceResponse.Body) 109 | if err != nil { 110 | panic(err) 111 | } 112 | ih := mi.HashInfoBytes() 113 | info, err := mi.UnmarshalInfo() 114 | if err != nil { 115 | panic(err) 116 | } 117 | var debug bytes.Buffer 118 | if false { 119 | spew.Fdump(&debug, info) 120 | } 121 | magnet := mi.Magnet(&ih, &info) 122 | addGatewayWebseedToMagnet(&magnet, infohashHost(ih.HexString(), r.Host), h.gatewayWebseedScheme(r), r.URL) 123 | templateData := &uploadedPageData{ 124 | ih.HexString(), 125 | template.URL(magnet.String()), 126 | &url.URL{ 127 | Scheme: r.URL.Scheme, 128 | Host: infohashHost(ih.HexString(), r.Host), 129 | }, 130 | debug.String(), 131 | } 132 | mi.InfoBytes = nil 133 | if false { 134 | spew.Fdump(&debug, mi) 135 | } 136 | // Confluence immediately imports upload data. 137 | if false { 138 | // If the data isn't accessible, the gateway will get stuck trying to load the autoindex if the torrent has one. 139 | templateData.GatewayUrl.RawQuery = "btlink-no-autoindex" 140 | } 141 | return templateData 142 | } 143 | 144 | func infohashHost(ihHex, gateway string) string { 145 | return ihHex + "-ih." + gateway 146 | } 147 | 148 | func (h *gatewayHandler) resolveDomainDnsLink(w http.ResponseWriter, r *http.Request) bool { 149 | name := "_dnslink." + r.Host 150 | txts, err := net.LookupTXT(name) 151 | if err != nil { 152 | err = fmt.Errorf("resolving txt for %v: %w", name, err) 153 | log.Printf("error %s", err) 154 | return false 155 | } 156 | if len(txts) == 0 { 157 | return false 158 | } 159 | return h.serveBtLinkDomain(w, r, strings.Split(txts[0], "."), generics.None[string]()) 160 | } 161 | 162 | func (h *gatewayHandler) serveOldBtLinkRedirection(w http.ResponseWriter, r *http.Request) bool { 163 | log.Printf("considering %q for btlink handling", r.Host) 164 | newHost := redirectOldBtRoot(r.Host) 165 | if !newHost.Ok { 166 | return false 167 | } 168 | newUrl := r.URL.ResolveReference(&url.URL{ 169 | Host: newHost.Value, 170 | }) 171 | http.Redirect(w, r, newUrl.String(), http.StatusTemporaryRedirect) 172 | return true 173 | } 174 | 175 | func (h *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) bool { 176 | if h.serveBtLink(w, r) { 177 | return true 178 | } 179 | if h.serveOldBtLinkRedirection(w, r) { 180 | return true 181 | } 182 | if h.resolveDomainDnsLink(w, r) { 183 | return true 184 | } 185 | return false 186 | } 187 | 188 | func (h *gatewayHandler) serveBtLink(w http.ResponseWriter, r *http.Request) bool { 189 | log.Printf("considering %q for btlink handling", r.Host) 190 | ss := strings.Split(r.Host, ".") 191 | reverse(ss) 192 | gatewayDomainParts := make([]string, 0, len(ss)) 193 | // Strip potential gateway labels 194 | for len(ss) != 0 && ss[0] != rootDomain { 195 | gatewayDomainParts = append(gatewayDomainParts, ss[0]) 196 | ss = ss[1:] 197 | } 198 | // The root domain was not seen 199 | if len(ss) == 0 { 200 | return false 201 | } 202 | log.Printf("handling btlink request for %q", requestUrl(r)) 203 | gatewayDomainParts = append(gatewayDomainParts, ss[0]) 204 | // Strip the root domain 205 | ss = ss[1:] 206 | reverse(gatewayDomainParts) 207 | return h.serveBtLinkDomain(w, r, ss, generics.Some(strings.Join(gatewayDomainParts, "."))) 208 | } 209 | 210 | func (h *gatewayHandler) serveBtLinkDomain(w http.ResponseWriter, r *http.Request, ss []string, gatewayDomain generics.Option[string]) bool { 211 | if len(ss) == 0 { 212 | h.serveRoot(w, r) 213 | return true 214 | } 215 | labelParts := strings.Split(ss[0], "-") 216 | ss = ss[1:] 217 | reverse(labelParts) 218 | switch labelParts[0] { 219 | case "ih": 220 | labelParts = labelParts[1:] 221 | reverse(labelParts) 222 | if len(labelParts) != 1 { 223 | http.Error(w, "bad host", http.StatusBadRequest) 224 | return true 225 | } 226 | h.serveTorrentPath(w, r, strings.Join(labelParts, "-"), gatewayDomain) 227 | return true 228 | case "pk": 229 | labelParts = labelParts[1:] 230 | if len(labelParts) == 0 { 231 | http.Error(w, "bad host", http.StatusBadRequest) 232 | return true 233 | } 234 | pk, err := base36.DecodeString(labelParts[0]) 235 | if err != nil { 236 | http.Error(w, fmt.Errorf("error decoding public key from base36: %w", err).Error(), http.StatusBadRequest) 237 | return true 238 | } 239 | labelParts = labelParts[1:] 240 | reverse(labelParts) 241 | reverse(ss) 242 | if len(labelParts) != 0 { 243 | ss = append(ss, strings.Join(labelParts, "-")) 244 | } 245 | salt := strings.Join(ss, ".") 246 | log.Printf("salt for %q: %q", r.Host, salt) 247 | target := bep44.MakeMutableTarget(*(*[32]byte)(pk), []byte(salt)) 248 | log.Printf("looking up infohash for %q at %x", r.Host, target) 249 | bep46, err := h.getMutableInfohash(target, string(salt)) 250 | if err != nil { 251 | log.Printf("error resolving %q: %v", r.Host, err) 252 | http.Error(w, err.Error(), http.StatusNotFound) 253 | return true 254 | } 255 | log.Printf("resolved %q to %x", r.Host, bep46.Ih) 256 | h.serveTorrentPath(w, r, hex.EncodeToString(bep46.Ih[:]), gatewayDomain) 257 | return true 258 | } 259 | http.Error(w, "bad host", http.StatusBadRequest) 260 | return true 261 | } 262 | 263 | func redirectOldBtRoot(host string) (newHost generics.Option[string]) { 264 | ss := strings.Split(host, ".") 265 | reverse(ss) 266 | var gatewayParts []string 267 | // Strip potential gateway labels 268 | for len(ss) != 0 && ss[0] != oldRootDomain { 269 | gatewayParts = append(gatewayParts, ss[0]) 270 | ss = ss[1:] 271 | } 272 | // The root domain was not seen 273 | if len(ss) == 0 { 274 | return 275 | } 276 | if len(gatewayParts) == 0 || gatewayParts[len(gatewayParts)-1] != rootDomain { 277 | gatewayParts = append(gatewayParts, rootDomain) 278 | } 279 | reverse(gatewayParts) 280 | gateway := strings.Join(gatewayParts, ".") 281 | // Strip the root domain 282 | ss = ss[1:] 283 | if len(ss) == 0 { 284 | return generics.Some(gateway) 285 | } 286 | switch ss[0] { 287 | case "ih": 288 | ss = ss[1:] 289 | reverse(ss) 290 | return generics.Some(infohashHost(strings.Join(ss, "."), gateway)) 291 | case "pk": 292 | ss = ss[1:] 293 | reverse(ss) 294 | return generics.Some(strings.Join(ss, ".") + "-pk." + gateway) 295 | } 296 | return 297 | } 298 | 299 | func (h *gatewayHandler) getTorrentMetaInfo(w http.ResponseWriter, r *http.Request, ihHex string) (mi metainfo.MetaInfo, ok bool) { 300 | cacheVal, ok := h.metainfoCache.Get(ihHex) 301 | if ok { 302 | mi = cacheVal.(metainfo.MetaInfo) 303 | return 304 | } 305 | resp, err := h.confluence.get(r.Context(), "/metainfo", url.Values{"ih": {ihHex}}) 306 | if err != nil { 307 | log.Printf("error getting meta-info from confluence [ih: %q]: %v", ihHex, err) 308 | http.Error(w, "error getting torrent meta-info", http.StatusBadGateway) 309 | return 310 | } 311 | defer resp.Body.Close() 312 | err = bencode.NewDecoder(resp.Body).Decode(&mi) 313 | if err != nil { 314 | log.Printf("error decoding info: %v", err) 315 | http.Error(w, "error decoding torrent meta-info", http.StatusBadGateway) 316 | return 317 | } 318 | ok = true 319 | cost := estimateRecursiveMemoryUse(mi) 320 | log.Printf("store info for %v in cache with estimated cost %v", ihHex, cost) 321 | h.metainfoCache.Set(ihHex, mi, int64(cost)) 322 | return 323 | } 324 | 325 | func estimateRecursiveMemoryUse(val interface{}) int { 326 | var buf bytes.Buffer 327 | err := gob.NewEncoder(&buf).Encode(val) 328 | if err != nil { 329 | panic(err) 330 | } 331 | return buf.Len() 332 | } 333 | 334 | type dirPageItem struct { 335 | Href string 336 | Name string 337 | Size string 338 | } 339 | 340 | var torrentFilesCollator = collate.New(language.AmericanEnglish, collate.Numeric, collate.IgnoreCase) 341 | 342 | func (h *gatewayHandler) gatewayWebseedScheme(r *http.Request) string { 343 | if r.TLS != nil { 344 | return "https" 345 | } 346 | return "http" 347 | } 348 | 349 | func requestHasBtlinkQueryFlag(r *http.Request, flag string) bool { 350 | return r.URL.Query().Has(xstrings.ToKebabCase("btlink " + flag)) 351 | } 352 | 353 | func (h *gatewayHandler) serveTorrentDir(w http.ResponseWriter, r *http.Request, ihHex string, gatewayDomain generics.Option[string]) { 354 | mi, ok := h.getTorrentMetaInfo(w, r, ihHex) 355 | if !ok { 356 | return 357 | } 358 | info, err := mi.UnmarshalInfo() 359 | if err != nil { 360 | panic(err) 361 | } 362 | var subFiles []dirPageItem 363 | autoIndex := !requestHasBtlinkQueryFlag(r, "no autoindex") 364 | baseDisplayPath := r.URL.Path[1:] 365 | upvertedFiles := info.UpvertedFiles() 366 | subDirs := make(map[string]int64, len(upvertedFiles)) 367 | for _, f := range upvertedFiles { 368 | dp := f.DisplayPath(&info) 369 | if !strings.HasPrefix(dp, baseDisplayPath) { 370 | continue 371 | } 372 | relPath := dp[len(baseDisplayPath):] 373 | if autoIndex { 374 | // Serve this file as the directory. 375 | if relPath == "index.html" { 376 | h.serveTorrentFile(w, r, ihHex, dp, gatewayDomain) 377 | return 378 | } 379 | } 380 | nextSep := strings.Index(relPath, "/") 381 | if nextSep == -1 { 382 | subFiles = append(subFiles, dirPageItem{ 383 | Href: relPath, 384 | Name: relPath, 385 | Size: humanize.Bytes(uint64(f.Length)), 386 | }) 387 | } else { 388 | relPath = relPath[:nextSep+1] 389 | subDirs[relPath] += f.Length 390 | } 391 | } 392 | if len(subDirs) == 0 && len(subFiles) == 0 { 393 | http.NotFound(w, r) 394 | return 395 | } 396 | children := make([]dirPageItem, 0, 1+len(subDirs)+len(subFiles)) 397 | if baseDisplayPath != "" { 398 | children = append(children, dirPageItem{"../", "../", ""}) 399 | } 400 | for relPath, size := range subDirs { 401 | children = append(children, dirPageItem{ 402 | Href: relPath, 403 | Name: relPath, 404 | Size: humanize.Bytes(uint64(size)), 405 | }) 406 | } 407 | children = append(children, subFiles...) 408 | // Dirs come from an unstable source (map), but their names are unique. 409 | slices.SortStableFunc(children, func(l, r dirPageItem) bool { 410 | lDir := strings.HasSuffix(l.Name, "/") 411 | rDir := strings.HasSuffix(r.Name, "/") 412 | if lDir != rDir { 413 | return lDir 414 | } 415 | i := torrentFilesCollator.CompareString(l.Name, r.Name) 416 | if i != 0 { 417 | return i < 0 418 | } 419 | return false 420 | }) 421 | dirPath := r.URL.Path 422 | infoHash := metainfo.NewHashFromHex(ihHex) 423 | w.Header().Set("Content-Type", "text/html") 424 | magnet := mi.Magnet(&infoHash, &info) 425 | spew.Dump(*r.URL) 426 | spew.Dump(h.gatewayWebseedScheme(r), magnet) 427 | addGatewayWebseedToMagnet(&magnet, r.Host, h.gatewayWebseedScheme(r), r.URL) 428 | h.dirPageTemplate.Execute(w, dirPageData{ 429 | Path: dirPath, 430 | Children: children, 431 | MagnetURI: template.URL(magnet.String()), 432 | }) 433 | } 434 | 435 | func addGatewayWebseedToMagnet(m *metainfo.Magnet, gatewayHost, scheme string, baseUrl *url.URL) { 436 | ref := url.URL{ 437 | Host: gatewayHost, 438 | Path: "/", 439 | } 440 | if baseUrl.Scheme == "" { 441 | ref.Scheme = scheme 442 | } 443 | m.Params.Add("ws", baseUrl.ResolveReference(&ref).String()) 444 | } 445 | 446 | type dirPageData struct { 447 | Path string 448 | Children []dirPageItem 449 | MagnetURI template.URL 450 | } 451 | 452 | func (h *gatewayHandler) serveTorrentPath(w http.ResponseWriter, r *http.Request, ihHex string, gatewayDomain generics.Option[string]) { 453 | if strings.HasSuffix(r.URL.Path, "/") { 454 | h.serveTorrentDir(w, r, ihHex, gatewayDomain) 455 | return 456 | } 457 | h.serveTorrentFile(w, r, ihHex, r.URL.Path[1:], gatewayDomain) 458 | } 459 | 460 | func (h *gatewayHandler) serveTorrentFile(w http.ResponseWriter, r *http.Request, ihHex, filePath string, gatewayDomain generics.Option[string]) { 461 | gatewayDomains := h.gatewayDomains 462 | if gatewayDomain.Ok { 463 | gatewayDomains = append([]string{gatewayDomain.Value}, gatewayDomains...) 464 | } 465 | h.confluence.data(w, r, ihHex, filePath, gatewayDomains) 466 | } 467 | 468 | func (h *gatewayHandler) getMutableInfohash(target bep44.Target, salt string) (_ krpc.Bep46Payload, err error) { 469 | ret, err, _ := h.dhtItemCacheGetDedup.Do(string(target[:]), func() (interface{}, error) { 470 | v, ok := h.dhtItemCache.Get(target[:]) 471 | if ok { 472 | v := v.(*dhtItemCacheValue) 473 | stale := time.Since(v.updated) >= time.Minute 474 | if !v.updating && stale { 475 | log.Printf("initiating async refresh of cached dht item [target=%x]", target) 476 | v.updating = true 477 | go func() { 478 | bep46, err := h.getMutableInfohashFromDht(target, salt) 479 | v.updating = false 480 | if err != nil { 481 | log.Printf("error updating dht item cache [target=%x]: %v", target, err) 482 | return 483 | } 484 | h.cacheDhtItem(target, bep46) 485 | }() 486 | } 487 | log.Printf("served dht item from cache [target=%x, stale=%v]", target, stale) 488 | return v.payload, nil 489 | } 490 | bep46, err := h.getMutableInfohashFromDht(target, salt) 491 | if err == nil { 492 | h.cacheDhtItem(target, bep46) 493 | } 494 | return bep46, err 495 | }) 496 | if err != nil { 497 | return 498 | } 499 | return ret.(krpc.Bep46Payload), err 500 | } 501 | 502 | func (h *gatewayHandler) cacheDhtItem(target bep44.Target, bep46 krpc.Bep46Payload) { 503 | stored := h.dhtItemCache.Set(target[:], &dhtItemCacheValue{ 504 | updated: time.Now(), 505 | updating: false, 506 | payload: bep46, 507 | target: target, 508 | }, 1) 509 | log.Printf("caching dht item [target=%x, stored=%v]", target, stored) 510 | } 511 | 512 | func (h *gatewayHandler) getMutableInfohashFromDht(target bep44.Target, salt string) (_ krpc.Bep46Payload, err error) { 513 | ret, err, _ := h.dhtGetDedup.Do(string(target[:]), func() (_ interface{}, err error) { 514 | b, err := h.confluence.dhtGet(context.Background(), hex.EncodeToString(target[:]), salt) 515 | if err != nil { 516 | err = fmt.Errorf("getting from dht via confluence: %w", err) 517 | return 518 | } 519 | var bep46 krpc.Bep46Payload 520 | err = bencode.Unmarshal(b, &bep46) 521 | if err != nil { 522 | err = fmt.Errorf("unmarshalling bep46 payload from confluence response: %w", err) 523 | return 524 | } 525 | return bep46, err 526 | }) 527 | if err != nil { 528 | return 529 | } 530 | return ret.(krpc.Bep46Payload), err 531 | } 532 | -------------------------------------------------------------------------------- /gateway_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/anacrolix/generics" 5 | "github.com/anacrolix/torrent/metainfo" 6 | qt "github.com/frankban/quicktest" 7 | "net/url" 8 | "testing" 9 | ) 10 | 11 | func TestRedirectOldDomain(t *testing.T) { 12 | c := qt.New(t) 13 | test := func(host string, expected generics.Option[string]) { 14 | c.Check(redirectOldBtRoot(host), qt.Equals, expected) 15 | } 16 | test( 17 | "bt.btlink.anacrolix.link", 18 | generics.Some("btlink.anacrolix.link"), 19 | ) 20 | test( 21 | "hello.ih.bt.btlink.anacrolix.link", 22 | generics.Some("hello-ih.btlink.anacrolix.link"), 23 | ) 24 | test( 25 | "hello.ih.bt.btlink.anacrolix.link", 26 | generics.Some("hello-ih.btlink.anacrolix.link"), 27 | ) 28 | test( 29 | "cast.hello.pk.bt.btlink.anacrolix.link", 30 | generics.Some("cast.hello-pk.btlink.anacrolix.link"), 31 | ) 32 | test( 33 | "cast.hello.pk.bt", 34 | generics.Some("cast.hello-pk.btlink"), 35 | ) 36 | } 37 | 38 | func TestAddGatewayWebseedScheme(t *testing.T) { 39 | m := metainfo.Magnet{ 40 | Params: make(url.Values), 41 | } 42 | addGatewayWebseedToMagnet(&m, "9a0df2a4500d65ab0b58e4ad3ef7c55576ca88f1-ih.btlink.localhost:42080", "http", &url.URL{ 43 | Path: "/", 44 | RawQuery: "btlink-no-autoindex", 45 | }) 46 | qt.Assert(t, m.Params["ws"], qt.DeepEquals, []string{"http://9a0df2a4500d65ab0b58e4ad3ef7c55576ca88f1-ih.btlink.localhost:42080/"}) 47 | } 48 | -------------------------------------------------------------------------------- /gencert.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/x509" 6 | "crypto/x509/pkix" 7 | "encoding/pem" 8 | "fmt" 9 | "math/big" 10 | "os" 11 | "time" 12 | 13 | "github.com/anacrolix/bargle" 14 | ) 15 | 16 | // https://github.com/golang/go/issues/33310#issuecomment-537251383 17 | var maxSerialNumber = new(big.Int).SetBytes([]byte{127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}) 18 | 19 | func genCert() bargle.Command { 20 | var params struct { 21 | CommonName string `arg:"positional"` 22 | DnsNames []string `arg:"positional" arity:"*"` 23 | } 24 | cmd := bargle.FromStruct(¶ms) 25 | cmd.Desc = "sign a certificate" 26 | cmd.DefaultAction = func() (err error) { 27 | privKeyBytes, err := os.ReadFile("ca.key") 28 | if err != nil { 29 | return err 30 | } 31 | b, _ := pem.Decode(privKeyBytes) 32 | privKey, err := x509.ParsePKCS1PrivateKey(b.Bytes) 33 | if err != nil { 34 | return fmt.Errorf("parsing private key: %w", err) 35 | } 36 | caCertBytes, err := os.ReadFile("ca.pem") 37 | if err != nil { 38 | return err 39 | } 40 | b, _ = pem.Decode(caCertBytes) 41 | caCert, err := x509.ParseCertificate(b.Bytes) 42 | if err != nil { 43 | return fmt.Errorf("parsing ca cert: %w", err) 44 | } 45 | //caCert, err := tls.LoadX509KeyPair("ca.pem", "ca.key") 46 | //if err != nil { 47 | // return err 48 | //} 49 | serialNumber, err := rand.Int(rand.Reader, maxSerialNumber) 50 | if err != nil { 51 | return 52 | } 53 | cert, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{ 54 | Subject: pkix.Name{ 55 | Organization: []string{"btlink"}, 56 | OrganizationalUnit: []string{"root CA"}, 57 | CommonName: params.CommonName, 58 | }, 59 | SerialNumber: serialNumber, 60 | DNSNames: params.DnsNames, 61 | // https://stackoverflow.com/a/65239775/149482 62 | NotAfter: time.Now().AddDate(1, 0, 0), 63 | NotBefore: time.Now(), 64 | }, caCert, &privKey.PublicKey, privKey) 65 | if err != nil { 66 | return err 67 | } 68 | err = pem.Encode(os.Stdout, &pem.Block{ 69 | Type: "CERTIFICATE", 70 | Bytes: cert, 71 | }) 72 | return 73 | } 74 | return cmd 75 | } 76 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/anacrolix/btlink 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/anacrolix/bargle v0.0.0-20220622082028-6c0bfc8b614d 7 | github.com/anacrolix/dht/v2 v2.11.1-0.20211104092016-7295f2558a39 8 | github.com/anacrolix/envpprof v1.1.1 9 | github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 10 | github.com/anacrolix/missinggo/v2 v2.5.2 11 | github.com/anacrolix/torrent v1.35.1-0.20211104223025-f86af21cd2fe 12 | github.com/davecgh/go-spew v1.1.1 13 | github.com/dgraph-io/ristretto v0.1.0 14 | github.com/dustin/go-humanize v1.0.0 15 | github.com/frankban/quicktest v1.14.3 16 | github.com/honeycombio/honeycomb-opentelemetry-go v0.2.0 17 | github.com/honeycombio/opentelemetry-go-contrib/launcher v0.0.0-20220824095536-e0b3dd3fbfe7 18 | github.com/huandu/xstrings v1.3.2 19 | github.com/multiformats/go-base36 v0.1.0 20 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 21 | golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f 22 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 23 | golang.org/x/text v0.3.7 24 | ) 25 | 26 | require ( 27 | github.com/anacrolix/log v0.10.0 // indirect 28 | github.com/anacrolix/missinggo v1.3.0 // indirect 29 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect 30 | github.com/cenkalti/backoff/v4 v4.1.3 // indirect 31 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 32 | github.com/felixge/httpsnoop v1.0.3 // indirect 33 | github.com/go-logr/logr v1.2.3 // indirect 34 | github.com/go-logr/stdr v1.2.2 // indirect 35 | github.com/go-ole/go-ole v1.2.6 // indirect 36 | github.com/golang/glog v1.0.0 // indirect 37 | github.com/golang/protobuf v1.5.2 // indirect 38 | github.com/google/go-cmp v0.5.8 // indirect 39 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 40 | github.com/kr/pretty v0.3.0 // indirect 41 | github.com/kr/text v0.2.0 // indirect 42 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 43 | github.com/pkg/errors v0.9.1 // indirect 44 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 45 | github.com/rogpeppe/go-internal v1.8.0 // indirect 46 | github.com/sethvargo/go-envconfig v0.6.2 // indirect 47 | github.com/shirou/gopsutil/v3 v3.22.6 // indirect 48 | github.com/tklauser/go-sysconf v0.3.10 // indirect 49 | github.com/tklauser/numcpus v0.4.0 // indirect 50 | github.com/yusufpapurcu/wmi v1.2.2 // indirect 51 | go.opentelemetry.io/contrib/instrumentation/host v0.33.0 // indirect 52 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 // indirect 53 | go.opentelemetry.io/contrib/instrumentation/runtime v0.33.0 // indirect 54 | go.opentelemetry.io/contrib/propagators/b3 v1.8.0 // indirect 55 | go.opentelemetry.io/contrib/propagators/ot v1.8.0 // indirect 56 | go.opentelemetry.io/otel v1.9.0 // indirect 57 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0 // indirect 58 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 // indirect 59 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 // indirect 60 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0 // indirect 61 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0 // indirect 62 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0 // indirect 63 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0 // indirect 64 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0 // indirect 65 | go.opentelemetry.io/otel/metric v0.31.0 // indirect 66 | go.opentelemetry.io/otel/sdk v1.9.0 // indirect 67 | go.opentelemetry.io/otel/sdk/metric v0.31.0 // indirect 68 | go.opentelemetry.io/otel/trace v1.9.0 // indirect 69 | go.opentelemetry.io/proto/otlp v0.18.0 // indirect 70 | go.uber.org/atomic v1.7.0 // indirect 71 | go.uber.org/multierr v1.8.0 // indirect 72 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect 73 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 74 | google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 // indirect 75 | google.golang.org/grpc v1.47.0 // indirect 76 | google.golang.org/protobuf v1.28.0 // indirect 77 | ) 78 | 79 | replace golang.org/x/crypto => github.com/anacrolix/golang-crypto v0.0.0-20220603051934-c408a93b3ef5 80 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= 34 | crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4= 35 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 36 | filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= 37 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 38 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 39 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 40 | github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= 41 | github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= 42 | github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= 43 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 44 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 45 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 46 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 47 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 48 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 49 | github.com/anacrolix/bargle v0.0.0-20220622082028-6c0bfc8b614d h1:eSBxjJUsa4p5lCn8mlp6gOdzhZYZxqr4YHPk8Uwn1iQ= 50 | github.com/anacrolix/bargle v0.0.0-20220622082028-6c0bfc8b614d/go.mod h1:9xUiZbkh+94FbiIAL1HXpAIBa832f3Mp07rRPl5c5RQ= 51 | github.com/anacrolix/dht/v2 v2.11.1-0.20211104092016-7295f2558a39 h1:D4rt3vt6UNG8ZjPeU9vLkNdrEoZ8JvsfXEN0MdfEn/Q= 52 | github.com/anacrolix/dht/v2 v2.11.1-0.20211104092016-7295f2558a39/go.mod h1:GfoiqvbDg0um5RVTJYf5HdPLDgk6TQeWKnPDfQ4bmWw= 53 | github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= 54 | github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= 55 | github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= 56 | github.com/anacrolix/envpprof v1.1.1 h1:sHQCyj7HtiSfaZAzL2rJrQdyS7odLqlwO6nhk/tG/j8= 57 | github.com/anacrolix/envpprof v1.1.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= 58 | github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 h1:k4/h2B1gGF+PJGyGHxs8nmHHt1pzWXZWBj6jn4OBlRc= 59 | github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= 60 | github.com/anacrolix/golang-crypto v0.0.0-20220603051934-c408a93b3ef5 h1:4V795Y7WV+uSfFVZt+0VrM346ikpPXSARhqYRHafndA= 61 | github.com/anacrolix/golang-crypto v0.0.0-20220603051934-c408a93b3ef5/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 62 | github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= 63 | github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= 64 | github.com/anacrolix/log v0.10.0 h1:uz9XDnmsw8ZEO/TTRU03lL7I74PlgVRFszYqPZ39WNY= 65 | github.com/anacrolix/log v0.10.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= 66 | github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= 67 | github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo= 68 | github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y= 69 | github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw= 70 | github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc= 71 | github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= 72 | github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= 73 | github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= 74 | github.com/anacrolix/missinggo/v2 v2.5.2 h1:hT9z1wGNc512Z53giT71kuPUNUREH9OGc6ppdaDjZ3s= 75 | github.com/anacrolix/missinggo/v2 v2.5.2/go.mod h1:yNvsLrtZYRYCOI+KRH/JM8TodHjtIE/bjOGhQaLOWIE= 76 | github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= 77 | github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= 78 | github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= 79 | github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= 80 | github.com/anacrolix/tagflag v1.3.0 h1:5NI+9CniDnEH0BWA4UcQbERyFPjKJqZnVkItGVIDy/s= 81 | github.com/anacrolix/torrent v1.35.1-0.20211104223025-f86af21cd2fe h1:huBKxw5pTN0UNMRlGxA5/m8y2Z0EFZRGeTlTLndVVrE= 82 | github.com/anacrolix/torrent v1.35.1-0.20211104223025-f86af21cd2fe/go.mod h1:AN0TQiQgkINA6IqebbRD00AITMp3I66Tc83aU2AhL7A= 83 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 84 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 85 | github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 86 | github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= 87 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 88 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 89 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 90 | github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= 91 | github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= 92 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= 93 | github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= 94 | github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= 95 | github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 96 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 97 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 98 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 99 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 100 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 101 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 102 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 103 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 104 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 105 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 106 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 107 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 108 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 109 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 110 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 111 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 112 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 113 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 114 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 115 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 116 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 117 | github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= 118 | github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= 119 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= 120 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 121 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 122 | github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 123 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 124 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 125 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 126 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 127 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 128 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 129 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 130 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 131 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 132 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 133 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 134 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 135 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 136 | github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= 137 | github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 138 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 139 | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= 140 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 141 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 142 | github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= 143 | github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= 144 | github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= 145 | github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= 146 | github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= 147 | github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= 148 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 149 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 150 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 151 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 152 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 153 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 154 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 155 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 156 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 157 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 158 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 159 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 160 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 161 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 162 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 163 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 164 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 165 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 166 | github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= 167 | github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= 168 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 169 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 170 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 171 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 172 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 173 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 174 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 175 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 176 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 177 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 178 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 179 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 180 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 181 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 182 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 183 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 184 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 185 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 186 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 187 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 188 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 189 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 190 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 191 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 192 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 193 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 194 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 195 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 196 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 197 | github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 198 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 199 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 200 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 201 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 202 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 203 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 204 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 205 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 206 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 207 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 208 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 209 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 210 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 211 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 212 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 213 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 214 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 215 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 216 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 217 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 218 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 219 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 220 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 221 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 222 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 223 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 224 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 225 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 226 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 227 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 228 | github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 229 | github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 230 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 231 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 232 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 233 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= 234 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= 235 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 236 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 237 | github.com/honeycombio/honeycomb-opentelemetry-go v0.2.0 h1:9wpD/SH0kfZ6tgsjvo9TgF6h7miItybKc4d/b1Cfsqk= 238 | github.com/honeycombio/honeycomb-opentelemetry-go v0.2.0/go.mod h1:5bx8RY96PkSZpSMbKoQ/+M+vWSFIxlA0TfbRKV52SKY= 239 | github.com/honeycombio/opentelemetry-go-contrib/launcher v0.0.0-20220824095536-e0b3dd3fbfe7 h1:s3EUCVM97Q860+VMdh4og/jUx+aiIx1QirNT90OUhUg= 240 | github.com/honeycombio/opentelemetry-go-contrib/launcher v0.0.0-20220824095536-e0b3dd3fbfe7/go.mod h1:RQL6cMY8S6SD3hx1i9ssKrQCHY6xgZWP+MFtYePjukc= 241 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 242 | github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= 243 | github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= 244 | github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 245 | github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 246 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= 247 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 248 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 249 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 250 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 251 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 252 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 253 | github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 254 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 255 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 256 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 257 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 258 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 259 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 260 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 261 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 262 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 263 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 264 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 265 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 266 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 267 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 268 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 269 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 270 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 271 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 272 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 273 | github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= 274 | github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= 275 | github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= 276 | github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= 277 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 278 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 279 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 280 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 281 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 282 | github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 283 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 284 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 285 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 286 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 287 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 288 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 289 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 290 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 291 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 292 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 293 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 294 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 295 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 296 | github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 297 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 298 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 299 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 300 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 301 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 302 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 303 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 304 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 305 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 306 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 307 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 308 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 309 | github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 310 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 311 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 312 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 313 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 314 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 315 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 316 | github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= 317 | github.com/sethvargo/go-envconfig v0.6.2 h1:982sW5Fc4zj7GWCGbt+tfjvl5fOUm/zMZXLT/hBBfr0= 318 | github.com/sethvargo/go-envconfig v0.6.2/go.mod h1:00S1FAhRUuTNJazWBWcJGvEHOM+NO6DhoRMAOX7FY5o= 319 | github.com/shirou/gopsutil/v3 v3.22.6 h1:FnHOFOh+cYAM0C30P+zysPISzlknLC5Z1G4EAElznfQ= 320 | github.com/shirou/gopsutil/v3 v3.22.6/go.mod h1:EdIubSnZhbAvBS1yJ7Xi+AShB/hxwLHOMz4MCYz7yMs= 321 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 322 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 323 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 324 | github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 325 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= 326 | github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= 327 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 328 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 329 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 330 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 331 | github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 332 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 333 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 334 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 335 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 336 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 337 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 338 | github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 339 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 340 | github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= 341 | github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= 342 | github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= 343 | github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= 344 | github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= 345 | github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= 346 | github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= 347 | github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 348 | github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 349 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 350 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 351 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 352 | github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= 353 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 354 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 355 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 356 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 357 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 358 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 359 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 360 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 361 | go.opentelemetry.io/contrib/instrumentation/host v0.33.0 h1:2xYXI9OcljCsjyQDz83gGXpwp7n8bDOKEvPkRCGp/4c= 362 | go.opentelemetry.io/contrib/instrumentation/host v0.33.0/go.mod h1:fqnYDsZTir1qY4BGHDRkALl3gqmX+cbIAycU0TyUq+U= 363 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 h1:9NkMW03wwEzPtP/KciZ4Ozu/Uz5ZA7kfqXJIObnrjGU= 364 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0/go.mod h1:548ZsYzmT4PL4zWKRd8q/N4z0Wxzn/ZxUE+lkEpwWQA= 365 | go.opentelemetry.io/contrib/instrumentation/runtime v0.33.0 h1:YhjJQGhEoxXXKwH16MQEW37KgdZSACk+HyHnFtv/NcI= 366 | go.opentelemetry.io/contrib/instrumentation/runtime v0.33.0/go.mod h1:cu2qiP1YaeuJtMVDUuMJZPfCBqJr4GB9kkwXbg5knMA= 367 | go.opentelemetry.io/contrib/propagators/b3 v1.8.0 h1:l6+IOjo7VzRWuYeKM/uDUkKOQvG22jDV3l8zs9+f1w0= 368 | go.opentelemetry.io/contrib/propagators/b3 v1.8.0/go.mod h1:fqKNRj1Bkecfq8v1T1H0q5di1ZHZK8fuSNpVn56tlAU= 369 | go.opentelemetry.io/contrib/propagators/ot v1.8.0 h1:LJRQci6KUNa6Rtcn27c0QX0B3d10HSyrTt+sAYyBvnU= 370 | go.opentelemetry.io/contrib/propagators/ot v1.8.0/go.mod h1:xjICLXWlvu139XAY2jAchS8bFGN2MKIUMXZCzKrRFf0= 371 | go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw= 372 | go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= 373 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0 h1:ggqApEjDKczicksfvZUCxuvoyDmR6Sbm56LwiK8DVR0= 374 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.9.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= 375 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 h1:H0+xwv4shKw0gfj/ZqR13qO2N/dBQogB1OcRjJjV39Y= 376 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0/go.mod h1:nkenGD8vcvs0uN6WhR90ZVHQlgDsRmXicnNadMnk+XQ= 377 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 h1:BaQ2xM5cPmldVCMvbLoy5tcLUhXCtIhItDYBNw83B7Y= 378 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0/go.mod h1:VRr8tlXQEsTdesDCh0qBe2iKDWhpi3ZqDYw6VlZ8MhI= 379 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0 h1:MuEG0gG27QZQrqhNl0f7vQ5Nl03OQfFeDAqWkGt+1zM= 380 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0/go.mod h1:52qtPFDDaa0FaSyyzPnxWMehx2SZv0xuobTlNEZA2JA= 381 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0 h1:NN90Cuna0CnBg8YNu1Q0V35i2E8LDByFOwHRCq/ZP9I= 382 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.9.0/go.mod h1:0EsCXjZAiiZGnLdEUXM9YjCKuuLZMYyglh2QDXcYKVA= 383 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0 h1:00hCSGLIxdYK/Z7r8GkaX0QIlfvgU3tmnLlQvcnix6U= 384 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0/go.mod h1:twhIvtDQW2sWP1O2cT1N8nkSBgKCRZv2z6COTTBrf8Q= 385 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0 h1:FAF9l8Wjxi9Ad2k/vLTfHZyzXYX72C62wBGpV3G6AIo= 386 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.9.0/go.mod h1:smUdtylgc0YQiUr2PuifS4hBXhAS5xtR6WQhxP1wiNA= 387 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0 h1:0uV0qzHk48i1SF8qRI8odMYiwPOLh9gBhiJFpj8H6JY= 388 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.9.0/go.mod h1:Fl1iS5ZhWgXXXTdJMuBSVsS5nkL5XluHbg97kjOuYU4= 389 | go.opentelemetry.io/otel/metric v0.31.0 h1:6SiklT+gfWAwWUR0meEMxQBtihpiEs4c+vL9spDTqUs= 390 | go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= 391 | go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo= 392 | go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4= 393 | go.opentelemetry.io/otel/sdk/metric v0.31.0 h1:2sZx4R43ZMhJdteKAlKoHvRgrMp53V1aRxvEf5lCq8Q= 394 | go.opentelemetry.io/otel/sdk/metric v0.31.0/go.mod h1:fl0SmNnX9mN9xgU6OLYLMBMrNAsaZQi7qBwprwO3abk= 395 | go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc= 396 | go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= 397 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 398 | go.opentelemetry.io/proto/otlp v0.18.0 h1:W5hyXNComRa23tGpKwG+FRAc4rfF6ZUg1JReK+QHS80= 399 | go.opentelemetry.io/proto/otlp v0.18.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= 400 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 401 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 402 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 403 | go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= 404 | go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 405 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 406 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 407 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 408 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 409 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 410 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 411 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 412 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 413 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 414 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 415 | golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f h1:KK6mxegmt5hGJRcAnEDjSNLxIRhZxDcgwMbcO/lMCRM= 416 | golang.org/x/exp v0.0.0-20220602145555-4a0574d9293f/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= 417 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 418 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 419 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 420 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 421 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 422 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 423 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 424 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 425 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 426 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 427 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 428 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 429 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 430 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 431 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 432 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 433 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 434 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 435 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 436 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 437 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 438 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 439 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 440 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 441 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 442 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 443 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 444 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 445 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 446 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 447 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 448 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 449 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 450 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 451 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 452 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 453 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 454 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 455 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 456 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 457 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 458 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 459 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 460 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 461 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 462 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 463 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 464 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 465 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 466 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 467 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 468 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 469 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 470 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 471 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 472 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 473 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 474 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 475 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 476 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 477 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 478 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 479 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 480 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 481 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 482 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 483 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 484 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 485 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 486 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 487 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 488 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 489 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 490 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 491 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 492 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 493 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 494 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 495 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 496 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 497 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 498 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 499 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 500 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 501 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 502 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 503 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 504 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 505 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 506 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 507 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 508 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 509 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 510 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 511 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 512 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 513 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 514 | golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 515 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 516 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 517 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 521 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 527 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 528 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 529 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 530 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 531 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 532 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 533 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 534 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 535 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 536 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 537 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 538 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 539 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 540 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 541 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 542 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 543 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 544 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 545 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 546 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 547 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 548 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 549 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 550 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 551 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 552 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 553 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 554 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 555 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 556 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 557 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 558 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 559 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 560 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 561 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 562 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 563 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 564 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 565 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 566 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 567 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 568 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 569 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 570 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 571 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 572 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 573 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 574 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 575 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 576 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 577 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 578 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 579 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 580 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 581 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 582 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 583 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 584 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 585 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 586 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 587 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 588 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 589 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 590 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 591 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 592 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 593 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 594 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 595 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 596 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 597 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 598 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 599 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 600 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 601 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 602 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 603 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 604 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 605 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 606 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 607 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 608 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 609 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 610 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 611 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 612 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 613 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 614 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 615 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 616 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 617 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 618 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 619 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 620 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 621 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 622 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 623 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 624 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 625 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 626 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 627 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 628 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 629 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 630 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 631 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 632 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 633 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 634 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 635 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 636 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 637 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 638 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 639 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 640 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 641 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 642 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 643 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 644 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 645 | google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2 h1:z+R4M/SuyaRsj1zu3WC+nIQyfSrSIpuDcY01/R3uCtg= 646 | google.golang.org/genproto v0.0.0-20220112215332-a9c7c0acf9f2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 647 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 648 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 649 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 650 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 651 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 652 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 653 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 654 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 655 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 656 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 657 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 658 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 659 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 660 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 661 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 662 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 663 | google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 664 | google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= 665 | google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 666 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 667 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 668 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 669 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 670 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 671 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 672 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 673 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 674 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 675 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 676 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 677 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 678 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 679 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 680 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 681 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 682 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 683 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 684 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 685 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 686 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 687 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 688 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 689 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 690 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 691 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 692 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 693 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 694 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 695 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 696 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 697 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 698 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 699 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 700 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 701 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 702 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 703 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 704 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 705 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 706 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 707 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 708 | -------------------------------------------------------------------------------- /logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net/http" 7 | "unicode" 8 | ) 9 | 10 | type indentWriter struct { 11 | w io.Writer 12 | newLine bool 13 | indent []byte 14 | } 15 | 16 | func newIndentWriter(w io.Writer, indent string) io.Writer { 17 | return &indentWriter{w, true, []byte(indent)} 18 | } 19 | 20 | func (me *indentWriter) Write(p []byte) (n int, err error) { 21 | for len(p) != 0 { 22 | if me.newLine { 23 | _, err = me.w.Write(me.indent) 24 | // We intentionally do not include the inserted indent in the return count due to the 25 | // io.Writer contract. 26 | if err != nil { 27 | return 28 | } 29 | me.newLine = false 30 | } 31 | var nn int 32 | nn, err = me.w.Write(p[:1]) 33 | n += nn 34 | if err != nil { 35 | return 36 | } 37 | if p[0] == '\n' { 38 | me.newLine = true 39 | } 40 | p = p[1:] 41 | } 42 | return 43 | } 44 | 45 | func requestLogString(r *http.Request) []byte { 46 | var buf bytes.Buffer 47 | r.Write(newIndentWriter(&buf, " ")) 48 | return bytes.TrimRightFunc(buf.Bytes(), unicode.IsSpace) 49 | } 50 | 51 | func requestUrl(r *http.Request) string { 52 | u := *r.URL 53 | u.Host = r.Host 54 | u.Scheme = "http" 55 | if r.TLS != nil { 56 | u.Scheme = "https" 57 | } 58 | return u.String() 59 | } 60 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/ed25519" 6 | "crypto/tls" 7 | "encoding/base32" 8 | "encoding/base64" 9 | "encoding/hex" 10 | "errors" 11 | "fmt" 12 | "log" 13 | mathRand "math/rand" 14 | "net/http" 15 | "runtime" 16 | "strconv" 17 | "strings" 18 | "sync" 19 | "time" 20 | 21 | "github.com/anacrolix/bargle" 22 | "github.com/anacrolix/envpprof" 23 | "github.com/anacrolix/generics" 24 | "github.com/anacrolix/missinggo/v2/iter" 25 | "github.com/dgraph-io/ristretto" 26 | _ "github.com/honeycombio/honeycomb-opentelemetry-go" 27 | "github.com/honeycombio/opentelemetry-go-contrib/launcher" 28 | "github.com/multiformats/go-base36" 29 | "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 30 | "golang.org/x/crypto/acme/autocert" 31 | ) 32 | 33 | func main() { 34 | log.SetFlags(log.Flags() | log.Lshortfile) 35 | main := bargle.Main{} 36 | main.Defer(envpprof.Stop) 37 | type encoderFunc func([]byte) string 38 | encoderChoices := bargle.Choices[encoderFunc]{ 39 | "base64url": base64.URLEncoding.EncodeToString, 40 | "base36lc": base36.EncodeToStringLc, 41 | "base32hex": base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString, 42 | "hex": hex.EncodeToString, 43 | } 44 | main.Positionals = append(main.Positionals, 45 | bargle.Subcommand{Name: "proxy", Command: proxy()}, 46 | bargle.Subcommand{Name: "gencert", Command: genCert()}, 47 | bargle.Subcommand{Name: "extract-pubkey", Command: func() (cmd bargle.Command) { 48 | encoderChoice := bargle.NewChoice(encoderChoices) 49 | cmd.Options = append(cmd.Options, func() bargle.Param { 50 | m := bargle.NewUnaryOption(encoderChoice) 51 | m.AddLong("encoder") 52 | m.SetDefault("base36lc") 53 | return m.Make() 54 | }()) 55 | var seed []byte 56 | { 57 | var encodedSeed string 58 | m := &bargle.Positional{ 59 | Value: bargle.AutoUnmarshaler(&encodedSeed), 60 | Name: "private-key", 61 | Desc: "private key seed", 62 | AfterParseFunc: func(ctx bargle.Context) (err error) { 63 | seed, err = hex.DecodeString(encodedSeed) 64 | if err != nil { 65 | return 66 | } 67 | if len(seed) != ed25519.SeedSize { 68 | return fmt.Errorf("expected %v bytes, got %v", ed25519.SeedSize, len(seed)) 69 | } 70 | return nil 71 | }, 72 | } 73 | cmd.Positionals = append(cmd.Positionals, m) 74 | } 75 | cmd.DefaultAction = func() error { 76 | privKey := ed25519.NewKeyFromSeed(seed) 77 | fmt.Println(encoderChoice.Value()(privKey.Public().(ed25519.PublicKey))) 78 | return nil 79 | } 80 | return 81 | }()}, 82 | bargle.Subcommand{Name: "convert", Command: func() (cmd bargle.Command) { 83 | cmd.Desc = "convert between different encodings used in btlink" 84 | type decoderFunc func(string) ([]byte, error) 85 | decoderChoice := bargle.NewChoice(bargle.Choices[decoderFunc]{ 86 | "hex": hex.DecodeString, 87 | "base36": base36.DecodeString, 88 | "base32hex": base32.HexEncoding.DecodeString, 89 | }) 90 | decoderParam := bargle.NewUnaryOption(decoderChoice) 91 | decoderParam.AddLong("from") 92 | decoderParam.AddShort('f') 93 | decoderParam.SetDefault("hex") 94 | encoderChoice := &bargle.Choice[encoderFunc]{Choices: encoderChoices} 95 | encoderParam := bargle.NewUnaryOption(encoderChoice) 96 | encoderParam.AddLong("to") 97 | encoderParam.AddShort('t') 98 | encoderParam.SetRequired() 99 | cmd.Options = append(cmd.Options, 100 | decoderParam.Make(), 101 | encoderParam.Make()) 102 | var input generics.Option[string] 103 | cmd.Positionals = append(cmd.Positionals, &bargle.Positional{ 104 | Name: "input", 105 | Desc: "data to decode", 106 | Value: bargle.NewOption(&input, nil), 107 | }) 108 | cmd.DefaultAction = func() error { 109 | b, err := decoderChoice.Value()(input.Unwrap()) 110 | if err != nil { 111 | return fmt.Errorf("error decoding input: %w", err) 112 | } 113 | fmt.Println(encoderChoice.Value()(b)) 114 | return nil 115 | } 116 | return 117 | }()}, 118 | bargle.Subcommand{Name: "generate-keys", Command: func() (cmd bargle.Command) { 119 | cmd.Desc = "search for progressively shorter valid public keys" 120 | parallelism := runtime.NumCPU() 121 | cmd.Options = append(cmd.Options, func() bargle.Param { 122 | m := bargle.NewUnaryOption(bargle.AutoUnmarshaler(¶llelism)) 123 | m.AddLong("parallel") 124 | return m.Make() 125 | }()) 126 | cmd.DefaultAction = func() error { 127 | var ( 128 | mu sync.Mutex 129 | tries int64 130 | shortest = "" 131 | ) 132 | for range iter.N(parallelism) { 133 | go func() { 134 | getPair := func() func() ([]byte, []byte) { 135 | if true { 136 | seed := make([]byte, ed25519.SeedSize) 137 | return func() ([]byte, []byte) { 138 | mathRand.Read(seed) 139 | privKey := ed25519.NewKeyFromSeed(seed) 140 | pubKey := privKey[ed25519.SeedSize:] 141 | return pubKey, seed 142 | } 143 | } else { 144 | return func() ([]byte, []byte) { 145 | pubKey, privKey, _ := ed25519.GenerateKey(nil) 146 | return pubKey, privKey.Seed() 147 | } 148 | } 149 | }() 150 | for { 151 | pubKey, seed := getPair() 152 | base36PubKey := base36.EncodeToStringLc(pubKey) 153 | mu.Lock() 154 | if shortest == "" || len(base36PubKey) < len(shortest) { 155 | shortest = base36PubKey 156 | seedBase36Lc := base36.EncodeToStringLc(seed) 157 | fmt.Printf("%s %s\n", base36PubKey, seedBase36Lc) 158 | } 159 | tries++ 160 | mu.Unlock() 161 | } 162 | }() 163 | } 164 | var lastTries int64 165 | duration := time.Second 166 | for { 167 | time.Sleep(duration) 168 | mu.Lock() 169 | curTries := tries 170 | mu.Unlock() 171 | log.Printf("%v tries/s", (curTries-lastTries)/int64(duration/time.Second)) 172 | duration *= 2 173 | lastTries = curTries 174 | } 175 | } 176 | return 177 | }()}, 178 | ) 179 | main.Run() 180 | } 181 | 182 | func proxy() (cmd bargle.Command) { 183 | cmd.Desc = "run proxy/gateway combo" 184 | var ( 185 | confluenceHost string 186 | confluenceScheme string 187 | httpPortInt uint16 = 42080 188 | httpsPortInt uint16 = 44369 189 | gatewayDomains []string 190 | logRequestHeaders bool 191 | ) 192 | opt := bargle.NewUnaryOption(bargle.AutoUnmarshaler(&confluenceHost)) 193 | opt.AddLong("confluence-host") 194 | opt.AddShort('h') 195 | opt.SetRequired() 196 | cmd.Options = append(cmd.Options, opt.Make()) 197 | opt = bargle.NewUnaryOption(bargle.AutoUnmarshaler(&confluenceScheme)) 198 | opt.AddLong("confluence-scheme") 199 | opt.AddShort('s') 200 | opt.SetRequired() 201 | cmd.Options = append(cmd.Options, opt.Make()) 202 | opt = bargle.NewUnaryOption(bargle.AutoUnmarshaler(&httpPortInt)) 203 | opt.AddLong("http-port") 204 | cmd.Options = append(cmd.Options, opt.Make()) 205 | opt = bargle.NewUnaryOption(bargle.AutoUnmarshaler(&httpsPortInt)) 206 | opt.AddLong("https-port") 207 | cmd.Options = append(cmd.Options, opt.Make()) 208 | opt = bargle.NewUnaryOption(bargle.AutoUnmarshaler(&gatewayDomains)) 209 | opt.AddLong("gateway-domain") 210 | opt.AddShort('g') 211 | opt.Description(`whitelist of domain roots to issue certificates for`) 212 | cmd.Options = append(cmd.Options, opt.Make()) 213 | flagOpt := bargle.NewFlag(&logRequestHeaders) 214 | flagOpt.AddLong("log-request-headers") 215 | cmd.Options = append(cmd.Options, flagOpt.Make()) 216 | cmd.DefaultAction = func() error { 217 | shutdownTelemetry, err := launcher.ConfigureOpenTelemetry() 218 | if err != nil { 219 | err = fmt.Errorf("configuring open telemetry: %w", err) 220 | // Silly honeycomb helper has a newline in the error message. Not cool. 221 | log.Printf("%q", err) 222 | } else { 223 | defer shutdownTelemetry() 224 | } 225 | confluenceClientCert, err := tls.LoadX509KeyPair("confluence.pem", "confluence.pem") 226 | if err != nil { 227 | log.Printf("error loading confluence client cert: %v", err) 228 | } 229 | dhtItemCache, err := ristretto.NewCache(&ristretto.Config{ 230 | NumCounters: 30, 231 | // So we can trigger this sooner for testing. 232 | MaxCost: 3, 233 | BufferItems: 64, 234 | // Because we don't represent the cost of cache items using bytes, but ristretto will add 235 | // the internal cost for the key in bytes. 236 | IgnoreInternalCost: true, 237 | OnExit: func(val interface{}) { 238 | v := val.(*dhtItemCacheValue) 239 | log.Printf("value removed from dht item cache [target=%v]", v.target) 240 | }, 241 | }) 242 | if err != nil { 243 | return fmt.Errorf("new dht item cache: %w", err) 244 | } 245 | infoCache, err := ristretto.NewCache(&ristretto.Config{ 246 | NumCounters: 10000, 247 | MaxCost: 50e6, 248 | BufferItems: 64, 249 | }) 250 | if err != nil { 251 | return fmt.Errorf("new info cache: %w", err) 252 | } 253 | handler := gatewayHandler{ 254 | confluence: confluenceHandler{ 255 | confluenceHost: confluenceHost, 256 | confluenceScheme: confluenceScheme, 257 | confluenceTransport: otelhttp.NewTransport(&http.Transport{ 258 | TLSClientConfig: &tls.Config{ 259 | Certificates: []tls.Certificate{confluenceClientCert}, 260 | InsecureSkipVerify: true, 261 | }, 262 | }), 263 | }, 264 | dhtItemCache: dhtItemCache, 265 | dirPageTemplate: htmlTemplates.Lookup("dir.html"), 266 | metainfoCache: infoCache, 267 | } 268 | httpPort := strconv.FormatUint(uint64(httpPortInt), 10) // Make the default 42080 269 | httpAddr := ":" + httpPort 270 | httpsPort := strconv.FormatUint(uint64(httpsPortInt), 10) // Make sure default is 44369 271 | httpsAddr := ":" + httpsPort 272 | proxyMux := http.NewServeMux() 273 | proxyMux.HandleFunc("/.btlink/proxy.pac", func(w http.ResponseWriter, r *http.Request) { 274 | err := serveDynamicPac(w, r, httpPort, httpsPort) 275 | if err != nil { 276 | log.Printf("error serving dynamic pac: %v", err) 277 | } 278 | }) 279 | proxyMux.HandleFunc("/.btlink/rootca.pem", func(w http.ResponseWriter, r *http.Request) { 280 | http.ServeFile(w, r, "ca.pem") 281 | }) 282 | autocertManager := autocert.Manager{ 283 | Prompt: autocert.AcceptTOS, 284 | Cache: autocert.DirCache("autocert-cache"), 285 | HostPolicy: func(ctx context.Context, host string) error { 286 | // Note that this doesn't match a specific proxy domain (which we can't just use 287 | // Cloudflare for due to CONNECT). If the proxy domain isn't the same as one of the 288 | // gateway domains, more configuration is required here. There's also no checking that 289 | // the requested domain name is valid, which would reduce unnecessary certificate 290 | // issuance. Lastly, if there is rate-limiting applied, it might be worth looking into 291 | // ACME DNS challenges to obtain wildcard certificates. 292 | for _, gd := range gatewayDomains { 293 | if host == gd || strings.HasSuffix(host, "."+gd) { 294 | return nil 295 | } 296 | } 297 | return errors.New("no gateway domains matched") 298 | }, 299 | Email: "anacrolix+btlink@gmail.com", 300 | ShortSAN: "btlink.anacrolix.link", 301 | } 302 | proxyHandler := func(logPrefix string) http.Handler { 303 | return otelhttp.NewHandler( 304 | http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 305 | if logRequestHeaders { 306 | log.Printf("%v: received request\n%s", logPrefix, requestLogString(r)) 307 | } 308 | err := func() error { 309 | // Connect can be passed to a HTTPS proxy endpoint. We want to handle this 310 | // ourselves, so we loop it back too. This also works if we receive CONNECT over HTTPS. 311 | if r.Method == http.MethodConnect { 312 | // We serve TLS by looping back to the HTTPS handler on this host. 313 | log.Printf("handling proxy request for %q", requestUrl(r)) 314 | handleConnect(w, "localhost"+httpsAddr, r) 315 | return nil 316 | } 317 | if handler.ServeHTTP(w, r) { 318 | return nil 319 | } 320 | log.Printf("handling proxy request for %q", requestUrl(r)) 321 | proxyMux.ServeHTTP(w, r) 322 | return nil 323 | }() 324 | if err != nil { 325 | log.Printf("%v: error in proxy handler: %v", logPrefix, err) 326 | } 327 | }), 328 | logPrefix, 329 | ) 330 | } 331 | serverErrs := make(chan error, 2) 332 | go func() { 333 | log.Printf("starting http server at %q", httpAddr) 334 | err := http.ListenAndServe(httpAddr, autocertManager.HTTPHandler(proxyHandler("http server"))) 335 | log.Printf("http server returned: %v", err) 336 | serverErrs <- err 337 | }() 338 | if true { 339 | go func() { 340 | var certs []tls.Certificate 341 | cert, err := tls.LoadX509KeyPair("wildcard.bt.pem", "ca.key") 342 | if err != nil { 343 | log.Printf("error loading bt wildcard cert: %v", err) 344 | } else { 345 | certs = append(certs, cert) 346 | } 347 | cert, err = tls.LoadX509KeyPair("localhost.pem", "ca.key") 348 | if err != nil { 349 | log.Printf("error loading localhost cert: %v", err) 350 | } else { 351 | certs = append(certs, cert) 352 | } 353 | tlsConfig := &tls.Config{ 354 | GetCertificate: func(info *tls.ClientHelloInfo) (_ *tls.Certificate, err error) { 355 | for _, cert := range certs { 356 | if info.SupportsCertificate(&cert) == nil { 357 | return &cert, nil 358 | } 359 | } 360 | started := time.Now() 361 | defer func() { 362 | elapsed := time.Since(started) 363 | if elapsed > time.Second { 364 | go log.Printf("getting certificate for %q took %v: %v", info.ServerName, elapsed, err) 365 | } 366 | }() 367 | return autocertManager.GetCertificate(info) 368 | }, 369 | } 370 | s := http.Server{ 371 | Addr: httpsAddr, 372 | Handler: proxyHandler("tls http server"), 373 | TLSConfig: tlsConfig, 374 | // TODO: Test this with Safari using HTTPS then falling back to HTTP proxying. 375 | ReadHeaderTimeout: 5 * time.Second, 376 | } 377 | log.Printf("starting https server at %q", s.Addr) 378 | err = s.ListenAndServeTLS("", "") 379 | log.Printf("https server returned: %v", err) 380 | serverErrs <- err 381 | }() 382 | } 383 | return fmt.Errorf("server error: %w", <-serverErrs) 384 | } 385 | return 386 | } 387 | -------------------------------------------------------------------------------- /proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net" 9 | "net/http" 10 | "net/http/httputil" 11 | "sync" 12 | ) 13 | 14 | func handleConnect(w http.ResponseWriter, destAddr string, r *http.Request) { 15 | r.Body.Close() 16 | hijacker, ok := w.(http.Hijacker) 17 | if !ok { 18 | log.Printf("can't hijack response writer %v", w) 19 | // Probably tried over HTTP2, dumbass browsers... 20 | http.Error(w, "can't hijack response writer", http.StatusBadRequest) 21 | return 22 | } 23 | conn, err := net.Dial("tcp", destAddr) 24 | if err != nil { 25 | log.Printf("error dialling %q: %v", destAddr, err) 26 | http.Error(w, err.Error(), http.StatusInternalServerError) 27 | return 28 | } 29 | defer conn.Close() 30 | rConn, buf, err := hijacker.Hijack() 31 | if err != nil { 32 | log.Printf("error hijacking connect response: %v", err) 33 | http.Error(w, "error hijacking response writer: %v", http.StatusServiceUnavailable) 34 | return 35 | } 36 | defer rConn.Close() 37 | if buf.Reader.Buffered() != 0 || buf.Writer.Buffered() != 0 { 38 | log.Printf("hijacked connection has %v unread and %v unwritten", buf.Reader.Buffered(), buf.Writer.Buffered()) 39 | } 40 | rConn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) 41 | var wg sync.WaitGroup 42 | wg.Add(2) 43 | copyErrs := make(chan error, 2) 44 | go func() { 45 | defer wg.Done() 46 | _, err := io.Copy(rConn, conn) 47 | if err != nil { 48 | log.Printf("error copying from origin to proxy client: %v", err) 49 | } 50 | copyErrs <- err 51 | }() 52 | go func() { 53 | defer wg.Done() 54 | _, err := io.Copy(conn, rConn) 55 | if err != nil { 56 | log.Printf("error copying from proxy client to origin: %v", err) 57 | } 58 | copyErrs <- err 59 | }() 60 | //<-copyErrs 61 | wg.Wait() 62 | } 63 | 64 | func reverseProxy(w http.ResponseWriter, r *http.Request) { 65 | (&httputil.ReverseProxy{ 66 | Director: func(r *http.Request) { 67 | log.Printf("directing request for %v", r.URL) 68 | r.URL.Scheme = "https" 69 | r.URL.Host = r.Host 70 | }, 71 | }).ServeHTTP(w, r) 72 | } 73 | 74 | var proxyPacTmpl = htmlTemplates.Lookup("templates/pac.tmpl") 75 | 76 | type pacData struct { 77 | HttpProxy string 78 | HttpsProxy string 79 | RootDomain string 80 | } 81 | 82 | func serveDynamicPac(w http.ResponseWriter, r *http.Request, httpProxyPort string, httpsProxyPort string) error { 83 | host, _, err := net.SplitHostPort(r.Host) 84 | if err != nil { 85 | host = r.Host 86 | } 87 | // Chrome seems to ignore the inline, but does pay attention to the filename. It's just nice for 88 | // end users to be able to view what they're getting remotely. 89 | w.Header().Set("Content-Disposition", "inline; filename=btlink.pac") 90 | w.Header().Set("Content-Type", `application/x-ns-proxy-autoconfig`) 91 | err = proxyPacTmpl.Execute(w, pacData{ 92 | HttpProxy: net.JoinHostPort(host, httpProxyPort), 93 | HttpsProxy: net.JoinHostPort(host, httpsProxyPort), 94 | RootDomain: "." + rootDomain, 95 | }) 96 | if err != nil { 97 | err = fmt.Errorf("executing template: %w", err) 98 | } 99 | return err 100 | } 101 | -------------------------------------------------------------------------------- /proxy.pac: -------------------------------------------------------------------------------- 1 | // This file is for local PAC testing without having to restart the server or execute the dynamic 2 | // PAC template. 3 | function FindProxyForURL(url, host) { 4 | if ( 5 | dnsDomainIs(host, ".bt") 6 | ) { 7 | return "HTTPS localhost:44369"; 8 | } else { 9 | return "DIRECT"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /templates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "html/template" 6 | ) 7 | 8 | //go:embed templates 9 | var templatesFS embed.FS 10 | 11 | var htmlTemplates = template.Must( 12 | template.New("").Funcs(template.FuncMap{ 13 | "safeHTML": func(s string) template.HTML { 14 | return template.HTML(s) 15 | }, 16 | }).ParseFS(templatesFS, "templates/*")) 17 | -------------------------------------------------------------------------------- /templates/dir.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ template "common-head" }} 4 | 5 | 6 |

$ ls

7 | 8 | 9 | {{ range .Children -}} 10 | 11 | 12 | 13 | 14 | {{- end }} 15 |
FileSize
{{.Name}}{{.Size}}
16 |

This website as a: 17 | Magnet URI 18 |

19 | 20 | -------------------------------------------------------------------------------- /templates/gateway-root.html: -------------------------------------------------------------------------------- 1 | {{ define "google-analytics" -}} 2 | {{ safeHTML "" -}} 3 | 4 | 5 | 12 | {{- end }} 13 | {{ define "common-head" -}} 14 | 15 | {{ template "google-analytics" }} 16 | 18 | {{ end }} 19 | 20 | 21 | btlink root gateway domain 22 | {{ template "common-head" }} 23 | 24 | 25 | {{ with .JustUploaded }} 26 |

Upload result

27 | 32 | {{ if .Debug -}} 33 |
{{ .Debug }}
34 | {{ end }} 35 | {{ end }} 36 |

Uploader

37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 | 48 | 49 |
50 |
51 | 54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /templates/pac.tmpl: -------------------------------------------------------------------------------- 1 | function FindProxyForURL(url, host) { 2 | if ( 3 | dnsDomainIs(host, "{{ .RootDomain }}") 4 | ) { 5 | // Safari doesn't support HTTPS proxies. If the btlink root domain matched an actual gateway we 6 | // could append DIRECT here. 7 | return "HTTPS {{ .HttpsProxy }}; PROXY {{ .HttpProxy }}"; 8 | } else { 9 | return "DIRECT"; 10 | } 11 | } 12 | --------------------------------------------------------------------------------