1 | /* Enumerate /etc/hosts with a long line (bug 18991). |
2 | Copyright (C) 2018-2022 Free Software Foundation, Inc. |
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 |
17 | <https://www.gnu.org/licenses/>. */ |
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 (path: chroot_env->path_hosts, mode: "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 (path: 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 (stream: &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 (stream: &expected); |
138 | char *context = xasprintf (format: "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 (query_description: context, e, expected: expected.buffer); |
148 | free (ptr: context); |
149 | free (ptr: 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 (query_description: "host2.example.com" , ai, ret, |
175 | expected: "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 (query_description: "host5.example.com" , ai, ret, |
180 | expected: "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 (query_description: "www.example.com" , ai, ret, |
185 | expected: "address: STREAM/TCP 192.0.2.80 80\n" ); |
186 | |
187 | ret = getaddrinfo ("www1.example.com" , "80" , &hints, &ai); |
188 | check_addrinfo (query_description: "www1.example.com" , ai, ret, |
189 | expected: "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 (path: chroot_env->path_chroot); |
197 | |
198 | /* This tests enlarging the read buffer in the multi case. */ |
199 | struct xmemstream expected; |
200 | xopen_memstream (stream: &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 (stream: &expected); |
210 | check_hostent (query_description: "host2.example.com" , |
211 | gethostbyname (name: "host2.example.com" ), |
212 | expected: expected.buffer); |
213 | free (ptr: expected.buffer); |
214 | |
215 | /* Similarly, but with a different order in the /etc/hosts file. */ |
216 | xopen_memstream (stream: &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 (stream: &expected); |
226 | check_hostent (query_description: "host5.example.com" , |
227 | gethostbyname (name: "host5.example.com" ), |
228 | expected: expected.buffer); |
229 | free (ptr: expected.buffer); |
230 | |
231 | check_hostent (query_description: "www.example.com" , |
232 | gethostbyname (name: "www.example.com" ), |
233 | expected: host4_expected); |
234 | check_hostent (query_description: "www1.example.com" , |
235 | gethostbyname (name: "www1.example.com" ), |
236 | expected: 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 (dbname: "hosts" , string: "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 (format: "info: name_count=%d do_stayopen=%d do_endent=%d\n" , |
264 | name_count, do_stayopen, do_endent); |
265 | support_isolate_in_subprocess (callback: subprocess_getent, NULL); |
266 | } |
267 | |
268 | support_isolate_in_subprocess (callback: 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> |
277 | |