cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2019.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

tunneldns/https_upstream.go

105lines · 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,
42}
43http2.ConfigureTransport(transport)
44
45client := &http.Client{
46Timeout: defaultTimeout,
47Transport: transport,
48}
49
50return &UpstreamHTTPS{client: client, endpoint: u}, nil
51}
52
53// Exchange provides an implementation for the Upstream interface
54func (u *UpstreamHTTPS) Exchange(ctx context.Context, query *dns.Msg) (*dns.Msg, error) {
55queryBuf, err := query.Pack()
56if err != nil {
57return nil, errors.Wrap(err, "failed to pack DNS query")
58}
59
60// No content negotiation for now, use DNS wire format
61buf, backendErr := u.exchangeWireformat(queryBuf)
62if backendErr == nil {
63response := &dns.Msg{}
64if err := response.Unpack(buf); err != nil {
65return nil, errors.Wrap(err, "failed to unpack DNS response from body")
66}
67
68response.Id = query.Id
69return response, nil
70}
71
72log.WithError(backendErr).Errorf("failed to connect to an HTTPS backend %q", u.endpoint)
73return nil, backendErr
74}
75
76// Perform message exchange with the default UDP wireformat defined in current draft
77// https://datatracker.ietf.org/doc/draft-ietf-doh-dns-over-https
78func (u *UpstreamHTTPS) exchangeWireformat(msg []byte) ([]byte, error) {
79req, err := http.NewRequest("POST", u.endpoint.String(), bytes.NewBuffer(msg))
80if err != nil {
81return nil, errors.Wrap(err, "failed to create an HTTPS request")
82}
83
84req.Header.Add("Content-Type", "application/dns-udpwireformat")
85req.Host = u.endpoint.Hostname()
86
87resp, err := u.client.Do(req)
88if err != nil {
89return nil, errors.Wrap(err, "failed to perform an HTTPS request")
90}
91
92// Check response status code
93defer resp.Body.Close()
94if resp.StatusCode != http.StatusOK {
95return nil, fmt.Errorf("returned status code %d", resp.StatusCode)
96}
97
98// Read wireformat response from the body
99buf, err := ioutil.ReadAll(resp.Body)
100if err != nil {
101return nil, errors.Wrap(err, "failed to read the response body")
102}
103
104return buf, nil
105}