]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |
2 | ||
3 | #include <arpa/inet.h> | |
4 | #include <netinet/in.h> | |
5 | #include <sys/socket.h> | |
6 | ||
7 | #include "fd-util.h" | |
8 | #include "fileio.h" | |
9 | #include "fs-util.h" | |
10 | #include "log.h" | |
11 | #include "resolved-etc-hosts.h" | |
12 | #include "strv.h" | |
13 | #include "tests.h" | |
14 | #include "tmpfile-util.h" | |
15 | ||
16 | static void test_parse_etc_hosts_system(void) { | |
17 | _cleanup_fclose_ FILE *f = NULL; | |
18 | ||
19 | log_info("/* %s */", __func__); | |
20 | ||
21 | f = fopen("/etc/hosts", "re"); | |
22 | if (!f) { | |
23 | assert_se(errno == ENOENT); | |
24 | return; | |
25 | } | |
26 | ||
27 | _cleanup_(etc_hosts_free) EtcHosts hosts = {}; | |
28 | assert_se(etc_hosts_parse(&hosts, f) == 0); | |
29 | } | |
30 | ||
31 | #define address_equal_4(_addr, _address) \ | |
32 | ((_addr)->family == AF_INET && \ | |
33 | !memcmp(&(_addr)->address.in, &(struct in_addr) { .s_addr = (_address) }, 4)) | |
34 | ||
35 | #define address_equal_6(_addr, ...) \ | |
36 | ((_addr)->family == AF_INET6 && \ | |
37 | !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) ) | |
38 | ||
39 | static void test_parse_etc_hosts(void) { | |
40 | _cleanup_(unlink_tempfilep) char | |
41 | t[] = "/tmp/test-resolved-etc-hosts.XXXXXX"; | |
42 | ||
43 | log_info("/* %s */", __func__); | |
44 | ||
45 | int fd; | |
46 | _cleanup_fclose_ FILE *f; | |
47 | const char *s; | |
48 | ||
49 | fd = mkostemp_safe(t); | |
50 | assert_se(fd >= 0); | |
51 | ||
52 | f = fdopen(fd, "r+"); | |
53 | assert_se(f); | |
54 | fputs("1.2.3.4 some.where\n" | |
55 | "1.2.3.5 some.where\n" | |
56 | "1.2.3.6 dash dash-dash.where-dash\n" | |
57 | "1.2.3.7 bad-dash- -bad-dash -bad-dash.bad-\n" | |
58 | "1.2.3.8\n" | |
59 | "1.2.3.9 before.comment # within.comment\n" | |
60 | "1.2.3.10 before.comment#within.comment2\n" | |
61 | "1.2.3.11 before.comment# within.comment3\n" | |
62 | "1.2.3.12 before.comment#\n" | |
63 | "1.2.3 short.address\n" | |
64 | "1.2.3.4.5 long.address\n" | |
65 | "1::2::3 multi.colon\n" | |
66 | ||
67 | "::0 some.where some.other\n" | |
68 | "0.0.0.0 black.listed\n" | |
69 | "::5\t\t\t \tsome.where\tsome.other foobar.foo.foo\t\t\t\n" | |
70 | " \n", f); | |
71 | assert_se(fflush_and_check(f) >= 0); | |
72 | rewind(f); | |
73 | ||
74 | _cleanup_(etc_hosts_free) EtcHosts hosts = {}; | |
75 | assert_se(etc_hosts_parse(&hosts, f) == 0); | |
76 | ||
77 | EtcHostsItemByName *bn; | |
78 | assert_se(bn = hashmap_get(hosts.by_name, "some.where")); | |
79 | assert_se(bn->n_addresses == 3); | |
80 | assert_se(bn->n_allocated >= 3); | |
81 | assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.4"))); | |
82 | assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.5"))); | |
83 | assert_se(address_equal_6(bn->addresses[2], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5})); | |
84 | ||
85 | assert_se(bn = hashmap_get(hosts.by_name, "dash")); | |
86 | assert_se(bn->n_addresses == 1); | |
87 | assert_se(bn->n_allocated >= 1); | |
88 | assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6"))); | |
89 | ||
90 | assert_se(bn = hashmap_get(hosts.by_name, "dash-dash.where-dash")); | |
91 | assert_se(bn->n_addresses == 1); | |
92 | assert_se(bn->n_allocated >= 1); | |
93 | assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.6"))); | |
94 | ||
95 | /* See https://tools.ietf.org/html/rfc1035#section-2.3.1 */ | |
96 | FOREACH_STRING(s, "bad-dash-", "-bad-dash", "-bad-dash.bad-") | |
97 | assert_se(!hashmap_get(hosts.by_name, s)); | |
98 | ||
99 | assert_se(bn = hashmap_get(hosts.by_name, "before.comment")); | |
100 | assert_se(bn->n_addresses == 4); | |
101 | assert_se(bn->n_allocated >= 4); | |
102 | assert_se(address_equal_4(bn->addresses[0], inet_addr("1.2.3.9"))); | |
103 | assert_se(address_equal_4(bn->addresses[1], inet_addr("1.2.3.10"))); | |
104 | assert_se(address_equal_4(bn->addresses[2], inet_addr("1.2.3.11"))); | |
105 | assert_se(address_equal_4(bn->addresses[3], inet_addr("1.2.3.12"))); | |
106 | ||
107 | assert(!hashmap_get(hosts.by_name, "within.comment")); | |
108 | assert(!hashmap_get(hosts.by_name, "within.comment2")); | |
109 | assert(!hashmap_get(hosts.by_name, "within.comment3")); | |
110 | assert(!hashmap_get(hosts.by_name, "#")); | |
111 | ||
112 | assert(!hashmap_get(hosts.by_name, "short.address")); | |
113 | assert(!hashmap_get(hosts.by_name, "long.address")); | |
114 | assert(!hashmap_get(hosts.by_name, "multi.colon")); | |
115 | assert_se(!set_contains(hosts.no_address, "short.address")); | |
116 | assert_se(!set_contains(hosts.no_address, "long.address")); | |
117 | assert_se(!set_contains(hosts.no_address, "multi.colon")); | |
118 | ||
119 | assert_se(bn = hashmap_get(hosts.by_name, "some.other")); | |
120 | assert_se(bn->n_addresses == 1); | |
121 | assert_se(bn->n_allocated >= 1); | |
122 | assert_se(address_equal_6(bn->addresses[0], {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5})); | |
123 | ||
124 | assert_se( set_contains(hosts.no_address, "some.where")); | |
125 | assert_se( set_contains(hosts.no_address, "some.other")); | |
126 | assert_se( set_contains(hosts.no_address, "black.listed")); | |
127 | assert_se(!set_contains(hosts.no_address, "foobar.foo.foo")); | |
128 | } | |
129 | ||
130 | static void test_parse_file(const char *fname) { | |
131 | _cleanup_(etc_hosts_free) EtcHosts hosts = {}; | |
132 | _cleanup_fclose_ FILE *f; | |
133 | ||
134 | log_info("/* %s(\"%s\") */", __func__, fname); | |
135 | ||
136 | assert_se(f = fopen(fname, "re")); | |
137 | assert_se(etc_hosts_parse(&hosts, f) == 0); | |
138 | } | |
139 | ||
140 | int main(int argc, char **argv) { | |
141 | test_setup_logging(LOG_DEBUG); | |
142 | ||
143 | if (argc == 1) { | |
144 | test_parse_etc_hosts_system(); | |
145 | test_parse_etc_hosts(); | |
146 | } else | |
147 | test_parse_file(argv[1]); | |
148 | ||
149 | return 0; | |
150 | } |