cloudflare/cloudflared
Publicmirrored from https://github.com/cloudflare/cloudflaredAvailable
tunneldns/tunnel.go
148lines · modecode
| 1 | package tunneldns |
| 2 | |
| 3 | import ( |
| 4 | "net" |
| 5 | "os" |
| 6 | "os/signal" |
| 7 | "strconv" |
| 8 | "sync" |
| 9 | "syscall" |
| 10 | |
| 11 | "github.com/cloudflare/cloudflared/log" |
| 12 | "github.com/cloudflare/cloudflared/metrics" |
| 13 | |
| 14 | "github.com/coredns/coredns/core/dnsserver" |
| 15 | "github.com/coredns/coredns/plugin" |
| 16 | "github.com/coredns/coredns/plugin/cache" |
| 17 | "github.com/pkg/errors" |
| 18 | "gopkg.in/urfave/cli.v2" |
| 19 | ) |
| 20 | |
| 21 | var logger = log.CreateLogger() |
| 22 | |
| 23 | // Listener is an adapter between CoreDNS server and Warp runnable |
| 24 | type Listener struct { |
| 25 | server *dnsserver.Server |
| 26 | wg sync.WaitGroup |
| 27 | } |
| 28 | |
| 29 | // Run implements a foreground runner |
| 30 | func Run(c *cli.Context) error { |
| 31 | metricsListener, err := net.Listen("tcp", c.String("metrics")) |
| 32 | if err != nil { |
| 33 | logger.WithError(err).Fatal("Failed to open the metrics listener") |
| 34 | } |
| 35 | |
| 36 | go metrics.ServeMetrics(metricsListener, nil, logger) |
| 37 | |
| 38 | listener, err := CreateListener(c.String("address"), uint16(c.Uint("port")), c.StringSlice("upstream")) |
| 39 | if err != nil { |
| 40 | logger.WithError(err).Errorf("Failed to create the listeners") |
| 41 | return err |
| 42 | } |
| 43 | |
| 44 | // Try to start the server |
| 45 | readySignal := make(chan struct{}) |
| 46 | err = listener.Start(readySignal) |
| 47 | if err != nil { |
| 48 | logger.WithError(err).Errorf("Failed to start the listeners") |
| 49 | return listener.Stop() |
| 50 | } |
| 51 | <-readySignal |
| 52 | |
| 53 | // Wait for signal |
| 54 | signals := make(chan os.Signal, 10) |
| 55 | signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) |
| 56 | defer signal.Stop(signals) |
| 57 | <-signals |
| 58 | |
| 59 | // Shut down server |
| 60 | err = listener.Stop() |
| 61 | if err != nil { |
| 62 | logger.WithError(err).Errorf("failed to stop") |
| 63 | } |
| 64 | return err |
| 65 | } |
| 66 | |
| 67 | // Create a CoreDNS server plugin from configuration |
| 68 | func createConfig(address string, port uint16, p plugin.Handler) *dnsserver.Config { |
| 69 | c := &dnsserver.Config{ |
| 70 | Zone: ".", |
| 71 | Transport: "dns", |
| 72 | ListenHosts: []string{address}, |
| 73 | Port: strconv.FormatUint(uint64(port), 10), |
| 74 | } |
| 75 | |
| 76 | c.AddPlugin(func(next plugin.Handler) plugin.Handler { return p }) |
| 77 | return c |
| 78 | } |
| 79 | |
| 80 | // Start blocks for serving requests |
| 81 | func (l *Listener) Start(readySignal chan struct{}) error { |
| 82 | defer close(readySignal) |
| 83 | logger.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server") |
| 84 | |
| 85 | // Start UDP listener |
| 86 | if udp, err := l.server.ListenPacket(); err == nil { |
| 87 | l.wg.Add(1) |
| 88 | go func() { |
| 89 | l.server.ServePacket(udp) |
| 90 | l.wg.Done() |
| 91 | }() |
| 92 | } else { |
| 93 | return errors.Wrap(err, "failed to create a UDP listener") |
| 94 | } |
| 95 | |
| 96 | // Start TCP listener |
| 97 | tcp, err := l.server.Listen() |
| 98 | if err == nil { |
| 99 | l.wg.Add(1) |
| 100 | go func() { |
| 101 | l.server.Serve(tcp) |
| 102 | l.wg.Done() |
| 103 | }() |
| 104 | } |
| 105 | |
| 106 | return errors.Wrap(err, "failed to create a TCP listener") |
| 107 | } |
| 108 | |
| 109 | // Stop signals server shutdown and blocks until completed |
| 110 | func (l *Listener) Stop() error { |
| 111 | if err := l.server.Stop(); err != nil { |
| 112 | return err |
| 113 | } |
| 114 | |
| 115 | l.wg.Wait() |
| 116 | return nil |
| 117 | } |
| 118 | |
| 119 | // CreateListener configures the server and bound sockets |
| 120 | func CreateListener(address string, port uint16, upstreams []string) (*Listener, error) { |
| 121 | // Build the list of upstreams |
| 122 | upstreamList := make([]Upstream, 0) |
| 123 | for _, url := range upstreams { |
| 124 | logger.WithField("url", url).Infof("Adding DNS upstream") |
| 125 | upstream, err := NewUpstreamHTTPS(url) |
| 126 | if err != nil { |
| 127 | return nil, errors.Wrap(err, "failed to create HTTPS upstream") |
| 128 | } |
| 129 | upstreamList = append(upstreamList, upstream) |
| 130 | } |
| 131 | |
| 132 | // Create a local cache with HTTPS proxy plugin |
| 133 | chain := cache.New() |
| 134 | chain.Next = ProxyPlugin{ |
| 135 | Upstreams: upstreamList, |
| 136 | } |
| 137 | |
| 138 | // Format an endpoint |
| 139 | endpoint := "dns://" + net.JoinHostPort(address, strconv.FormatUint(uint64(port), 10)) |
| 140 | |
| 141 | // Create the actual middleware server |
| 142 | server, err := dnsserver.NewServer(endpoint, []*dnsserver.Config{createConfig(address, port, NewMetricsPlugin(chain))}) |
| 143 | if err != nil { |
| 144 | return nil, err |
| 145 | } |
| 146 | |
| 147 | return &Listener{server: server}, nil |
| 148 | } |
| 149 | |