Compare commits
9 Commits
dsnet/http
...
v1.20.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
958917dce8 | ||
|
|
7c1a1aa5d9 | ||
|
|
90423bf3de | ||
|
|
0028a8d4d5 | ||
|
|
8519cab83d | ||
|
|
4cd062071c | ||
|
|
04a7f5066d | ||
|
|
a14d445fc7 | ||
|
|
1a4293c15c |
@@ -1 +1 @@
|
||||
1.19.0
|
||||
1.20.0
|
||||
|
||||
@@ -786,6 +786,16 @@ func TestUpdatePrefs(t *testing.T) {
|
||||
wantSimpleUp: true,
|
||||
wantJustEditMP: &ipn.MaskedPrefs{WantRunningSet: true},
|
||||
},
|
||||
{
|
||||
name: "just_edit_reset",
|
||||
flags: []string{"--reset"},
|
||||
curPrefs: &ipn.Prefs{
|
||||
ControlURL: ipn.DefaultControlURL,
|
||||
Persist: &persist.Persist{LoginName: "crawshaw.github"},
|
||||
},
|
||||
env: upCheckEnv{backendState: "Running"},
|
||||
wantJustEditMP: &ipn.MaskedPrefs{WantRunningSet: true},
|
||||
},
|
||||
{
|
||||
name: "control_synonym",
|
||||
flags: []string{},
|
||||
|
||||
@@ -413,7 +413,6 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
|
||||
|
||||
justEdit := env.backendState == ipn.Running.String() &&
|
||||
!env.upArgs.forceReauth &&
|
||||
!env.upArgs.reset &&
|
||||
env.upArgs.authKeyOrFile == "" &&
|
||||
!controlURLChanged &&
|
||||
!tagsChanged
|
||||
|
||||
@@ -375,7 +375,7 @@ func webHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data.AdvertiseExitNode = true
|
||||
} else {
|
||||
if data.AdvertiseRoutes != "" {
|
||||
data.AdvertiseRoutes = ","
|
||||
data.AdvertiseRoutes += ","
|
||||
}
|
||||
data.AdvertiseRoutes += r.String()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,71 @@
|
||||
|
||||
package dns
|
||||
|
||||
import "tailscale.com/types/logger"
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
type kv struct {
|
||||
k, v string
|
||||
}
|
||||
|
||||
func (kv kv) String() string {
|
||||
return fmt.Sprintf("%s=%s", kv.k, kv.v)
|
||||
}
|
||||
|
||||
func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) {
|
||||
return newOSConfigurator(logf, interfaceName,
|
||||
newOSConfigEnv{
|
||||
rcIsResolvd: rcIsResolvd,
|
||||
fs: directFS{},
|
||||
})
|
||||
}
|
||||
|
||||
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
|
||||
type newOSConfigEnv struct {
|
||||
fs directFS
|
||||
rcIsResolvd func(resolvConfContents []byte) bool
|
||||
}
|
||||
|
||||
func newOSConfigurator(logf logger.Logf, interfaceName string, env newOSConfigEnv) (ret OSConfigurator, err error) {
|
||||
var debug []kv
|
||||
dbg := func(k, v string) {
|
||||
debug = append(debug, kv{k, v})
|
||||
}
|
||||
defer func() {
|
||||
if ret != nil {
|
||||
dbg("ret", fmt.Sprintf("%T", ret))
|
||||
}
|
||||
logf("dns: %v", debug)
|
||||
}()
|
||||
|
||||
bs, err := env.fs.ReadFile(resolvConf)
|
||||
if os.IsNotExist(err) {
|
||||
dbg("rc", "missing")
|
||||
return newDirectManager(logf), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
|
||||
}
|
||||
|
||||
if env.rcIsResolvd(bs) {
|
||||
dbg("resolvd", "yes")
|
||||
return newResolvdManager(logf, interfaceName)
|
||||
}
|
||||
|
||||
dbg("resolvd", "missing")
|
||||
return newDirectManager(logf), nil
|
||||
}
|
||||
|
||||
func rcIsResolvd(resolvConfContents []byte) bool {
|
||||
// If we have the string "# resolvd:" in resolv.conf resolvd(8) is
|
||||
// managing things.
|
||||
if bytes.Contains(resolvConfContents, []byte("# resolvd:")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ package dns
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -34,6 +36,8 @@ const (
|
||||
versionKey = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
|
||||
)
|
||||
|
||||
var configureWSL, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_CONFIGURE_WSL"))
|
||||
|
||||
type windowsManager struct {
|
||||
logf logger.Logf
|
||||
guid string
|
||||
@@ -307,13 +311,15 @@ func (m windowsManager) SetDNS(cfg OSConfig) error {
|
||||
|
||||
// On initial setup of WSL, the restart caused by --shutdown is slow,
|
||||
// so we do it out-of-line.
|
||||
go func() {
|
||||
if err := m.wslManager.SetDNS(cfg); err != nil {
|
||||
m.logf("WSL SetDNS: %v", err) // continue
|
||||
} else {
|
||||
m.logf("WSL SetDNS: success")
|
||||
}
|
||||
}()
|
||||
if configureWSL {
|
||||
go func() {
|
||||
if err := m.wslManager.SetDNS(cfg); err != nil {
|
||||
m.logf("WSL SetDNS: %v", err) // continue
|
||||
} else {
|
||||
m.logf("WSL SetDNS: success")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
141
net/dns/resolvd.go
Normal file
141
net/dns/resolvd.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2020 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.
|
||||
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/dnsname"
|
||||
)
|
||||
|
||||
func newResolvdManager(logf logger.Logf, interfaceName string) (*resolvdManager, error) {
|
||||
return &resolvdManager{
|
||||
logf: logf,
|
||||
ifName: interfaceName,
|
||||
fs: directFS{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// resolvdManager is an OSConfigurator which uses route(1) to teach OpenBSD's
|
||||
// resolvd(8) about DNS servers.
|
||||
type resolvdManager struct {
|
||||
logf logger.Logf
|
||||
ifName string
|
||||
fs directFS
|
||||
}
|
||||
|
||||
func (m *resolvdManager) SetDNS(config OSConfig) error {
|
||||
args := []string{
|
||||
"nameserver",
|
||||
m.ifName,
|
||||
}
|
||||
|
||||
origResolv, err := m.readAndCopy(resolvConf, backupConf, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newResolvConf := removeSearchLines(origResolv)
|
||||
|
||||
for _, ns := range config.Nameservers {
|
||||
args = append(args, ns.String())
|
||||
}
|
||||
|
||||
var newSearch = []string{
|
||||
"search",
|
||||
}
|
||||
for _, s := range config.SearchDomains {
|
||||
newSearch = append(newSearch, s.WithoutTrailingDot())
|
||||
}
|
||||
|
||||
newResolvConf = append(newResolvConf, []byte(strings.Join(newSearch, " "))...)
|
||||
|
||||
err = m.fs.WriteFile(resolvConf, newResolvConf, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("/sbin/route", args...)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (m *resolvdManager) SupportsSplitDNS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *resolvdManager) GetBaseConfig() (OSConfig, error) {
|
||||
cfg, err := m.readResolvConf()
|
||||
if err != nil {
|
||||
return OSConfig{}, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (m *resolvdManager) Close() error {
|
||||
// resolvd handles teardown of nameservers so we only need to write back the original
|
||||
// config and be done.
|
||||
|
||||
_, err := m.readAndCopy(backupConf, resolvConf, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.fs.Remove(backupConf)
|
||||
}
|
||||
|
||||
func (m *resolvdManager) readAndCopy(a, b string, mode os.FileMode) ([]byte, error) {
|
||||
orig, err := m.fs.ReadFile(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = m.fs.WriteFile(b, orig, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orig, nil
|
||||
}
|
||||
|
||||
func (m resolvdManager) readResolvConf() (config OSConfig, err error) {
|
||||
b, err := m.fs.ReadFile(resolvConf)
|
||||
if err != nil {
|
||||
return OSConfig{}, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
// resolvd manages "nameserver" lines, we only need to handle
|
||||
// "search".
|
||||
if strings.HasPrefix(line, "search") {
|
||||
domain := strings.TrimPrefix(line, "search")
|
||||
domain = strings.TrimSpace(domain)
|
||||
fqdn, err := dnsname.ToFQDN(domain)
|
||||
if err != nil {
|
||||
return OSConfig{}, fmt.Errorf("parsing search domains %q: %w", line, err)
|
||||
}
|
||||
config.SearchDomains = append(config.SearchDomains, fqdn)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func removeSearchLines(orig []byte) []byte {
|
||||
re := regexp.MustCompile(`(?m)^search\s+.+$`)
|
||||
return re.ReplaceAll(orig, []byte(""))
|
||||
}
|
||||
@@ -529,6 +529,10 @@ func stubResolverForOS() (ip netaddr.IP, err error) {
|
||||
if c, ok := resolvConfCacheValue.Load().(resolvConfCache); ok && c.mod == cur.mod && c.size == cur.size {
|
||||
return c.ip, nil
|
||||
}
|
||||
// TODO(bradfitz): unify this /etc/resolv.conf parsing code with readResolv
|
||||
// in net/dns, which we can't use due to circular dependency reasons.
|
||||
// Move it to a leaf, including the OSConfig type (perhaps in its own dnstype
|
||||
// package?)
|
||||
err = lineread.File("/etc/resolv.conf", func(line []byte) error {
|
||||
if !ip.IsZero() {
|
||||
return nil
|
||||
@@ -537,6 +541,12 @@ func stubResolverForOS() (ip netaddr.IP, err error) {
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return nil
|
||||
}
|
||||
// Normalize tabs to spaces to simplify parsing code later.
|
||||
for i, b := range line {
|
||||
if b == '\t' {
|
||||
line[i] = ' '
|
||||
}
|
||||
}
|
||||
if mem.HasPrefix(mem.B(line), mem.S("nameserver ")) {
|
||||
s := strings.TrimSpace(strings.TrimPrefix(string(line), "nameserver "))
|
||||
ip, err = netaddr.ParseIP(s)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@@ -67,8 +66,7 @@ func socketMarkWorks() bool {
|
||||
// If it doesn't, we have to use SO_BINDTODEVICE on our sockets instead.
|
||||
func useSocketMark() bool {
|
||||
socketMarkWorksOnce.Do(func() {
|
||||
ipRuleWorks := exec.Command("ip", "rule").Run() == nil
|
||||
socketMarkWorksOnce.v = ipRuleWorks && socketMarkWorks()
|
||||
socketMarkWorksOnce.v = socketMarkWorks()
|
||||
})
|
||||
return socketMarkWorksOnce.v
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ import (
|
||||
// 23: 2021-08-25: DNSConfig.Routes values may be empty (for ExtraRecords support in 1.14.1+)
|
||||
// 24: 2021-09-18: MapResponse.Health from control to node; node shows in "tailscale status"
|
||||
// 25: 2021-11-01: MapResponse.Debug.Exit
|
||||
const CurrentMapRequestVersion = 25
|
||||
// 26: 2022-01-12: (nothing, just bumping for 1.20.0)
|
||||
const CurrentMapRequestVersion = 26
|
||||
|
||||
type StableID string
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/netmap"
|
||||
"tailscale.com/version/distro"
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
@@ -394,8 +395,14 @@ func (ns *Impl) shouldProcessInbound(p *packet.Parsed, t *tstun.Wrapper) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// setAmbientCapsRaw is non-nil on Linux for Synology, to run ping with
|
||||
// CAP_NET_RAW from tailscaled's binary.
|
||||
var setAmbientCapsRaw func(*exec.Cmd)
|
||||
|
||||
var userPingSem = syncs.NewSemaphore(20) // 20 child ping processes at once
|
||||
|
||||
var isSynology = runtime.GOOS == "linux" && distro.Get() == distro.Synology
|
||||
|
||||
// userPing tried to ping dstIP and if it succeeds, injects pingResPkt
|
||||
// into the tundev.
|
||||
//
|
||||
@@ -426,11 +433,21 @@ func (ns *Impl) userPing(dstIP netaddr.IP, pingResPkt []byte) {
|
||||
}
|
||||
err = exec.Command(ping, "-c", "1", "-w", "3", dstIP.String()).Run()
|
||||
default:
|
||||
err = exec.Command("ping", "-c", "1", "-W", "3", dstIP.String()).Run()
|
||||
ping := "ping"
|
||||
if isSynology {
|
||||
ping = "/bin/ping"
|
||||
}
|
||||
cmd := exec.Command(ping, "-c", "1", "-W", "3", dstIP.String())
|
||||
if isSynology && os.Getuid() != 0 {
|
||||
// On DSM7 we run as non-root and need to pass
|
||||
// CAP_NET_RAW if our binary has it.
|
||||
setAmbientCapsRaw(cmd)
|
||||
}
|
||||
err = cmd.Run()
|
||||
}
|
||||
d := time.Since(t0)
|
||||
if err != nil {
|
||||
ns.logf("exec ping of %v failed in %v", dstIP, d)
|
||||
ns.logf("exec ping of %v failed in %v: %v", dstIP, d, err)
|
||||
return
|
||||
}
|
||||
if debugNetstack {
|
||||
|
||||
20
wgengine/netstack/netstack_linux.go
Normal file
20
wgengine/netstack/netstack_linux.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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 netstack
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setAmbientCapsRaw = func(cmd *exec.Cmd) {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
AmbientCaps: []uintptr{unix.CAP_NET_RAW},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user