resolvconf_unix_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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 # ignored`,
  146. result: []string{"example.com"},
  147. },
  148. {
  149. input: ` search example.com `,
  150. result: []string{"example.com"},
  151. },
  152. {
  153. input: ` search example.com # ignored`,
  154. result: []string{"example.com"},
  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 # ignored`,
  166. result: []string{"foo.example.com", "example.com"},
  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: `nameserver 1.2.3.4`,
  209. },
  210. {
  211. input: `options opt1`,
  212. result: []string{"opt1"},
  213. },
  214. {
  215. input: `options opt1 # ignored`,
  216. result: []string{"opt1"},
  217. },
  218. {
  219. input: ` options opt1 `,
  220. result: []string{"opt1"},
  221. },
  222. {
  223. input: ` options opt1 # ignored`,
  224. result: []string{"opt1"},
  225. },
  226. {
  227. input: `options opt1 opt2 opt3`,
  228. result: []string{"opt1", "opt2", "opt3"},
  229. },
  230. {
  231. input: `options opt1 opt2 opt3 # ignored`,
  232. result: []string{"opt1", "opt2", "opt3"},
  233. },
  234. {
  235. input: ` options opt1 opt2 opt3 `,
  236. result: []string{"opt1", "opt2", "opt3"},
  237. },
  238. {
  239. input: ` options opt1 opt2 opt3 # ignored`,
  240. result: []string{"opt1", "opt2", "opt3"},
  241. },
  242. {
  243. input: `nameserver 1.2.3.4
  244. options opt1 opt2 opt3`,
  245. result: []string{"opt1", "opt2", "opt3"},
  246. },
  247. {
  248. input: `nameserver 1.2.3.4
  249. options opt1 opt2
  250. options opt3 opt4`,
  251. result: []string{"opt3", "opt4"},
  252. },
  253. } {
  254. test := GetOptions([]byte(tc.input))
  255. if !strSlicesEqual(test, tc.result) {
  256. t.Errorf("Wrong options string {%s} should be %v. Input: %s", test, tc.result, tc.input)
  257. }
  258. }
  259. }
  260. func strSlicesEqual(a, b []string) bool {
  261. if len(a) != len(b) {
  262. return false
  263. }
  264. for i, v := range a {
  265. if v != b[i] {
  266. return false
  267. }
  268. }
  269. return true
  270. }
  271. func TestBuild(t *testing.T) {
  272. tmpDir := t.TempDir()
  273. file, err := os.CreateTemp(tmpDir, "")
  274. if err != nil {
  275. t.Fatal(err)
  276. }
  277. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"})
  278. if err != nil {
  279. t.Fatal(err)
  280. }
  281. const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
  282. if !bytes.Equal(f.Content, []byte(expected)) {
  283. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  284. }
  285. content, err := os.ReadFile(file.Name())
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. if !bytes.Equal(content, []byte(expected)) {
  290. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  291. }
  292. }
  293. func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
  294. tmpDir := t.TempDir()
  295. file, err := os.CreateTemp(tmpDir, "")
  296. if err != nil {
  297. t.Fatal(err)
  298. }
  299. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"})
  300. if err != nil {
  301. t.Fatal(err)
  302. }
  303. const expected = "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"
  304. if !bytes.Equal(f.Content, []byte(expected)) {
  305. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  306. }
  307. content, err := os.ReadFile(file.Name())
  308. if err != nil {
  309. t.Fatal(err)
  310. }
  311. if !bytes.Equal(content, []byte(expected)) {
  312. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  313. }
  314. }
  315. func TestBuildWithNoOptions(t *testing.T) {
  316. tmpDir := t.TempDir()
  317. file, err := os.CreateTemp(tmpDir, "")
  318. if err != nil {
  319. t.Fatal(err)
  320. }
  321. f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{})
  322. if err != nil {
  323. t.Fatal(err)
  324. }
  325. const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"
  326. if !bytes.Equal(f.Content, []byte(expected)) {
  327. t.Errorf("Expected to find '%s' got '%s'", expected, f.Content)
  328. }
  329. content, err := os.ReadFile(file.Name())
  330. if err != nil {
  331. t.Fatal(err)
  332. }
  333. if !bytes.Equal(content, []byte(expected)) {
  334. t.Errorf("Expected to find '%s' got '%s'", expected, content)
  335. }
  336. }
  337. func TestFilterResolvDNS(t *testing.T) {
  338. testcases := []struct {
  339. name string
  340. input string
  341. ipv6Enabled bool
  342. expOut string
  343. }{
  344. {
  345. name: "No localhost",
  346. input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\n",
  347. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  348. },
  349. {
  350. name: "Localhost last",
  351. input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n",
  352. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  353. },
  354. {
  355. name: "Localhost middle",
  356. input: "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n",
  357. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  358. },
  359. {
  360. name: "Localhost first",
  361. input: "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n",
  362. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  363. },
  364. {
  365. name: "IPv6 Localhost",
  366. input: "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n",
  367. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  368. },
  369. {
  370. name: "Two IPv6 Localhosts",
  371. input: "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1",
  372. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  373. },
  374. {
  375. name: "IPv6 disabled",
  376. input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1",
  377. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  378. },
  379. {
  380. name: "IPv6 link-local disabled",
  381. input: "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21",
  382. expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21",
  383. },
  384. {
  385. name: "IPv6 enabled",
  386. input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1\n",
  387. ipv6Enabled: true,
  388. expOut: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21",
  389. },
  390. {
  391. // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added
  392. name: "localhost only IPv6",
  393. input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
  394. ipv6Enabled: true,
  395. expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844",
  396. },
  397. {
  398. // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added
  399. name: "localhost only no IPv6",
  400. input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1",
  401. expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4",
  402. },
  403. }
  404. for _, tc := range testcases {
  405. t.Run(tc.name, func(t *testing.T) {
  406. f, err := FilterResolvDNS([]byte(tc.input), tc.ipv6Enabled)
  407. assert.Check(t, is.Nil(err))
  408. out := strings.TrimSpace(string(f.Content))
  409. assert.Check(t, is.Equal(out, tc.expOut))
  410. })
  411. }
  412. }