2020-02-05 22:16:58 +00:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
package ipn
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2020-02-17 21:48:04 +00:00
|
|
|
"net"
|
2020-02-05 22:16:58 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"tailscale.com/atomicfile"
|
|
|
|
"tailscale.com/control/controlclient"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Prefs struct {
|
|
|
|
RouteAll bool
|
|
|
|
AllowSingleHosts bool
|
|
|
|
CorpDNS bool
|
|
|
|
WantRunning bool
|
|
|
|
NotepadURLs bool
|
|
|
|
UsePacketFilter bool
|
2020-02-17 21:48:04 +00:00
|
|
|
AdvertiseRoutes []*net.IPNet
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
// The Persist field is named 'Config' in the file for backward
|
|
|
|
// compatibility with earlier versions.
|
|
|
|
// TODO(apenwarr): We should move this out of here, it's not a pref.
|
|
|
|
// We can maybe do that once we're sure which module should persist
|
|
|
|
// it (backend or frontend?)
|
|
|
|
Persist *controlclient.Persist `json:"Config"`
|
|
|
|
}
|
|
|
|
|
2020-02-03 18:35:52 +00:00
|
|
|
// IsEmpty reports whether p is nil or pointing to a Prefs zero value.
|
2020-02-17 23:01:23 +00:00
|
|
|
func (p *Prefs) IsEmpty() bool { return p == nil || p.Equals(&Prefs{}) }
|
2020-02-03 18:35:52 +00:00
|
|
|
|
2020-02-17 23:01:23 +00:00
|
|
|
func (p *Prefs) Pretty() string {
|
|
|
|
var pp string
|
|
|
|
if p.Persist != nil {
|
|
|
|
pp = p.Persist.Pretty()
|
2020-02-05 22:16:58 +00:00
|
|
|
} else {
|
2020-02-17 23:01:23 +00:00
|
|
|
pp = "Persist=nil"
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-17 21:48:04 +00:00
|
|
|
return fmt.Sprintf("Prefs{ra=%v mesh=%v dns=%v want=%v notepad=%v pf=%v routes=%v %v}",
|
2020-02-17 23:01:23 +00:00
|
|
|
p.RouteAll, p.AllowSingleHosts, p.CorpDNS, p.WantRunning,
|
|
|
|
p.NotepadURLs, p.UsePacketFilter, p.AdvertiseRoutes, pp)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 23:01:23 +00:00
|
|
|
func (p *Prefs) ToBytes() []byte {
|
|
|
|
data, err := json.MarshalIndent(p, "", "\t")
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Prefs marshal: %v\n", err)
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2020-02-17 23:01:23 +00:00
|
|
|
func (p *Prefs) Equals(p2 *Prefs) bool {
|
|
|
|
if p == nil && p2 == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if p == nil || p2 == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return p != nil && p2 != nil &&
|
|
|
|
p.RouteAll == p2.RouteAll &&
|
|
|
|
p.AllowSingleHosts == p2.AllowSingleHosts &&
|
|
|
|
p.CorpDNS == p2.CorpDNS &&
|
|
|
|
p.WantRunning == p2.WantRunning &&
|
|
|
|
p.NotepadURLs == p2.NotepadURLs &&
|
|
|
|
p.UsePacketFilter == p2.UsePacketFilter &&
|
|
|
|
compareIPNets(p.AdvertiseRoutes, p2.AdvertiseRoutes) &&
|
|
|
|
p.Persist.Equals(p2.Persist)
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareIPNets(a, b []*net.IPNet) bool {
|
|
|
|
if len(a) != len(b) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range a {
|
|
|
|
if !a[i].IP.Equal(b[i].IP) || !bytes.Equal(a[i].Mask, b[i].Mask) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewPrefs() Prefs {
|
|
|
|
return Prefs{
|
|
|
|
// Provide default values for options which are normally
|
|
|
|
// true, but might be missing from the json data for any
|
|
|
|
// reason. The json can still override them to false.
|
|
|
|
RouteAll: true,
|
|
|
|
AllowSingleHosts: true,
|
|
|
|
CorpDNS: true,
|
|
|
|
WantRunning: true,
|
|
|
|
UsePacketFilter: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func PrefsFromBytes(b []byte, enforceDefaults bool) (Prefs, error) {
|
2020-02-17 23:01:23 +00:00
|
|
|
p := NewPrefs()
|
2020-02-05 22:16:58 +00:00
|
|
|
if len(b) == 0 {
|
2020-02-17 23:01:23 +00:00
|
|
|
return p, nil
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
persist := &controlclient.Persist{}
|
|
|
|
err := json.Unmarshal(b, persist)
|
|
|
|
if err == nil && (persist.Provider != "" || persist.LoginName != "") {
|
|
|
|
// old-style relaynode config; import it
|
2020-02-17 23:01:23 +00:00
|
|
|
p.Persist = persist
|
2020-02-05 22:16:58 +00:00
|
|
|
} else {
|
2020-02-17 23:01:23 +00:00
|
|
|
err = json.Unmarshal(b, &p)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Prefs parse: %v: %v\n", err, b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if enforceDefaults {
|
2020-02-17 23:01:23 +00:00
|
|
|
p.RouteAll = true
|
|
|
|
p.AllowSingleHosts = true
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-17 23:01:23 +00:00
|
|
|
return p, err
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 23:01:23 +00:00
|
|
|
func (p *Prefs) Copy() *Prefs {
|
|
|
|
p2, err := PrefsFromBytes(p.ToBytes(), false)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Prefs was uncopyable: %v\n", err)
|
|
|
|
}
|
2020-02-17 23:01:23 +00:00
|
|
|
return &p2
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 23:58:40 +00:00
|
|
|
func LoadPrefs(filename string, enforceDefaults bool) *Prefs {
|
2020-02-05 22:16:58 +00:00
|
|
|
log.Printf("Loading prefs %v\n", filename)
|
|
|
|
data, err := ioutil.ReadFile(filename)
|
2020-02-17 23:01:23 +00:00
|
|
|
p := NewPrefs()
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Read: %v: %v\n", filename, err)
|
|
|
|
goto fail
|
|
|
|
}
|
2020-02-17 23:01:23 +00:00
|
|
|
p, err = PrefsFromBytes(data, enforceDefaults)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Parse: %v: %v\n", filename, err)
|
|
|
|
goto fail
|
|
|
|
}
|
|
|
|
goto post
|
|
|
|
fail:
|
|
|
|
log.Printf("failed to load config. Generating a new one.\n")
|
2020-02-17 23:01:23 +00:00
|
|
|
p = NewPrefs()
|
|
|
|
p.WantRunning = true
|
2020-02-05 22:16:58 +00:00
|
|
|
post:
|
|
|
|
// Update: we changed our minds :)
|
|
|
|
// Versabank would like to persist the setting across reboots, for now,
|
|
|
|
// because they don't fully trust the system and want to be able to
|
|
|
|
// leave it turned off when not in use. Eventually we need to make
|
|
|
|
// all motivation for this go away.
|
|
|
|
if false {
|
|
|
|
// Usability note: we always want WantRunning = true on startup.
|
|
|
|
// That way, if someone accidentally disables their VPN and doesn't
|
|
|
|
// know how, rebooting will fix it.
|
|
|
|
// We still persist WantRunning just in case we change our minds on
|
|
|
|
// this topic.
|
2020-02-17 23:01:23 +00:00
|
|
|
p.WantRunning = true
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-17 23:01:23 +00:00
|
|
|
log.Printf("Loaded prefs %v %v\n", filename, p.Pretty())
|
|
|
|
return &p
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 23:01:23 +00:00
|
|
|
func SavePrefs(filename string, p *Prefs) {
|
|
|
|
log.Printf("Saving prefs %v %v\n", filename, p.Pretty())
|
|
|
|
data := p.ToBytes()
|
2020-02-05 22:16:58 +00:00
|
|
|
os.MkdirAll(filepath.Dir(filename), 0700)
|
|
|
|
if err := atomicfile.WriteFile(filename, data, 0666); err != nil {
|
|
|
|
log.Printf("SavePrefs: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|