// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package tsdial provides a Dialer type that can dial out of tailscaled. package tsdial import ( "context" "errors" "net" "sync" "inet.af/netaddr" ) // Dialer dials out of tailscaled, while taking care of details while // handling the dozens of edge cases depending on the server mode // (TUN, netstack), the OS network sandboxing style (macOS/iOS // Extension, none), user-selected route acceptance prefs, etc. type Dialer struct { // UseNetstackForIP if non-nil is whether NetstackDialTCP (if // it's non-nil) should be used to dial the provided IP. UseNetstackForIP func(netaddr.IP) bool // NetstackDialTCP dials the provided IPPort using netstack. // If nil, it's not used. NetstackDialTCP func(context.Context, netaddr.IPPort) (net.Conn, error) mu sync.Mutex dns DNSMap } func (d *Dialer) SetDNSMap(m DNSMap) { d.mu.Lock() defer d.mu.Unlock() d.dns = m } func (d *Dialer) resolve(ctx context.Context, addr string) (netaddr.IPPort, error) { d.mu.Lock() dns := d.dns d.mu.Unlock() return dns.Resolve(ctx, addr) } func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { ipp, err := d.resolve(ctx, addr) if err != nil { return nil, err } if d.UseNetstackForIP != nil && d.UseNetstackForIP(ipp.IP()) { if d.NetstackDialTCP == nil { return nil, errors.New("Dialer not initialized correctly") } return d.NetstackDialTCP(ctx, ipp) } // TODO(bradfitz): netns, etc var stdDialer net.Dialer return stdDialer.DialContext(ctx, network, ipp.String()) }