prefs_test.go 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipn
  4. import (
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "net/netip"
  9. "os"
  10. "reflect"
  11. "strings"
  12. "testing"
  13. "time"
  14. "go4.org/mem"
  15. "tailscale.com/ipn/ipnstate"
  16. "tailscale.com/net/netaddr"
  17. "tailscale.com/tailcfg"
  18. "tailscale.com/tstest"
  19. "tailscale.com/types/key"
  20. "tailscale.com/types/opt"
  21. "tailscale.com/types/persist"
  22. "tailscale.com/types/preftype"
  23. "tailscale.com/util/syspolicy/policyclient"
  24. )
  25. func fieldsOf(t reflect.Type) (fields []string) {
  26. for i := range t.NumField() {
  27. fields = append(fields, t.Field(i).Name)
  28. }
  29. return
  30. }
  31. func TestPrefsEqual(t *testing.T) {
  32. tstest.PanicOnLog()
  33. prefsHandles := []string{
  34. "ControlURL",
  35. "RouteAll",
  36. "ExitNodeID",
  37. "ExitNodeIP",
  38. "AutoExitNode",
  39. "InternalExitNodePrior",
  40. "ExitNodeAllowLANAccess",
  41. "CorpDNS",
  42. "RunSSH",
  43. "RunWebClient",
  44. "WantRunning",
  45. "LoggedOut",
  46. "ShieldsUp",
  47. "AdvertiseTags",
  48. "Hostname",
  49. "NotepadURLs",
  50. "ForceDaemon",
  51. "Egg",
  52. "AdvertiseRoutes",
  53. "AdvertiseServices",
  54. "Sync",
  55. "NoSNAT",
  56. "NoStatefulFiltering",
  57. "NetfilterMode",
  58. "OperatorUser",
  59. "ProfileName",
  60. "AutoUpdate",
  61. "AppConnector",
  62. "PostureChecking",
  63. "NetfilterKind",
  64. "DriveShares",
  65. "RelayServerPort",
  66. "RelayServerStaticEndpoints",
  67. "AllowSingleHosts",
  68. "Persist",
  69. }
  70. if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) {
  71. t.Errorf("Prefs.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
  72. have, prefsHandles)
  73. }
  74. relayServerPort := func(port uint16) *uint16 {
  75. return &port
  76. }
  77. nets := func(strs ...string) (ns []netip.Prefix) {
  78. for _, s := range strs {
  79. n, err := netip.ParsePrefix(s)
  80. if err != nil {
  81. panic(err)
  82. }
  83. ns = append(ns, n)
  84. }
  85. return ns
  86. }
  87. aps := func(strs ...string) (ret []netip.AddrPort) {
  88. for _, s := range strs {
  89. n, err := netip.ParseAddrPort(s)
  90. if err != nil {
  91. panic(err)
  92. }
  93. ret = append(ret, n)
  94. }
  95. return ret
  96. }
  97. tests := []struct {
  98. a, b *Prefs
  99. want bool
  100. }{
  101. {
  102. &Prefs{},
  103. nil,
  104. false,
  105. },
  106. {
  107. nil,
  108. &Prefs{},
  109. false,
  110. },
  111. {
  112. &Prefs{},
  113. &Prefs{},
  114. true,
  115. },
  116. {
  117. &Prefs{ControlURL: "https://controlplane.tailscale.com"},
  118. &Prefs{ControlURL: "https://login.private.co"},
  119. false,
  120. },
  121. {
  122. &Prefs{ControlURL: "https://controlplane.tailscale.com"},
  123. &Prefs{ControlURL: "https://controlplane.tailscale.com"},
  124. true,
  125. },
  126. {
  127. &Prefs{RouteAll: true},
  128. &Prefs{RouteAll: false},
  129. false,
  130. },
  131. {
  132. &Prefs{RouteAll: true},
  133. &Prefs{RouteAll: true},
  134. true,
  135. },
  136. {
  137. &Prefs{ExitNodeID: "n1234"},
  138. &Prefs{},
  139. false,
  140. },
  141. {
  142. &Prefs{ExitNodeID: "n1234"},
  143. &Prefs{ExitNodeID: "n1234"},
  144. true,
  145. },
  146. {
  147. &Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
  148. &Prefs{},
  149. false,
  150. },
  151. {
  152. &Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
  153. &Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
  154. true,
  155. },
  156. {
  157. &Prefs{AutoExitNode: ""},
  158. &Prefs{AutoExitNode: "auto:any"},
  159. false,
  160. },
  161. {
  162. &Prefs{AutoExitNode: "auto:any"},
  163. &Prefs{AutoExitNode: "auto:any"},
  164. true,
  165. },
  166. {
  167. &Prefs{},
  168. &Prefs{ExitNodeAllowLANAccess: true},
  169. false,
  170. },
  171. {
  172. &Prefs{ExitNodeAllowLANAccess: true},
  173. &Prefs{ExitNodeAllowLANAccess: true},
  174. true,
  175. },
  176. {
  177. &Prefs{CorpDNS: true},
  178. &Prefs{CorpDNS: false},
  179. false,
  180. },
  181. {
  182. &Prefs{CorpDNS: true},
  183. &Prefs{CorpDNS: true},
  184. true,
  185. },
  186. {
  187. &Prefs{WantRunning: true},
  188. &Prefs{WantRunning: false},
  189. false,
  190. },
  191. {
  192. &Prefs{WantRunning: true},
  193. &Prefs{WantRunning: true},
  194. true,
  195. },
  196. {
  197. &Prefs{NoSNAT: true},
  198. &Prefs{NoSNAT: false},
  199. false,
  200. },
  201. {
  202. &Prefs{NoSNAT: true},
  203. &Prefs{NoSNAT: true},
  204. true,
  205. },
  206. {
  207. &Prefs{Hostname: "android-host01"},
  208. &Prefs{Hostname: "android-host02"},
  209. false,
  210. },
  211. {
  212. &Prefs{Hostname: ""},
  213. &Prefs{Hostname: ""},
  214. true,
  215. },
  216. {
  217. &Prefs{NotepadURLs: true},
  218. &Prefs{NotepadURLs: false},
  219. false,
  220. },
  221. {
  222. &Prefs{NotepadURLs: true},
  223. &Prefs{NotepadURLs: true},
  224. true,
  225. },
  226. {
  227. &Prefs{ShieldsUp: true},
  228. &Prefs{ShieldsUp: false},
  229. false,
  230. },
  231. {
  232. &Prefs{ShieldsUp: true},
  233. &Prefs{ShieldsUp: true},
  234. true,
  235. },
  236. {
  237. &Prefs{AdvertiseRoutes: nil},
  238. &Prefs{AdvertiseRoutes: []netip.Prefix{}},
  239. true,
  240. },
  241. {
  242. &Prefs{AdvertiseRoutes: []netip.Prefix{}},
  243. &Prefs{AdvertiseRoutes: []netip.Prefix{}},
  244. true,
  245. },
  246. {
  247. &Prefs{AdvertiseRoutes: nets("192.168.0.0/24", "10.1.0.0/16")},
  248. &Prefs{AdvertiseRoutes: nets("192.168.1.0/24", "10.2.0.0/16")},
  249. false,
  250. },
  251. {
  252. &Prefs{AdvertiseRoutes: nets("192.168.0.0/24", "10.1.0.0/16")},
  253. &Prefs{AdvertiseRoutes: nets("192.168.0.0/24", "10.2.0.0/16")},
  254. false,
  255. },
  256. {
  257. &Prefs{AdvertiseRoutes: nets("192.168.0.0/24", "10.1.0.0/16")},
  258. &Prefs{AdvertiseRoutes: nets("192.168.0.0/24", "10.1.0.0/16")},
  259. true,
  260. },
  261. {
  262. &Prefs{NetfilterMode: preftype.NetfilterOff},
  263. &Prefs{NetfilterMode: preftype.NetfilterOn},
  264. false,
  265. },
  266. {
  267. &Prefs{NetfilterMode: preftype.NetfilterOn},
  268. &Prefs{NetfilterMode: preftype.NetfilterOn},
  269. true,
  270. },
  271. {
  272. &Prefs{Persist: &persist.Persist{}},
  273. &Prefs{Persist: &persist.Persist{
  274. UserProfile: tailcfg.UserProfile{LoginName: "dave"},
  275. }},
  276. false,
  277. },
  278. {
  279. &Prefs{Persist: &persist.Persist{
  280. UserProfile: tailcfg.UserProfile{LoginName: "dave"},
  281. }},
  282. &Prefs{Persist: &persist.Persist{
  283. UserProfile: tailcfg.UserProfile{LoginName: "dave"},
  284. }},
  285. true,
  286. },
  287. {
  288. &Prefs{ProfileName: "work"},
  289. &Prefs{ProfileName: "work"},
  290. true,
  291. },
  292. {
  293. &Prefs{ProfileName: "work"},
  294. &Prefs{ProfileName: "home"},
  295. false,
  296. },
  297. {
  298. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)}},
  299. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: false, Apply: opt.NewBool(false)}},
  300. false,
  301. },
  302. {
  303. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(true)}},
  304. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)}},
  305. false,
  306. },
  307. {
  308. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)}},
  309. &Prefs{AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)}},
  310. true,
  311. },
  312. {
  313. &Prefs{AppConnector: AppConnectorPrefs{Advertise: true}},
  314. &Prefs{AppConnector: AppConnectorPrefs{Advertise: true}},
  315. true,
  316. },
  317. {
  318. &Prefs{AppConnector: AppConnectorPrefs{Advertise: true}},
  319. &Prefs{AppConnector: AppConnectorPrefs{Advertise: false}},
  320. false,
  321. },
  322. {
  323. &Prefs{PostureChecking: true},
  324. &Prefs{PostureChecking: true},
  325. true,
  326. },
  327. {
  328. &Prefs{PostureChecking: true},
  329. &Prefs{PostureChecking: false},
  330. false,
  331. },
  332. {
  333. &Prefs{NetfilterKind: "iptables"},
  334. &Prefs{NetfilterKind: "iptables"},
  335. true,
  336. },
  337. {
  338. &Prefs{NetfilterKind: "nftables"},
  339. &Prefs{NetfilterKind: ""},
  340. false,
  341. },
  342. {
  343. &Prefs{AdvertiseServices: []string{"svc:tux", "svc:xenia"}},
  344. &Prefs{AdvertiseServices: []string{"svc:tux", "svc:xenia"}},
  345. true,
  346. },
  347. {
  348. &Prefs{AdvertiseServices: []string{"svc:tux", "svc:xenia"}},
  349. &Prefs{AdvertiseServices: []string{"svc:tux", "svc:amelie"}},
  350. false,
  351. },
  352. {
  353. &Prefs{RelayServerPort: relayServerPort(0)},
  354. &Prefs{RelayServerPort: nil},
  355. false,
  356. },
  357. {
  358. &Prefs{RelayServerPort: relayServerPort(0)},
  359. &Prefs{RelayServerPort: relayServerPort(1)},
  360. false,
  361. },
  362. {
  363. &Prefs{RelayServerStaticEndpoints: aps("[2001:db8::1]:40000", "192.0.2.1:40000")},
  364. &Prefs{RelayServerStaticEndpoints: aps("[2001:db8::1]:40000", "192.0.2.1:40000")},
  365. true,
  366. },
  367. {
  368. &Prefs{RelayServerStaticEndpoints: aps("[2001:db8::1]:40000", "192.0.2.2:40000")},
  369. &Prefs{RelayServerStaticEndpoints: aps("[2001:db8::1]:40000", "192.0.2.1:40000")},
  370. false,
  371. },
  372. }
  373. for i, tt := range tests {
  374. got := tt.a.Equals(tt.b)
  375. if got != tt.want {
  376. t.Errorf("%d. Equal = %v; want %v", i, got, tt.want)
  377. }
  378. }
  379. }
  380. func checkPrefs(t *testing.T, p Prefs) {
  381. var err error
  382. var p2, p2c *Prefs
  383. var p2b *Prefs
  384. pp := p.Pretty()
  385. if pp == "" {
  386. t.Fatalf("default p.Pretty() failed\n")
  387. }
  388. t.Logf("\npp: %#v\n", pp)
  389. b := p.ToBytes()
  390. if len(b) == 0 {
  391. t.Fatalf("default p.ToBytes() failed\n")
  392. }
  393. if !p.Equals(&p) {
  394. t.Fatalf("p != p\n")
  395. }
  396. p2 = p.Clone()
  397. p2.RouteAll = true
  398. if p.Equals(p2) {
  399. t.Fatalf("p == p2\n")
  400. }
  401. p2b = new(Prefs)
  402. err = PrefsFromBytes(p2.ToBytes(), p2b)
  403. if err != nil {
  404. t.Fatalf("PrefsFromBytes(p2) failed: bytes=%q; err=%v\n", p2.ToBytes(), err)
  405. }
  406. p2b.normalizeOptBools()
  407. p2p := p2.Pretty()
  408. p2bp := p2b.Pretty()
  409. t.Logf("\np2p: %#v\np2bp: %#v\n", p2p, p2bp)
  410. if p2p != p2bp {
  411. t.Fatalf("p2p != p2bp\n%#v\n%#v\n", p2p, p2bp)
  412. }
  413. if !p2.Equals(p2b) {
  414. t.Fatalf("p2 != p2b\n%#v\n%#v\n", p2, p2b)
  415. }
  416. p2c = p2.Clone()
  417. if !p2b.Equals(p2c) {
  418. t.Fatalf("p2b != p2c\n")
  419. }
  420. }
  421. // PrefsFromBytes documents that it preserves fields unset in the JSON.
  422. // This verifies that stays true.
  423. func TestPrefsFromBytesPreservesOldValues(t *testing.T) {
  424. tests := []struct {
  425. name string
  426. old Prefs
  427. json []byte
  428. want Prefs
  429. }{
  430. {
  431. name: "preserve-control-url",
  432. old: Prefs{ControlURL: "https://foo"},
  433. json: []byte(`{"RouteAll": true}`),
  434. want: Prefs{ControlURL: "https://foo", RouteAll: true},
  435. },
  436. {
  437. name: "opt.Bool", // test that we don't normalize it early
  438. old: Prefs{Sync: "unset"},
  439. json: []byte(`{}`),
  440. want: Prefs{Sync: "unset"},
  441. },
  442. }
  443. for _, tt := range tests {
  444. t.Run(tt.name, func(t *testing.T) {
  445. old := tt.old // shallow
  446. err := PrefsFromBytes(tt.json, &old)
  447. if err != nil {
  448. t.Fatalf("PrefsFromBytes failed: %v", err)
  449. }
  450. if !old.Equals(&tt.want) {
  451. t.Fatalf("got %+v; want %+v", old, tt.want)
  452. }
  453. })
  454. }
  455. }
  456. func TestBasicPrefs(t *testing.T) {
  457. tstest.PanicOnLog()
  458. p := Prefs{
  459. ControlURL: "https://controlplane.tailscale.com",
  460. }
  461. checkPrefs(t, p)
  462. }
  463. func TestPrefsPersist(t *testing.T) {
  464. tstest.PanicOnLog()
  465. c := persist.Persist{
  466. UserProfile: tailcfg.UserProfile{
  467. LoginName: "[email protected]",
  468. },
  469. }
  470. p := Prefs{
  471. ControlURL: "https://controlplane.tailscale.com",
  472. CorpDNS: true,
  473. Persist: &c,
  474. }
  475. checkPrefs(t, p)
  476. }
  477. func TestPrefsPretty(t *testing.T) {
  478. tests := []struct {
  479. p Prefs
  480. os string
  481. want string
  482. }{
  483. {
  484. Prefs{},
  485. "linux",
  486. "Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}",
  487. },
  488. {
  489. Prefs{},
  490. "windows",
  491. "Prefs{ra=false dns=false want=false update=off Persist=nil}",
  492. },
  493. {
  494. Prefs{ShieldsUp: true},
  495. "windows",
  496. "Prefs{ra=false dns=false want=false shields=true update=off Persist=nil}",
  497. },
  498. {
  499. Prefs{},
  500. "windows",
  501. "Prefs{ra=false dns=false want=false update=off Persist=nil}",
  502. },
  503. {
  504. Prefs{
  505. NotepadURLs: true,
  506. },
  507. "windows",
  508. "Prefs{ra=false dns=false want=false notepad=true update=off Persist=nil}",
  509. },
  510. {
  511. Prefs{
  512. WantRunning: true,
  513. ForceDaemon: true, // server mode
  514. },
  515. "windows",
  516. "Prefs{ra=false dns=false want=true server=true update=off Persist=nil}",
  517. },
  518. {
  519. Prefs{
  520. WantRunning: true,
  521. ControlURL: "http://localhost:1234",
  522. AdvertiseTags: []string{"tag:foo", "tag:bar"},
  523. },
  524. "darwin",
  525. `Prefs{ra=false dns=false want=true tags=tag:foo,tag:bar url="http://localhost:1234" update=off Persist=nil}`,
  526. },
  527. {
  528. Prefs{
  529. Persist: &persist.Persist{
  530. PrivateNodeKey: key.NodePrivateFromRaw32(mem.B([]byte{1: 1, 31: 0})),
  531. },
  532. },
  533. "linux",
  534. `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist{o=, n=[B1VKl] u="" ak=-}}`,
  535. },
  536. {
  537. Prefs{
  538. ExitNodeIP: netip.MustParseAddr("1.2.3.4"),
  539. },
  540. "linux",
  541. `Prefs{ra=false dns=false want=false exit=1.2.3.4 lan=false routes=[] nf=off update=off Persist=nil}`,
  542. },
  543. {
  544. Prefs{
  545. ExitNodeID: tailcfg.StableNodeID("myNodeABC"),
  546. },
  547. "linux",
  548. `Prefs{ra=false dns=false want=false exit=myNodeABC lan=false routes=[] nf=off update=off Persist=nil}`,
  549. },
  550. {
  551. Prefs{
  552. ExitNodeID: tailcfg.StableNodeID("myNodeABC"),
  553. ExitNodeAllowLANAccess: true,
  554. },
  555. "linux",
  556. `Prefs{ra=false dns=false want=false exit=myNodeABC lan=true routes=[] nf=off update=off Persist=nil}`,
  557. },
  558. {
  559. Prefs{
  560. ExitNodeAllowLANAccess: true,
  561. },
  562. "linux",
  563. `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
  564. },
  565. {
  566. Prefs{
  567. Hostname: "foo",
  568. },
  569. "linux",
  570. `Prefs{ra=false dns=false want=false routes=[] nf=off host="foo" update=off Persist=nil}`,
  571. },
  572. {
  573. Prefs{
  574. AutoUpdate: AutoUpdatePrefs{
  575. Check: true,
  576. Apply: opt.NewBool(false),
  577. },
  578. },
  579. "linux",
  580. `Prefs{ra=false dns=false want=false routes=[] nf=off update=check Persist=nil}`,
  581. },
  582. {
  583. Prefs{
  584. AutoUpdate: AutoUpdatePrefs{
  585. Check: true,
  586. Apply: opt.NewBool(true),
  587. },
  588. },
  589. "linux",
  590. `Prefs{ra=false dns=false want=false routes=[] nf=off update=on Persist=nil}`,
  591. },
  592. {
  593. Prefs{
  594. AppConnector: AppConnectorPrefs{
  595. Advertise: true,
  596. },
  597. },
  598. "linux",
  599. `Prefs{ra=false dns=false want=false routes=[] nf=off update=off appconnector=advertise Persist=nil}`,
  600. },
  601. {
  602. Prefs{
  603. AppConnector: AppConnectorPrefs{
  604. Advertise: false,
  605. },
  606. },
  607. "linux",
  608. `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
  609. },
  610. {
  611. Prefs{
  612. NetfilterKind: "iptables",
  613. },
  614. "linux",
  615. `Prefs{ra=false dns=false want=false routes=[] nf=off netfilterKind=iptables update=off Persist=nil}`,
  616. },
  617. {
  618. Prefs{
  619. NetfilterKind: "",
  620. },
  621. "linux",
  622. `Prefs{ra=false dns=false want=false routes=[] nf=off update=off Persist=nil}`,
  623. },
  624. {
  625. Prefs{Sync: "false"},
  626. "linux",
  627. "Prefs{ra=false dns=false want=false sync=false routes=[] nf=off update=off Persist=nil}",
  628. },
  629. }
  630. for i, tt := range tests {
  631. got := tt.p.pretty(tt.os)
  632. if got != tt.want {
  633. t.Errorf("%d. wrong String:\n got: %s\nwant: %s\n", i, got, tt.want)
  634. }
  635. }
  636. }
  637. func TestLoadPrefsNotExist(t *testing.T) {
  638. bogusFile := fmt.Sprintf("/tmp/not-exist-%d", time.Now().UnixNano())
  639. p, err := LoadPrefsWindows(bogusFile)
  640. if errors.Is(err, os.ErrNotExist) {
  641. // expected.
  642. return
  643. }
  644. t.Fatalf("unexpected prefs=%#v, err=%v", p, err)
  645. }
  646. // TestLoadPrefsFileWithZeroInIt verifies that LoadPrefs handles corrupted input files.
  647. // See issue #954 for details.
  648. func TestLoadPrefsFileWithZeroInIt(t *testing.T) {
  649. f, err := os.CreateTemp("", "TestLoadPrefsFileWithZeroInIt")
  650. if err != nil {
  651. t.Fatal(err)
  652. }
  653. path := f.Name()
  654. if _, err := f.Write(jsonEscapedZero); err != nil {
  655. t.Fatal(err)
  656. }
  657. f.Close()
  658. defer os.Remove(path)
  659. p, err := LoadPrefsWindows(path)
  660. if errors.Is(err, os.ErrNotExist) {
  661. // expected.
  662. return
  663. }
  664. t.Fatalf("unexpected prefs=%#v, err=%v", p, err)
  665. }
  666. func TestMaskedPrefsSetsInternal(t *testing.T) {
  667. for _, f := range fieldsOf(reflect.TypeFor[MaskedPrefs]()) {
  668. if !strings.HasSuffix(f, "Set") || !strings.HasPrefix(f, "Internal") {
  669. continue
  670. }
  671. mp := new(MaskedPrefs)
  672. reflect.ValueOf(mp).Elem().FieldByName(f).SetBool(true)
  673. if !mp.SetsInternal() {
  674. t.Errorf("MaskedPrefs.%sSet=true but SetsInternal=false", f)
  675. }
  676. }
  677. }
  678. func TestMaskedPrefsFields(t *testing.T) {
  679. have := map[string]bool{}
  680. for _, f := range fieldsOf(reflect.TypeFor[Prefs]()) {
  681. switch f {
  682. case "Persist", "AllowSingleHosts":
  683. // These can't be edited.
  684. continue
  685. }
  686. have[f] = true
  687. }
  688. for _, f := range fieldsOf(reflect.TypeFor[MaskedPrefs]()) {
  689. if f == "Prefs" {
  690. continue
  691. }
  692. if !strings.HasSuffix(f, "Set") {
  693. t.Errorf("unexpected non-/Set$/ field %q", f)
  694. continue
  695. }
  696. bare := strings.TrimSuffix(f, "Set")
  697. _, ok := have[bare]
  698. if !ok {
  699. t.Errorf("no corresponding Prefs.%s field for MaskedPrefs.%s", bare, f)
  700. continue
  701. }
  702. delete(have, bare)
  703. }
  704. for f := range have {
  705. t.Errorf("missing MaskedPrefs.%sSet for Prefs.%s", f, f)
  706. }
  707. // And also make sure they line up in the right order, which
  708. // ApplyEdits assumes.
  709. pt := reflect.TypeFor[Prefs]()
  710. mt := reflect.TypeFor[MaskedPrefs]()
  711. for i := range mt.NumField() {
  712. name := mt.Field(i).Name
  713. if i == 0 {
  714. if name != "Prefs" {
  715. t.Errorf("first field of MaskedPrefs should be Prefs")
  716. }
  717. continue
  718. }
  719. prefName := pt.Field(i - 1).Name
  720. if prefName+"Set" != name {
  721. t.Errorf("MaskedField[%d] = %s; want %sSet", i-1, name, prefName)
  722. }
  723. }
  724. }
  725. func TestPrefsApplyEdits(t *testing.T) {
  726. tests := []struct {
  727. name string
  728. prefs *Prefs
  729. edit *MaskedPrefs
  730. want *Prefs
  731. }{
  732. {
  733. name: "no_change",
  734. prefs: &Prefs{
  735. Hostname: "foo",
  736. },
  737. edit: &MaskedPrefs{},
  738. want: &Prefs{
  739. Hostname: "foo",
  740. },
  741. },
  742. {
  743. name: "set1_decoy1",
  744. prefs: &Prefs{
  745. Hostname: "foo",
  746. },
  747. edit: &MaskedPrefs{
  748. Prefs: Prefs{
  749. Hostname: "bar",
  750. OperatorUser: "ignore-this", // not set
  751. },
  752. HostnameSet: true,
  753. },
  754. want: &Prefs{
  755. Hostname: "bar",
  756. },
  757. },
  758. {
  759. name: "set_several",
  760. prefs: &Prefs{},
  761. edit: &MaskedPrefs{
  762. Prefs: Prefs{
  763. Hostname: "bar",
  764. OperatorUser: "galaxybrain",
  765. },
  766. HostnameSet: true,
  767. OperatorUserSet: true,
  768. },
  769. want: &Prefs{
  770. Hostname: "bar",
  771. OperatorUser: "galaxybrain",
  772. },
  773. },
  774. }
  775. for _, tt := range tests {
  776. t.Run(tt.name, func(t *testing.T) {
  777. got := tt.prefs.Clone()
  778. got.ApplyEdits(tt.edit)
  779. if !got.Equals(tt.want) {
  780. gotj, _ := json.Marshal(got)
  781. wantj, _ := json.Marshal(tt.want)
  782. t.Errorf("fail.\n got: %s\nwant: %s\n", gotj, wantj)
  783. }
  784. })
  785. }
  786. }
  787. func TestMaskedPrefsPretty(t *testing.T) {
  788. tests := []struct {
  789. m *MaskedPrefs
  790. want string
  791. }{
  792. {
  793. m: &MaskedPrefs{},
  794. want: "MaskedPrefs{}",
  795. },
  796. {
  797. m: &MaskedPrefs{
  798. Prefs: Prefs{
  799. Hostname: "bar",
  800. OperatorUser: "galaxybrain",
  801. RouteAll: false,
  802. ExitNodeID: "foo",
  803. AdvertiseTags: []string{"tag:foo", "tag:bar"},
  804. NetfilterMode: preftype.NetfilterNoDivert,
  805. },
  806. RouteAllSet: true,
  807. HostnameSet: true,
  808. OperatorUserSet: true,
  809. ExitNodeIDSet: true,
  810. AdvertiseTagsSet: true,
  811. NetfilterModeSet: true,
  812. },
  813. want: `MaskedPrefs{RouteAll=false ExitNodeID="foo" AdvertiseTags=["tag:foo" "tag:bar"] Hostname="bar" NetfilterMode=nodivert OperatorUser="galaxybrain"}`,
  814. },
  815. {
  816. m: &MaskedPrefs{
  817. Prefs: Prefs{
  818. ExitNodeIP: netaddr.IPv4(100, 102, 104, 105),
  819. },
  820. ExitNodeIPSet: true,
  821. },
  822. want: `MaskedPrefs{ExitNodeIP=100.102.104.105}`,
  823. },
  824. {
  825. m: &MaskedPrefs{
  826. Prefs: Prefs{
  827. AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)},
  828. },
  829. AutoUpdateSet: AutoUpdatePrefsMask{CheckSet: true, ApplySet: false},
  830. },
  831. want: `MaskedPrefs{AutoUpdate={Check=true}}`,
  832. },
  833. {
  834. m: &MaskedPrefs{
  835. Prefs: Prefs{
  836. AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(true)},
  837. },
  838. AutoUpdateSet: AutoUpdatePrefsMask{CheckSet: true, ApplySet: true},
  839. },
  840. want: `MaskedPrefs{AutoUpdate={Check=true Apply=true}}`,
  841. },
  842. {
  843. m: &MaskedPrefs{
  844. Prefs: Prefs{
  845. AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(false)},
  846. },
  847. AutoUpdateSet: AutoUpdatePrefsMask{CheckSet: false, ApplySet: true},
  848. },
  849. want: `MaskedPrefs{AutoUpdate={Apply=false}}`,
  850. },
  851. {
  852. m: &MaskedPrefs{
  853. Prefs: Prefs{
  854. AutoUpdate: AutoUpdatePrefs{Check: true, Apply: opt.NewBool(true)},
  855. },
  856. AutoUpdateSet: AutoUpdatePrefsMask{CheckSet: false, ApplySet: false},
  857. },
  858. want: `MaskedPrefs{}`,
  859. },
  860. }
  861. for i, tt := range tests {
  862. got := tt.m.Pretty()
  863. if got != tt.want {
  864. t.Errorf("%d.\n got: %#q\nwant: %#q\n", i, got, tt.want)
  865. }
  866. }
  867. }
  868. func TestPrefsExitNode(t *testing.T) {
  869. var p *Prefs
  870. if p.AdvertisesExitNode() {
  871. t.Errorf("nil shouldn't advertise exit node")
  872. }
  873. p = NewPrefs()
  874. if p.AdvertisesExitNode() {
  875. t.Errorf("default shouldn't advertise exit node")
  876. }
  877. p.AdvertiseRoutes = []netip.Prefix{
  878. netip.MustParsePrefix("10.0.0.0/16"),
  879. }
  880. p.SetAdvertiseExitNode(true)
  881. if got, want := len(p.AdvertiseRoutes), 3; got != want {
  882. t.Errorf("routes = %d; want %d", got, want)
  883. }
  884. p.SetAdvertiseExitNode(true)
  885. if got, want := len(p.AdvertiseRoutes), 3; got != want {
  886. t.Errorf("routes = %d; want %d", got, want)
  887. }
  888. if !p.AdvertisesExitNode() {
  889. t.Errorf("not advertising after enable")
  890. }
  891. p.SetAdvertiseExitNode(false)
  892. if p.AdvertisesExitNode() {
  893. t.Errorf("advertising after disable")
  894. }
  895. if got, want := len(p.AdvertiseRoutes), 1; got != want {
  896. t.Errorf("routes = %d; want %d", got, want)
  897. }
  898. }
  899. func TestExitNodeIPOfArg(t *testing.T) {
  900. mustIP := netip.MustParseAddr
  901. tests := []struct {
  902. name string
  903. arg string
  904. st *ipnstate.Status
  905. want netip.Addr
  906. wantErr string
  907. }{
  908. {
  909. name: "ip_while_stopped_okay",
  910. arg: "1.2.3.4",
  911. st: &ipnstate.Status{
  912. BackendState: "Stopped",
  913. },
  914. want: mustIP("1.2.3.4"),
  915. },
  916. {
  917. name: "ip_not_found",
  918. arg: "1.2.3.4",
  919. st: &ipnstate.Status{
  920. BackendState: "Running",
  921. },
  922. wantErr: `no node found in netmap with IP 1.2.3.4`,
  923. },
  924. {
  925. name: "ip_is_self",
  926. arg: "1.2.3.4",
  927. st: &ipnstate.Status{
  928. TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
  929. },
  930. wantErr: "cannot use 1.2.3.4 as an exit node as it is a local IP address to this machine",
  931. },
  932. {
  933. name: "ip_is_self_when_backend_running",
  934. arg: "1.2.3.4",
  935. st: &ipnstate.Status{
  936. BackendState: "Running",
  937. TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
  938. },
  939. wantErr: "cannot use 1.2.3.4 as an exit node as it is a local IP address to this machine",
  940. },
  941. {
  942. name: "ip_not_exit",
  943. arg: "1.2.3.4",
  944. st: &ipnstate.Status{
  945. BackendState: "Running",
  946. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  947. key.NewNode().Public(): {
  948. TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
  949. },
  950. },
  951. },
  952. wantErr: `node 1.2.3.4 is not advertising an exit node`,
  953. },
  954. {
  955. name: "ip",
  956. arg: "1.2.3.4",
  957. st: &ipnstate.Status{
  958. BackendState: "Running",
  959. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  960. key.NewNode().Public(): {
  961. TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
  962. ExitNodeOption: true,
  963. },
  964. },
  965. },
  966. want: mustIP("1.2.3.4"),
  967. },
  968. {
  969. name: "no_match",
  970. arg: "unknown",
  971. st: &ipnstate.Status{MagicDNSSuffix: ".foo"},
  972. wantErr: `invalid value "unknown" for --exit-node; must be IP or unique node name`,
  973. },
  974. {
  975. name: "name",
  976. arg: "skippy",
  977. st: &ipnstate.Status{
  978. MagicDNSSuffix: ".foo",
  979. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  980. key.NewNode().Public(): {
  981. DNSName: "skippy.foo.",
  982. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  983. ExitNodeOption: true,
  984. },
  985. },
  986. },
  987. want: mustIP("1.0.0.2"),
  988. },
  989. {
  990. name: "name_fqdn",
  991. arg: "skippy.foo.",
  992. st: &ipnstate.Status{
  993. MagicDNSSuffix: ".foo",
  994. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  995. key.NewNode().Public(): {
  996. DNSName: "skippy.foo.",
  997. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  998. ExitNodeOption: true,
  999. },
  1000. },
  1001. },
  1002. want: mustIP("1.0.0.2"),
  1003. },
  1004. {
  1005. name: "name_not_exit",
  1006. arg: "skippy",
  1007. st: &ipnstate.Status{
  1008. MagicDNSSuffix: ".foo",
  1009. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  1010. key.NewNode().Public(): {
  1011. DNSName: "skippy.foo.",
  1012. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  1013. },
  1014. },
  1015. },
  1016. wantErr: `node "skippy" is not advertising an exit node`,
  1017. },
  1018. {
  1019. name: "name_wrong_fqdn",
  1020. arg: "skippy.bar.",
  1021. st: &ipnstate.Status{
  1022. MagicDNSSuffix: ".foo",
  1023. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  1024. key.NewNode().Public(): {
  1025. DNSName: "skippy.foo.",
  1026. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  1027. },
  1028. },
  1029. },
  1030. wantErr: `invalid value "skippy.bar." for --exit-node; must be IP or unique node name`,
  1031. },
  1032. {
  1033. name: "ambiguous",
  1034. arg: "skippy",
  1035. st: &ipnstate.Status{
  1036. MagicDNSSuffix: ".foo",
  1037. Peer: map[key.NodePublic]*ipnstate.PeerStatus{
  1038. key.NewNode().Public(): {
  1039. DNSName: "skippy.foo.",
  1040. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  1041. ExitNodeOption: true,
  1042. },
  1043. key.NewNode().Public(): {
  1044. DNSName: "SKIPPY.foo.",
  1045. TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
  1046. ExitNodeOption: true,
  1047. },
  1048. },
  1049. },
  1050. wantErr: `ambiguous exit node name "skippy"`,
  1051. },
  1052. }
  1053. for _, tt := range tests {
  1054. t.Run(tt.name, func(t *testing.T) {
  1055. got, err := exitNodeIPOfArg(tt.arg, tt.st)
  1056. if err != nil {
  1057. if err.Error() == tt.wantErr {
  1058. return
  1059. }
  1060. if tt.wantErr == "" {
  1061. t.Fatal(err)
  1062. }
  1063. t.Fatalf("error = %#q; want %#q", err, tt.wantErr)
  1064. }
  1065. if tt.wantErr != "" {
  1066. t.Fatalf("got %v; want error %#q", got, tt.wantErr)
  1067. }
  1068. if got != tt.want {
  1069. t.Fatalf("got %v; want %v", got, tt.want)
  1070. }
  1071. })
  1072. }
  1073. }
  1074. func TestControlURLOrDefault(t *testing.T) {
  1075. var p Prefs
  1076. polc := policyclient.NoPolicyClient{}
  1077. if got, want := p.ControlURLOrDefault(polc), DefaultControlURL; got != want {
  1078. t.Errorf("got %q; want %q", got, want)
  1079. }
  1080. p.ControlURL = "http://foo.bar"
  1081. if got, want := p.ControlURLOrDefault(polc), "http://foo.bar"; got != want {
  1082. t.Errorf("got %q; want %q", got, want)
  1083. }
  1084. p.ControlURL = "https://login.tailscale.com"
  1085. if got, want := p.ControlURLOrDefault(polc), DefaultControlURL; got != want {
  1086. t.Errorf("got %q; want %q", got, want)
  1087. }
  1088. }
  1089. func TestMaskedPrefsIsEmpty(t *testing.T) {
  1090. tests := []struct {
  1091. name string
  1092. mp *MaskedPrefs
  1093. wantEmpty bool
  1094. }{
  1095. {
  1096. name: "nil",
  1097. wantEmpty: true,
  1098. },
  1099. {
  1100. name: "empty",
  1101. wantEmpty: true,
  1102. mp: &MaskedPrefs{},
  1103. },
  1104. {
  1105. name: "no-masks",
  1106. wantEmpty: true,
  1107. mp: &MaskedPrefs{
  1108. Prefs: Prefs{
  1109. WantRunning: true,
  1110. },
  1111. },
  1112. },
  1113. {
  1114. name: "with-mask",
  1115. wantEmpty: false,
  1116. mp: &MaskedPrefs{
  1117. Prefs: Prefs{
  1118. WantRunning: true,
  1119. },
  1120. WantRunningSet: true,
  1121. },
  1122. },
  1123. }
  1124. for _, tc := range tests {
  1125. t.Run(tc.name, func(t *testing.T) {
  1126. got := tc.mp.IsEmpty()
  1127. if got != tc.wantEmpty {
  1128. t.Fatalf("mp.IsEmpty = %t; want %t", got, tc.wantEmpty)
  1129. }
  1130. })
  1131. }
  1132. }
  1133. func TestNotifyPrefsJSONRoundtrip(t *testing.T) {
  1134. var n Notify
  1135. if n.Prefs != nil && n.Prefs.Valid() {
  1136. t.Fatal("Prefs should not be valid at start")
  1137. }
  1138. b, err := json.Marshal(n)
  1139. if err != nil {
  1140. t.Fatal(err)
  1141. }
  1142. var n2 Notify
  1143. if err := json.Unmarshal(b, &n2); err != nil {
  1144. t.Fatal(err)
  1145. }
  1146. if n2.Prefs != nil && n2.Prefs.Valid() {
  1147. t.Fatal("Prefs should not be valid after deserialization")
  1148. }
  1149. }
  1150. // Verify that our Prefs type writes out an AllowSingleHosts field so we can
  1151. // downgrade to older versions that require it.
  1152. func TestPrefsDowngrade(t *testing.T) {
  1153. var p Prefs
  1154. j, err := json.Marshal(p)
  1155. if err != nil {
  1156. t.Fatal(err)
  1157. }
  1158. type oldPrefs struct {
  1159. AllowSingleHosts bool
  1160. }
  1161. var op oldPrefs
  1162. if err := json.Unmarshal(j, &op); err != nil {
  1163. t.Fatal(err)
  1164. }
  1165. if !op.AllowSingleHosts {
  1166. t.Fatal("AllowSingleHosts should be true")
  1167. }
  1168. }
  1169. func TestParseAutoExitNodeString(t *testing.T) {
  1170. tests := []struct {
  1171. name string
  1172. exitNodeID string
  1173. wantOk bool
  1174. wantExpr ExitNodeExpression
  1175. }{
  1176. {
  1177. name: "empty expr",
  1178. exitNodeID: "",
  1179. wantOk: false,
  1180. wantExpr: "",
  1181. },
  1182. {
  1183. name: "no auto prefix",
  1184. exitNodeID: "foo",
  1185. wantOk: false,
  1186. wantExpr: "",
  1187. },
  1188. {
  1189. name: "auto:any",
  1190. exitNodeID: "auto:any",
  1191. wantOk: true,
  1192. wantExpr: AnyExitNode,
  1193. },
  1194. {
  1195. name: "auto:foo",
  1196. exitNodeID: "auto:foo",
  1197. wantOk: true,
  1198. wantExpr: "foo",
  1199. },
  1200. {
  1201. name: "auto prefix but empty suffix",
  1202. exitNodeID: "auto:",
  1203. wantOk: false,
  1204. wantExpr: "",
  1205. },
  1206. {
  1207. name: "auto prefix no colon",
  1208. exitNodeID: "auto",
  1209. wantOk: false,
  1210. wantExpr: "",
  1211. },
  1212. }
  1213. for _, tt := range tests {
  1214. t.Run(tt.name, func(t *testing.T) {
  1215. gotExpr, gotOk := ParseAutoExitNodeString(tt.exitNodeID)
  1216. if gotOk != tt.wantOk || gotExpr != tt.wantExpr {
  1217. if tt.wantOk {
  1218. t.Fatalf("got %v (%q); want %v (%q)", gotOk, gotExpr, tt.wantOk, tt.wantExpr)
  1219. } else {
  1220. t.Fatalf("got %v (%q); want false", gotOk, gotExpr)
  1221. }
  1222. }
  1223. })
  1224. }
  1225. }