]>
Commit | Line | Data |
---|---|---|
916124ed | 1 | /* Enumerate /etc/hosts with a long line (bug 18991). |
2b778ceb | 2 | Copyright (C) 2018-2021 Free Software Foundation, Inc. |
916124ed FW |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
916124ed FW |
18 | |
19 | ||
20 | #include <dlfcn.h> | |
21 | #include <errno.h> | |
22 | #include <gnu/lib-names.h> | |
23 | #include <netdb.h> | |
24 | #include <nss.h> | |
25 | #include <stdlib.h> | |
26 | #include <stdio.h> | |
27 | #include <support/check.h> | |
28 | #include <support/check_nss.h> | |
29 | #include <support/namespace.h> | |
30 | #include <support/support.h> | |
31 | #include <support/test-driver.h> | |
32 | #include <support/xmemstream.h> | |
33 | #include <support/xstdio.h> | |
34 | #include <support/xunistd.h> | |
35 | ||
36 | struct support_chroot *chroot_env; | |
37 | ||
38 | /* Number of alias names in the long line. This is varied to catch | |
39 | different cases where the ERANGE handling can go wrong (line buffer | |
40 | length, alias buffer). */ | |
41 | static int name_count; | |
42 | ||
43 | /* Write /etc/hosts, from outside of the chroot. */ | |
44 | static void | |
45 | write_hosts (void) | |
46 | { | |
47 | FILE *fp = xfopen (chroot_env->path_hosts, "w"); | |
48 | fputs ("127.0.0.1 localhost localhost.localdomain\n", fp); | |
49 | fputs ("192.0.2.2 host2.example.com\n", fp); | |
50 | fputs ("192.0.2.1", fp); | |
51 | for (int i = 0; i < name_count; ++i) | |
52 | fprintf (fp, " host%d.example.com", i); | |
53 | fputs ("\n192.0.2.80 www.example.com\n" | |
54 | "192.0.2.5 host5.example.com\n" | |
55 | "192.0.2.81 www1.example.com\n", fp); | |
56 | xfclose (fp); | |
57 | } | |
58 | ||
59 | const char *host1_expected = | |
60 | "name: localhost\n" | |
61 | "alias: localhost.localdomain\n" | |
62 | "address: 127.0.0.1\n"; | |
63 | const char *host2_expected = | |
64 | "name: host2.example.com\n" | |
65 | "address: 192.0.2.2\n"; | |
66 | const char *host4_expected = | |
67 | "name: www.example.com\n" | |
68 | "address: 192.0.2.80\n"; | |
69 | const char *host5_expected = | |
70 | "name: host5.example.com\n" | |
71 | "address: 192.0.2.5\n"; | |
72 | const char *host6_expected = | |
73 | "name: www1.example.com\n" | |
74 | "address: 192.0.2.81\n"; | |
75 | ||
76 | static void | |
77 | prepare (int argc, char **argv) | |
78 | { | |
79 | chroot_env = support_chroot_create | |
80 | ((struct support_chroot_configuration) | |
81 | { | |
82 | .resolv_conf = "", | |
83 | .hosts = "", /* Filled in by write_hosts. */ | |
84 | .host_conf = "multi on\n", | |
85 | }); | |
86 | } | |
87 | ||
88 | /* If -1, no sethostent call. Otherwise, pass do_stayopen as the | |
89 | sethostent argument. */ | |
90 | static int do_stayopen; | |
91 | ||
92 | /* If non-zero, perform an endostent call. */ | |
93 | static int do_endent; | |
94 | ||
95 | static void | |
96 | subprocess_getent (void *closure) | |
97 | { | |
98 | xchroot (chroot_env->path_chroot); | |
99 | ||
100 | errno = 0; | |
101 | if (do_stayopen >= 0) | |
102 | sethostent (do_stayopen); | |
103 | TEST_VERIFY (errno == 0); | |
104 | ||
105 | int i = 0; | |
106 | while (true) | |
107 | { | |
108 | struct xmemstream expected; | |
109 | xopen_memstream (&expected); | |
110 | switch (++i) | |
111 | { | |
112 | case 1: | |
113 | fputs (host1_expected, expected.out); | |
114 | break; | |
115 | case 2: | |
116 | fputs (host2_expected, expected.out); | |
117 | break; | |
118 | case 3: | |
119 | fputs ("name: host0.example.com\n", expected.out); | |
120 | for (int j = 1; j < name_count; ++j) | |
121 | fprintf (expected.out, "alias: host%d.example.com\n", j); | |
122 | fputs ("address: 192.0.2.1\n", expected.out); | |
123 | break; | |
124 | case 4: | |
125 | fputs (host4_expected, expected.out); | |
126 | break; | |
127 | case 5: | |
128 | fputs (host5_expected, expected.out); | |
129 | break; | |
130 | case 6: | |
131 | fputs (host6_expected, expected.out); | |
132 | break; | |
133 | default: | |
134 | fprintf (expected.out, "*** unexpected host %d ***\n", i); | |
135 | break; | |
136 | } | |
137 | xfclose_memstream (&expected); | |
138 | char *context = xasprintf ("do_stayopen=%d host=%d", do_stayopen, i); | |
139 | ||
140 | errno = 0; | |
141 | struct hostent *e = gethostent (); | |
142 | if (e == NULL) | |
143 | { | |
144 | TEST_VERIFY (errno == 0); | |
145 | break; | |
146 | } | |
147 | check_hostent (context, e, expected.buffer); | |
148 | free (context); | |
149 | free (expected.buffer); | |
150 | } | |
151 | ||
152 | errno = 0; | |
153 | if (do_endent) | |
154 | endhostent (); | |
155 | TEST_VERIFY (errno == 0); | |
156 | ||
157 | /* Exercise process termination. */ | |
158 | exit (0); | |
159 | } | |
160 | ||
161 | /* getaddrinfo test. To be run from a subprocess. */ | |
162 | static void | |
163 | test_gai (int family) | |
164 | { | |
165 | struct addrinfo hints = | |
166 | { | |
167 | .ai_family = family, | |
168 | .ai_protocol = IPPROTO_TCP, | |
169 | .ai_socktype = SOCK_STREAM, | |
170 | }; | |
171 | ||
172 | struct addrinfo *ai; | |
173 | int ret = getaddrinfo ("host2.example.com", "80", &hints, &ai); | |
174 | check_addrinfo ("host2.example.com", ai, ret, | |
175 | "address: STREAM/TCP 192.0.2.2 80\n" | |
176 | "address: STREAM/TCP 192.0.2.1 80\n"); | |
177 | ||
178 | ret = getaddrinfo ("host5.example.com", "80", &hints, &ai); | |
179 | check_addrinfo ("host5.example.com", ai, ret, | |
180 | "address: STREAM/TCP 192.0.2.1 80\n" | |
181 | "address: STREAM/TCP 192.0.2.5 80\n"); | |
182 | ||
183 | ret = getaddrinfo ("www.example.com", "80", &hints, &ai); | |
184 | check_addrinfo ("www.example.com", ai, ret, | |
185 | "address: STREAM/TCP 192.0.2.80 80\n"); | |
186 | ||
187 | ret = getaddrinfo ("www1.example.com", "80", &hints, &ai); | |
188 | check_addrinfo ("www1.example.com", ai, ret, | |
189 | "address: STREAM/TCP 192.0.2.81 80\n"); | |
190 | } | |
191 | ||
192 | /* Subprocess routine for gethostbyname/getaddrinfo testing. */ | |
193 | static void | |
194 | subprocess_gethost (void *closure) | |
195 | { | |
196 | xchroot (chroot_env->path_chroot); | |
197 | ||
198 | /* This tests enlarging the read buffer in the multi case. */ | |
199 | struct xmemstream expected; | |
200 | xopen_memstream (&expected); | |
201 | fputs ("name: host2.example.com\n", expected.out); | |
202 | for (int j = 1; j < name_count; ++j) | |
203 | /* NB: host2 is duplicated in the alias list. */ | |
204 | fprintf (expected.out, "alias: host%d.example.com\n", j); | |
205 | fputs ("alias: host0.example.com\n" | |
206 | "address: 192.0.2.2\n" | |
207 | "address: 192.0.2.1\n", | |
208 | expected.out); | |
209 | xfclose_memstream (&expected); | |
210 | check_hostent ("host2.example.com", | |
211 | gethostbyname ("host2.example.com"), | |
212 | expected.buffer); | |
213 | free (expected.buffer); | |
214 | ||
215 | /* Similarly, but with a different order in the /etc/hosts file. */ | |
216 | xopen_memstream (&expected); | |
217 | fputs ("name: host0.example.com\n", expected.out); | |
218 | for (int j = 1; j < name_count; ++j) | |
219 | fprintf (expected.out, "alias: host%d.example.com\n", j); | |
220 | /* NB: host5 is duplicated in the alias list. */ | |
221 | fputs ("alias: host5.example.com\n" | |
222 | "address: 192.0.2.1\n" | |
223 | "address: 192.0.2.5\n", | |
224 | expected.out); | |
225 | xfclose_memstream (&expected); | |
226 | check_hostent ("host5.example.com", | |
227 | gethostbyname ("host5.example.com"), | |
228 | expected.buffer); | |
229 | free (expected.buffer); | |
230 | ||
231 | check_hostent ("www.example.com", | |
232 | gethostbyname ("www.example.com"), | |
233 | host4_expected); | |
234 | check_hostent ("www1.example.com", | |
235 | gethostbyname ("www1.example.com"), | |
236 | host6_expected); | |
237 | ||
238 | test_gai (AF_INET); | |
239 | test_gai (AF_UNSPEC); | |
240 | } | |
241 | ||
242 | static int | |
243 | do_test (void) | |
244 | { | |
245 | support_become_root (); | |
246 | if (!support_can_chroot ()) | |
247 | return EXIT_UNSUPPORTED; | |
248 | ||
249 | __nss_configure_lookup ("hosts", "files"); | |
250 | if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL) | |
251 | FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ()); | |
252 | ||
253 | /* Each name takes about 20 bytes, so this covers a wide range of | |
254 | buffer sizes, from less than 1000 bytes to about 18000 bytes. */ | |
255 | for (name_count = 40; name_count <= 850; ++name_count) | |
256 | { | |
257 | write_hosts (); | |
258 | ||
259 | for (do_stayopen = -1; do_stayopen < 2; ++do_stayopen) | |
260 | for (do_endent = 0; do_endent < 2; ++do_endent) | |
261 | { | |
262 | if (test_verbose > 0) | |
263 | printf ("info: name_count=%d do_stayopen=%d do_endent=%d\n", | |
264 | name_count, do_stayopen, do_endent); | |
265 | support_isolate_in_subprocess (subprocess_getent, NULL); | |
266 | } | |
267 | ||
268 | support_isolate_in_subprocess (subprocess_gethost, NULL); | |
269 | } | |
270 | ||
271 | support_chroot_free (chroot_env); | |
272 | return 0; | |
273 | } | |
274 | ||
275 | #define PREPARE prepare | |
276 | #include <support/test-driver.c> |