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