resolvconf_unix_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. //go:build !windows
  2. package resolvconf
  3. import (
  4. "bytes"
  5. "os"
  6. "strings"
  7. "testing"
  8. "github.com/opencontainers/go-digest"
  9. "gotest.tools/v3/assert"
  10. is "gotest.tools/v3/assert/cmp"
  11. )
  12. func TestGet(t *testing.T) {
  13. actual, err := Get()
  14. if err != nil {
  15. t.Fatal(err)
  16. }
  17. expected, err := os.ReadFile(Path())
  18. if err != nil {
  19. t.Fatal(err)
  20. }
  21. if !bytes.Equal(actual.Content, expected) {
  22. t.Errorf("%s and GetResolvConf have different content.", Path())
  23. }
  24. hash := digest.FromBytes(expected)
  25. if !bytes.Equal(actual.Hash, []byte(hash)) {
  26. t.Errorf("%s and GetResolvConf have different hashes.", Path())
  27. }
  28. }
  29. func TestGetNameservers(t *testing.T) {
  30. for _, tc := range []struct {
  31. input string
  32. result []string
  33. }{
  34. {
  35. input: ``,
  36. },
  37. {
  38. input: `search example.com`,
  39. },
  40. {
  41. input: ` nameserver 1.2.3.4 `,
  42. result: []string{"1.2.3.4"},
  43. },
  44. {
  45. input: `
  46. nameserver 1.2.3.4
  47. nameserver 40.3.200.10
  48. search example.com`,
  49. result: []string{"1.2.3.4", "40.3.200.10"},
  50. },
  51. {
  52. input: `nameserver 1.2.3.4
  53. search example.com
  54. nameserver 4.30.20.100`,
  55. result: []string{"1.2.3.4", "4.30.20.100"},
  56. },
  57. {
  58. input: `search example.com
  59. nameserver 1.2.3.4
  60. #nameserver 4.3.2.1`,
  61. result: []string{"1.2.3.4"},
  62. },
  63. {
  64. input: `search example.com
  65. nameserver 1.2.3.4 # not 4.3.2.1`,
  66. result: []string{"1.2.3.4"},
  67. },
  68. } {
  69. test := GetNameservers([]byte(tc.input), IP)
  70. if !strSlicesEqual(test, tc.result) {
  71. t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input)
  72. }
  73. }
  74. }
  75. func TestGetNameserversAsCIDR(t *testing.T) {
  76. for _, tc := range []struct {
  77. input string
  78. result []string
  79. }{
  80. {
  81. input: ``,
  82. },
  83. {
  84. input: `search example.com`,
  85. },
  86. {
  87. input: ` nameserver 1.2.3.4 `,
  88. result: []string{"1.2.3.4/32"},
  89. },
  90. {
  91. input: `
  92. nameserver 1.2.3.4
  93. nameserver 40.3.200.10
  94. search example.com`,
  95. result: []string{"1.2.3.4/32", "40.3.200.10/32"},
  96. },
  97. {
  98. input: `nameserver 1.2.3.4
  99. search example.com
  100. nameserver 4.30.20.100`,
  101. result: []string{"1.2.3.4/32", "4.30.20.100/32"},
  102. },
  103. {
  104. input: `search example.com
  105. nameserver 1.2.3.4
  106. #nameserver 4.3.2.1`,
  107. result: []string{"1.2.3.4/32"},
  108. },
  109. {
  110. input: `search example.com
  111. nameserver 1.2.3.4 # not 4.3.2.1`,
  112. result: []string{"1.2.3.4/32"},
  113. },
  114. {
  115. input: `nameserver fd6f:c490:ec68::1`,
  116. result: []string{"fd6f:c490:ec68::1/128"},
  117. },
  118. {
  119. input: `nameserver fe80::1234%eth0`,
  120. result: []string{"fe80::1234/128"},
  121. },
  122. } {
  123. test := GetNameserversAsCIDR([]byte(tc.input))
  124. if !strSlicesEqual(test, tc.result) {
  125. t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input)
  126. }
  127. }
  128. }
  129. func TestGetSearchDomains(t *testing.T) {
  130. for _, tc := range []struct {
  131. input string
  132. result []string
  133. }{
  134. {
  135. input: ``,
  136. },
  137. {
  138. input: `# ignored`,
  139. },
  140. {
  141. input: `search example.com`,
  142. result: []string{"example.com"},
  143. },
  144. {
  145. input: `search example.com # notignored`,
  146. result: []string{"example.com", "#", "notignored"},
  147. },
  148. {
  149. input: ` search example.com `,
  150. result: []string{"example.com"},
  151. },
  152. {
  153. input: ` search example.com # notignored`,
  154. result: []string{"example.com", "#", "notignored"},
  155. },
  156. {
  157. input: `search foo.example.com example.com`,
  158. result: []string{"foo.example.com", "example.com"},
  159. },
  160. {
  161. input: ` search foo.example.com example.com `,
  162. result: []string{"foo.example.com", "example.com"},
  163. },
  164. {
  165. input: ` search foo.example.com example.com # notignored`,
  166. result: []string{"foo.example.com", "example.com", "#", "notignored"},
  167. },
  168. {
  169. input: `nameserver 1.2.3.4
  170. search foo.example.com example.com`,
  171. result: []string{"foo.example.com", "example.com"},
  172. },
  173. {
  174. input: `nameserver 1.2.3.4
  175. search dup1.example.com dup2.example.com
  176. search foo.example.com example.com`,
  177. result: []string{"foo.example.com", "example.com"},
  178. },
  179. {
  180. input: `nameserver 1.2.3.4
  181. search foo.example.com example.com
  182. nameserver 4.30.20.100`,
  183. result: []string{"foo.example.com", "example.com"},
  184. },
  185. {
  186. input: `domain an.example`,
  187. result: []string{"an.example"},
  188. },
  189. } {
  190. test := GetSearchDomains([]byte(tc.input))
  191. if !strSlicesEqual(test, tc.result) {
  192. t.Errorf("Wrong search domain string {%s} should be %v. Input: %s", test, tc.result, tc.input)
  193. }
  194. }
  195. }
  196. func TestGetOptions(t *testing.T) {
  197. for _, tc := range []struct {
  198. input string
  199. result []string
  200. }{
  201. {
  202. input: ``,
  203. },
  204. {
  205. input: `# ignored`,
  206. },
  207. {
  208. input: `; ignored`,
  209. },
  210. {
  211. input: `nameserver 1.2.3.4`,
  212. },
  213. {
  214. input: `options opt1`,
  215. result: []string{"opt1"},
  216. },
  217. {
  218. input: `options opt1 # notignored`,
  219. result: []string{"opt1", "#", "notignored"},
  220. },
  221. {
  222. input: `options opt1 ; notignored`,
  223. result: []string{"opt1", ";", "notignored"},
  224. },
  225. {
  226. input: ` options opt1 `,
  227. result: []string{"opt1"},
  228. },
  229. {
  230. input: ` options opt1 # notignored`,
  231. result: []string{"opt1", "#", "notignored"},
  232. },
  233. {
  234. input: `options opt1 opt2 opt3`,
  235. result: []string{"opt1", "opt2", "opt3"},
  236. },
  237. {
  238. input: `options opt1 opt2 opt3 # notignored`,
  239. result: []string{"opt1", "opt2", "opt3", "#", "notignored"},
  240. },
  241. {
  242. input: ` options opt1 opt2 opt3 `,
  243. result: []string{"opt1", "opt2", "opt3"},
  244. },
  245. {
  246. input: ` options opt1 opt2 opt3 # notignored`,
  247. result: []string{"opt1", "opt2", "opt3", "#", "notignored"},
  248. },
  249. {
  250. input: `nameserver 1.2.3.4
  251. options opt1 opt2 opt3`,
  252. result: []string{"opt1", "opt2", "opt3"},
  253. },
  254. {
  255. input: `nameserver 1.2.3.4
  256. options opt1 opt2
  257. options opt3 opt4`,
  258. result: []string{"opt1", "opt2", "opt3", "opt4"},
  259. },
  260. } {
  261. test := GetOptions([]byte(tc.input))
  262. if !strSlicesEqual(test, tc.result) {
  263. t.Errorf("Wrong options string {%s} should be %v. Input: %s", test, tc.result, tc.input)
  264. }
  265. }
  266. }
  267. func strSlicesEqual(a, b []string) bool {
  268. if len(a) != len(b) {
  269. return false
  270. }
  271. for i, v := range a {
  272. if v != b[i] {
  273. return false
  274. }
  275. }
  276. return true
  277. }
  278. func TestBuild(t *testing.T) {
  279. tmpDir := t.TempDir()
  280. file, err := os.CreateTemp(tmpDir, "")
  281. if err != nil {
  282. t.Fatal(err)
  283. }
  284. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"})
  285. if err != nil {
  286. t.Fatal(err)
  287. }
  288. const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
  289. if !bytes.Equal(f.Content, []byte(expected)) {
  290. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  291. }
  292. content, err := os.ReadFile(file.Name())
  293. if err != nil {
  294. t.Fatal(err)
  295. }
  296. if !bytes.Equal(content, []byte(expected)) {
  297. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  298. }
  299. }
  300. func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
  301. tmpDir := t.TempDir()
  302. file, err := os.CreateTemp(tmpDir, "")
  303. if err != nil {
  304. t.Fatal(err)
  305. }
  306. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"})
  307. if err != nil {
  308. t.Fatal(err)
  309. }
  310. const expected = "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
  311. if !bytes.Equal(f.Content, []byte(expected)) {
  312. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  313. }
  314. content, err := os.ReadFile(file.Name())
  315. if err != nil {
  316. t.Fatal(err)
  317. }
  318. if !bytes.Equal(content, []byte(expected)) {
  319. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  320. }
  321. }
  322. func TestBuildWithNoOptions(t *testing.T) {
  323. tmpDir := t.TempDir()
  324. file, err := os.CreateTemp(tmpDir, "")
  325. if err != nil {
  326. t.Fatal(err)
  327. }
  328. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{})
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"
  333. if !bytes.Equal(f.Content, []byte(expected)) {
  334. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  335. }
  336. content, err := os.ReadFile(file.Name())
  337. if err != nil {
  338. t.Fatal(err)
  339. }
  340. if !bytes.Equal(content, []byte(expected)) {
  341. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  342. }
  343. }
  344. func TestFilterResolvDNS(t *testing.T) {
  345. testcases := []struct {
  346. name string
  347. input string
  348. ipv6Enabled bool
  349. expOut string
  350. }{
  351. {
  352. name: "No localhost",
  353. input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\n",
  354. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  355. },
  356. {
  357. name: "Localhost last",
  358. input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n",
  359. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  360. },
  361. {
  362. name: "Localhost middle",
  363. input: "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n",
  364. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  365. },
  366. {
  367. name: "Localhost first",
  368. input: "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n",
  369. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  370. },
  371. {
  372. name: "IPv6 Localhost",
  373. input: "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n",
  374. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  375. },
  376. {
  377. name: "Two IPv6 Localhosts",
  378. input: "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1",
  379. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  380. },
  381. {
  382. name: "IPv6 disabled",
  383. input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1",
  384. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  385. },
  386. {
  387. name: "IPv6 link-local disabled",
  388. input: "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21",
  389. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  390. },
  391. {
  392. name: "IPv6 enabled",
  393. input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1\n",
  394. ipv6Enabled: true,
  395. expOut: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21",
  396. },
  397. {
  398. // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added
  399. name: "localhost only IPv6",
  400. input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
  401. ipv6Enabled: true,
  402. expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844",
  403. },
  404. {
  405. // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added
  406. name: "localhost only no IPv6",
  407. input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
  408. expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4",
  409. },
  410. }
  411. for _, tc := range testcases {
  412. t.Run(tc.name, func(t *testing.T) {
  413. f, err := FilterResolvDNS([]byte(tc.input), tc.ipv6Enabled)
  414. assert.Check(t, is.Nil(err))
  415. out := strings.TrimSpace(string(f.Content))
  416. assert.Check(t, is.Equal(out, tc.expOut))
  417. })
  418. }
  419. }