resolvconf_unix_test.go 11 KB

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