cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2019.4.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tunneldns/https_upstream.go

106lines · modeblame

d06fc520Areg Harutyunyan8 years ago1package tunneldns
2
3import (
4"bytes"
62b1ab8cNick Vollmar7 years ago5"context"
d06fc520Areg Harutyunyan8 years ago6"crypto/tls"
7"fmt"
8"io/ioutil"
9"net/http"
10"net/url"
11"time"
12
13"github.com/miekg/dns"
14"github.com/pkg/errors"
15log "github.com/sirupsen/logrus"
16"golang.org/x/net/http2"
17)
18
19const (
20defaultTimeout = 5 * time.Second
21)
22
23// UpstreamHTTPS is the upstream implementation for DNS over HTTPS service
24type UpstreamHTTPS struct {
25client *http.Client
26endpoint *url.URL
27}
28
29// NewUpstreamHTTPS creates a new DNS over HTTPS upstream from hostname
30func NewUpstreamHTTPS(endpoint string) (Upstream, error) {
31u, err := url.Parse(endpoint)
32if err != nil {
33return nil, err
34}
35
36// Update TLS and HTTP client configuration
37tls := &tls.Config{ServerName: u.Hostname()}
38transport := &http.Transport{
39TLSClientConfig: tls,
40DisableCompression: true,
41MaxIdleConns: 1,
15d68a0eKane Dou7 years ago42Proxy: http.ProxyFromEnvironment,
d06fc520Areg Harutyunyan8 years ago43}
44http2.ConfigureTransport(transport)
45
46client := &http.Client{
47Timeout: defaultTimeout,
48Transport: transport,
49}
50
51return &UpstreamHTTPS{client: client, endpoint: u}, nil
52}
53
54// Exchange provides an implementation for the Upstream interface
55func (u *UpstreamHTTPS) Exchange(ctx context.Context, query *dns.Msg) (*dns.Msg, error) {
56queryBuf, err := query.Pack()
57if err != nil {
58return nil, errors.Wrap(err, "failed to pack DNS query")
59}
60
61// No content negotiation for now, use DNS wire format
62buf, backendErr := u.exchangeWireformat(queryBuf)
63if backendErr == nil {
64response := &dns.Msg{}
65if err := response.Unpack(buf); err != nil {
66return nil, errors.Wrap(err, "failed to unpack DNS response from body")
67}
68
69response.Id = query.Id
70return response, nil
71}
72
73log.WithError(backendErr).Errorf("failed to connect to an HTTPS backend %q", u.endpoint)
74return nil, backendErr
75}
76
77// Perform message exchange with the default UDP wireformat defined in current draft
78// https://datatracker.ietf.org/doc/draft-ietf-doh-dns-over-https
79func (u *UpstreamHTTPS) exchangeWireformat(msg []byte) ([]byte, error) {
80req, err := http.NewRequest("POST", u.endpoint.String(), bytes.NewBuffer(msg))
81if err != nil {
82return nil, errors.Wrap(err, "failed to create an HTTPS request")
83}
84
85req.Header.Add("Content-Type", "application/dns-udpwireformat")
86req.Host = u.endpoint.Hostname()
87
88resp, err := u.client.Do(req)
89if err != nil {
90return nil, errors.Wrap(err, "failed to perform an HTTPS request")
91}
92
93// Check response status code
94defer resp.Body.Close()
95if resp.StatusCode != http.StatusOK {
96return nil, fmt.Errorf("returned status code %d", resp.StatusCode)
97}
98
99// Read wireformat response from the body
100buf, err := ioutil.ReadAll(resp.Body)
101if err != nil {
102return nil, errors.Wrap(err, "failed to read the response body")
103}
104
105return buf, nil
106}