]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/nss_files/files-alias.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / nss / nss_files / files-alias.c
1 /* Mail alias file parser in nss_files module.
2 Copyright (C) 1996-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20 #include <aliases.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <libc-lock.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include <kernel-features.h>
30
31 #include "nsswitch.h"
32 #include <nss_files.h>
33
34 NSS_DECLARE_MODULE_FUNCTIONS (files)
35
36 /* Locks the static variables in this file. */
37 __libc_lock_define_initialized (static, lock)
38 \f
39 /* Maintenance of the stream open on the database file. For getXXent
40 operations the stream needs to be held open across calls, the other
41 getXXbyYY operations all use their own stream. */
42
43 static FILE *stream;
44
45
46 static enum nss_status
47 internal_setent (FILE **stream)
48 {
49 enum nss_status status = NSS_STATUS_SUCCESS;
50
51 if (*stream == NULL)
52 {
53 *stream = __nss_files_fopen ("/etc/aliases");
54
55 if (*stream == NULL)
56 status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
57 }
58 else
59 rewind (*stream);
60
61 return status;
62 }
63
64
65 /* Thread-safe, exported version of that. */
66 enum nss_status
67 _nss_files_setaliasent (void)
68 {
69 enum nss_status status;
70
71 __libc_lock_lock (lock);
72
73 status = internal_setent (&stream);
74
75 __libc_lock_unlock (lock);
76
77 return status;
78 }
79
80
81 /* Close the database file. */
82 static void
83 internal_endent (FILE **stream)
84 {
85 if (*stream != NULL)
86 {
87 fclose (*stream);
88 *stream = NULL;
89 }
90 }
91
92
93 /* Thread-safe, exported version of that. */
94 enum nss_status
95 _nss_files_endaliasent (void)
96 {
97 __libc_lock_lock (lock);
98
99 internal_endent (&stream);
100
101 __libc_lock_unlock (lock);
102
103 return NSS_STATUS_SUCCESS;
104 }
105 \f
106 /* Parsing the database file into `struct aliasent' data structures. */
107 static enum nss_status
108 get_next_alias (FILE *stream, const char *match, struct aliasent *result,
109 char *buffer, size_t buflen, int *errnop)
110 {
111 enum nss_status status = NSS_STATUS_NOTFOUND;
112 int ignore = 0;
113
114 result->alias_members_len = 0;
115
116 while (1)
117 {
118 /* Now we are ready to process the input. We have to read a
119 line and all its continuations and construct the array of
120 string pointers. This pointers and the names itself have to
121 be placed in BUFFER. */
122 char *first_unused = buffer;
123 size_t room_left = buflen - (buflen % __alignof__ (char *));
124 char *line;
125
126 /* Check whether the buffer is large enough for even trying to
127 read something. */
128 if (room_left < 2)
129 goto no_more_room;
130
131 /* Read the first line. It must contain the alias name and
132 possibly some alias names. */
133 first_unused[room_left - 1] = '\xff';
134 line = fgets_unlocked (first_unused, room_left, stream);
135 if (line == NULL)
136 /* Nothing to read. */
137 break;
138 else if (first_unused[room_left - 1] != '\xff')
139 {
140 /* The line is too long for our buffer. */
141 no_more_room:
142 *errnop = ERANGE;
143 status = NSS_STATUS_TRYAGAIN;
144 break;
145 }
146 else
147 {
148 char *cp;
149
150 /* If we are in IGNORE mode and the first character in the
151 line is a white space we ignore the line and start
152 reading the next. */
153 if (ignore && isspace (*first_unused))
154 continue;
155
156 /* Terminate the line for any case. */
157 cp = strpbrk (first_unused, "#\n");
158 if (cp != NULL)
159 *cp = '\0';
160
161 /* Skip leading blanks. */
162 while (isspace (*line))
163 ++line;
164
165 result->alias_name = first_unused;
166 while (*line != '\0' && *line != ':')
167 *first_unused++ = *line++;
168 if (*line == '\0' || result->alias_name == first_unused)
169 /* No valid name. Ignore the line. */
170 continue;
171
172 *first_unused++ = '\0';
173 if (room_left < (size_t) (first_unused - result->alias_name))
174 goto no_more_room;
175 room_left -= first_unused - result->alias_name;
176 ++line;
177
178 /* When we search for a specific alias we can avoid all the
179 difficult parts and compare now with the name we are
180 looking for. If it does not match we simply ignore all
181 lines until the next line containing the start of a new
182 alias is found. */
183 ignore = (match != NULL
184 && __strcasecmp (result->alias_name, match) != 0);
185
186 while (! ignore)
187 {
188 while (isspace (*line))
189 ++line;
190
191 cp = first_unused;
192 while (*line != '\0' && *line != ',')
193 *first_unused++ = *line++;
194
195 if (first_unused != cp)
196 {
197 /* OK, we can have a regular entry or an include
198 request. */
199 if (*line != '\0')
200 ++line;
201 *first_unused++ = '\0';
202
203 if (strncmp (cp, ":include:", 9) != 0)
204 {
205 if (room_left < (first_unused - cp) + sizeof (char *))
206 goto no_more_room;
207 room_left -= (first_unused - cp) + sizeof (char *);
208
209 ++result->alias_members_len;
210 }
211 else
212 {
213 /* Oh well, we have to read the addressed file. */
214 FILE *listfile;
215 char *old_line = NULL;
216
217 first_unused = cp;
218
219 listfile = __nss_files_fopen (&cp[9]);
220 /* If the file does not exist we simply ignore
221 the statement. */
222 if (listfile != NULL
223 && (old_line = strdup (line)) != NULL)
224 {
225 while (! feof_unlocked (listfile))
226 {
227 if (room_left < 2)
228 {
229 free (old_line);
230 fclose (listfile);
231 goto no_more_room;
232 }
233
234 first_unused[room_left - 1] = '\xff';
235 line = fgets_unlocked (first_unused, room_left,
236 listfile);
237 if (line == NULL)
238 break;
239 if (first_unused[room_left - 1] != '\xff')
240 {
241 free (old_line);
242 fclose (listfile);
243 goto no_more_room;
244 }
245
246 /* Parse the line. */
247 cp = strpbrk (line, "#\n");
248 if (cp != NULL)
249 *cp = '\0';
250
251 do
252 {
253 while (isspace (*line))
254 ++line;
255
256 cp = first_unused;
257 while (*line != '\0' && *line != ',')
258 *first_unused++ = *line++;
259
260 if (*line != '\0')
261 ++line;
262
263 if (first_unused != cp)
264 {
265 *first_unused++ = '\0';
266 if (room_left < ((first_unused - cp)
267 + __alignof__ (char *)))
268 {
269 free (old_line);
270 fclose (listfile);
271 goto no_more_room;
272 }
273 room_left -= ((first_unused - cp)
274 + __alignof__ (char *));
275 ++result->alias_members_len;
276 }
277 }
278 while (*line != '\0');
279 }
280 fclose (listfile);
281
282 first_unused[room_left - 1] = '\0';
283 strncpy (first_unused, old_line, room_left);
284
285 free (old_line);
286 line = first_unused;
287
288 if (first_unused[room_left - 1] != '\0')
289 goto no_more_room;
290 }
291 }
292 }
293
294 if (*line == '\0')
295 {
296 /* Get the next line. But we must be careful. We
297 must not read the whole line at once since it
298 might belong to the current alias. Simply read
299 the first character. If it is a white space we
300 have a continuation line. Otherwise it is the
301 beginning of a new alias and we can push back the
302 just read character. */
303 int ch;
304
305 ch = fgetc_unlocked (stream);
306 if (ch == EOF || ch == '\n' || !isspace (ch))
307 {
308 size_t cnt;
309
310 /* Now prepare the return. Provide string
311 pointers for the currently selected aliases. */
312 if (ch != EOF)
313 ungetc (ch, stream);
314
315 /* Adjust the pointer so it is aligned for
316 storing pointers. */
317 first_unused += __alignof__ (char *) - 1;
318 first_unused -= ((first_unused - (char *) 0)
319 % __alignof__ (char *));
320 result->alias_members = (char **) first_unused;
321
322 /* Compute addresses of alias entry strings. */
323 cp = result->alias_name;
324 for (cnt = 0; cnt < result->alias_members_len; ++cnt)
325 {
326 cp = strchr (cp, '\0') + 1;
327 result->alias_members[cnt] = cp;
328 }
329
330 status = (result->alias_members_len == 0
331 ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
332 break;
333 }
334
335 /* The just read character is a white space and so
336 can be ignored. */
337 first_unused[room_left - 1] = '\xff';
338 line = fgets_unlocked (first_unused, room_left, stream);
339 if (line == NULL)
340 {
341 /* Continuation line without any data and
342 without a newline at the end. Treat it as an
343 empty line and retry, reaching EOF once
344 more. */
345 line = first_unused;
346 *line = '\0';
347 continue;
348 }
349 if (first_unused[room_left - 1] != '\xff')
350 goto no_more_room;
351 cp = strpbrk (line, "#\n");
352 if (cp != NULL)
353 *cp = '\0';
354 }
355 }
356 }
357
358 if (status != NSS_STATUS_NOTFOUND)
359 /* We read something. In any case break here. */
360 break;
361 }
362
363 return status;
364 }
365
366
367 enum nss_status
368 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
369 int *errnop)
370 {
371 /* Return next entry in host file. */
372 enum nss_status status = NSS_STATUS_SUCCESS;
373
374 __libc_lock_lock (lock);
375
376 /* Be prepared that the set*ent function was not called before. */
377 if (stream == NULL)
378 status = internal_setent (&stream);
379
380 if (status == NSS_STATUS_SUCCESS)
381 {
382 result->alias_local = 1;
383
384 /* Read lines until we get a definite result. */
385 do
386 status = get_next_alias (stream, NULL, result, buffer, buflen, errnop);
387 while (status == NSS_STATUS_RETURN);
388 }
389
390 __libc_lock_unlock (lock);
391
392 return status;
393 }
394
395
396 enum nss_status
397 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
398 char *buffer, size_t buflen, int *errnop)
399 {
400 /* Return next entry in host file. */
401 enum nss_status status = NSS_STATUS_SUCCESS;
402 FILE *stream = NULL;
403
404 if (name == NULL)
405 {
406 __set_errno (EINVAL);
407 return NSS_STATUS_UNAVAIL;
408 }
409
410 /* Open the stream. */
411 status = internal_setent (&stream);
412
413 if (status == NSS_STATUS_SUCCESS)
414 {
415 result->alias_local = 1;
416
417 /* Read lines until we get a definite result. */
418 do
419 status = get_next_alias (stream, name, result, buffer, buflen, errnop);
420 while (status == NSS_STATUS_RETURN);
421 }
422
423 internal_endent (&stream);
424
425 return status;
426 }