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
36struct 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). */
41static int name_count;
42
43/* Write /etc/hosts, from outside of the chroot. */
44static void
45write_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
59const char *host1_expected =
60 "name: localhost\n"
61 "alias: localhost.localdomain\n"
62 "address: 127.0.0.1\n";
63const char *host2_expected =
64 "name: host2.example.com\n"
65 "address: 192.0.2.2\n";
66const char *host4_expected =
67 "name: www.example.com\n"
68 "address: 192.0.2.80\n";
69const char *host5_expected =
70 "name: host5.example.com\n"
71 "address: 192.0.2.5\n";
72const char *host6_expected =
73 "name: www1.example.com\n"
74 "address: 192.0.2.81\n";
75
76static void
77prepare (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. */
90static int do_stayopen;
91
92/* If non-zero, perform an endostent call. */
93static int do_endent;
94
95static void
96subprocess_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. */
162static void
163test_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. */
193static void
194subprocess_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
242static int
243do_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

source code of glibc/nss/tst-nss-files-hosts-getent.c