]>
Commit | Line | Data |
---|---|---|
d67281a7 UD |
1 | /* Copyright (c) 1998 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. | |
3 | Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | 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 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
17 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | Boston, MA 02111-1307, USA. */ | |
19 | ||
20 | #include <errno.h> | |
21 | #include <error.h> | |
22 | #include <fcntl.h> | |
23 | #include <libintl.h> | |
24 | #include <locale.h> | |
25 | #include <pthread.h> | |
26 | #include <pwd.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <unistd.h> | |
30 | #include <sys/socket.h> | |
31 | #include <sys/stat.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/uio.h> | |
34 | #include <sys/un.h> | |
35 | ||
36 | #include "nscd.h" | |
37 | #include "dbg_log.h" | |
38 | ||
39 | /* Socket 0 in the array is named and exported into the file namespace | |
40 | as a connection point for clients. */ | |
41 | static int sock[MAX_NUM_CONNECTIONS]; | |
42 | static int socks_active; | |
43 | static fd_set read_set; | |
44 | static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; | |
45 | ||
46 | ||
47 | /* Cleanup. */ | |
48 | void | |
49 | close_sockets (void) | |
50 | { | |
51 | int i; | |
52 | ||
53 | if (debug_flag) | |
54 | dbg_log (_("close_sockets called")); | |
55 | ||
56 | pthread_mutex_lock (&sock_lock); | |
57 | ||
58 | /* Close sockets. */ | |
59 | for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) | |
60 | if (sock[i] != 0) | |
61 | { | |
62 | if (close (sock[i])) | |
63 | dbg_log (_("socket [%d|%d] close: %s"), strerror (errno)); | |
64 | ||
65 | sock[i] = 0; | |
66 | --socks_active; | |
67 | } | |
68 | ||
69 | pthread_mutex_unlock (&sock_lock); | |
70 | } | |
71 | ||
72 | void | |
73 | close_socket (int conn) | |
74 | { | |
75 | if (debug_flag > 2) | |
76 | dbg_log (_("close socket (%d|%d)"), conn, sock[conn]); | |
77 | ||
78 | pthread_mutex_lock (&sock_lock); | |
79 | ||
80 | close (sock[conn]); | |
81 | sock[conn] = 0; | |
82 | --socks_active; | |
83 | ||
84 | pthread_mutex_unlock (&sock_lock); | |
85 | } | |
86 | ||
87 | /* Local rountine, assigns a socket to a new connection request. */ | |
88 | static void | |
89 | handle_new_connection (void) | |
90 | { | |
91 | int i; | |
92 | ||
93 | if (debug_flag > 2) | |
94 | dbg_log (_("handle_new_connection")); | |
95 | ||
96 | pthread_mutex_lock (&sock_lock); | |
97 | ||
98 | if (socks_active < MAX_NUM_CONNECTIONS) | |
99 | /* Find a free socket entry to use. */ | |
100 | for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) | |
101 | { | |
102 | if (sock[i] == 0) | |
103 | { | |
104 | if ((sock[i] = accept (sock[0], NULL, NULL)) < 0) | |
105 | { | |
106 | dbg_log (_("socket accept: %s"), strerror (errno)); | |
107 | return; | |
108 | } | |
109 | ++socks_active; | |
110 | FD_SET (sock[i], &read_set); | |
111 | if (debug_flag > 2) | |
112 | dbg_log (_("handle_new_connection used socket %d|%d"), i, | |
113 | sock[i]); | |
114 | break; | |
115 | } | |
116 | } | |
117 | else | |
118 | { | |
119 | int black_widow_sock; | |
120 | dbg_log (_("Supported number of simultainious connections exceeded")); | |
121 | dbg_log (_("Ignoring client connect request")); | |
122 | /* There has to be a better way to ignore a connection request,.. | |
123 | when I get my hands on a sockets wiz I'll modify this. */ | |
124 | black_widow_sock = accept (sock[0], NULL, NULL); | |
125 | close (black_widow_sock); | |
126 | } | |
127 | pthread_mutex_unlock (&sock_lock); | |
128 | } | |
129 | ||
130 | /* Local routine, reads a request off a socket indicated by a selectset. */ | |
131 | static int | |
132 | handle_new_request (fd_set read_selects, int **connp, request_header **reqp, | |
133 | char **key) | |
134 | { | |
135 | ssize_t nbytes; | |
136 | int i; | |
137 | ||
138 | if (debug_flag) | |
139 | dbg_log ("handle_new_request"); | |
140 | ||
141 | /* Find the descriptor. */ | |
142 | for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) | |
143 | if (FD_ISSET(sock[i], &read_selects)) | |
144 | break; | |
145 | ||
146 | if (debug_flag > 2) | |
147 | dbg_log (_("handle_new_request uses socket %d"), i); | |
148 | ||
149 | /* Read from it. */ | |
150 | nbytes = read (sock[i], *reqp, sizeof (request_header)); | |
151 | if (nbytes != sizeof (request_header)) | |
152 | { | |
153 | /* Handle non-data read cases. */ | |
154 | if (nbytes == 0) | |
155 | { | |
156 | /* Close socket down. */ | |
157 | if (debug_flag > 2) | |
158 | dbg_log (_("Real close socket %d|%d"), i, sock[i]); | |
159 | ||
160 | pthread_mutex_lock (&sock_lock); | |
161 | FD_CLR (sock[i], &read_set); | |
162 | close (sock[i]); | |
163 | sock[i] = 0; | |
164 | --socks_active; | |
165 | pthread_mutex_unlock (&sock_lock); | |
166 | } | |
167 | else | |
168 | if (nbytes < 0) | |
169 | { | |
170 | dbg_log (_("Read(%d|%d) error on get request: %s"), | |
171 | i, sock[i], strerror (errno)); | |
172 | exit (1); | |
173 | } | |
174 | else | |
175 | dbg_log (_("Read, data < request buf size, ignoring data")); | |
176 | ||
177 | return -1; | |
178 | } | |
179 | else | |
180 | { | |
181 | *key = malloc ((*reqp)->key_len + 1); | |
182 | /* Read the key from it */ | |
183 | nbytes = read (sock[i], *key, (*reqp)->key_len); | |
184 | if (nbytes != (*reqp)->key_len) | |
185 | { | |
186 | /* Handle non-data read cases. */ | |
187 | if (nbytes == 0) | |
188 | { | |
189 | /* Close socket down. */ | |
190 | if (debug_flag > 2) | |
191 | dbg_log (_("Real close socket %d|%d"), i, sock[i]); | |
192 | ||
193 | pthread_mutex_lock (&sock_lock); | |
194 | FD_CLR (sock[i], &read_set); | |
195 | close (sock[i]); | |
196 | sock[i] = 0; | |
197 | --socks_active; | |
198 | pthread_mutex_unlock (&sock_lock); | |
199 | } | |
200 | else | |
201 | if (nbytes < 0) | |
202 | { | |
203 | perror (_("Read() error on get request")); | |
204 | return 0; | |
205 | } | |
206 | else | |
207 | fputs (_("Read, data < request buf size, ignoring data"), | |
208 | stderr); | |
209 | ||
210 | free (*key); | |
211 | return -1; | |
212 | } | |
213 | else | |
214 | { | |
215 | /* Ok, have a live one, A real data req buf has been obtained. */ | |
216 | (*key)[(*reqp)->key_len] = '\0'; | |
217 | **connp = i; | |
218 | return 0; | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
223 | void | |
224 | get_request (int *conn, request_header *req, char **key) | |
225 | { | |
226 | int i, nr, done = 0; | |
227 | fd_set read_selects; | |
228 | ||
229 | if (debug_flag) | |
230 | dbg_log ("get_request"); | |
231 | ||
232 | /* loop, processing new connection requests until a client buffer | |
233 | is read in on an existing connection. */ | |
234 | while (!done) | |
235 | { | |
236 | /* Set up the socket descriptor mask for the select. | |
237 | copy read_set into the local copy. */ | |
238 | ||
239 | FD_ZERO (&read_selects); | |
240 | pthread_mutex_lock (&sock_lock); | |
241 | for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) | |
242 | { | |
243 | if (FD_ISSET (sock[i], &read_set)) | |
244 | FD_SET (sock[i], &read_selects); | |
245 | } | |
246 | pthread_mutex_unlock (&sock_lock); | |
247 | /* Poll active connections using select(). */ | |
248 | nr = select (FD_SETSIZE, &read_selects, NULL, NULL, NULL); | |
249 | if (nr <= 0) | |
250 | { | |
251 | perror (_("Select new reads")); | |
252 | exit (1); | |
253 | } | |
254 | if (FD_ISSET (sock[0], &read_selects)) | |
255 | /* Handle the case of a new connection request on the named socket. */ | |
256 | handle_new_connection (); | |
257 | else | |
258 | { | |
259 | /* Read data from client specific descriptor. */ | |
260 | if (handle_new_request (read_selects, &conn, &req, key) == 0) | |
261 | { | |
262 | FD_CLR (sock[*conn], &read_set); | |
263 | done = 1; | |
264 | } | |
265 | } | |
266 | } /* While not_done. */ | |
267 | } | |
268 | ||
269 | void | |
270 | init_sockets (void) | |
271 | { | |
272 | struct sockaddr_un sock_addr; | |
273 | ||
274 | /* Initialize the connections db. */ | |
275 | socks_active = 0; | |
276 | FD_ZERO (&read_set); | |
277 | ||
278 | /* Create the socket. */ | |
279 | sock[0] = socket (AF_UNIX, SOCK_STREAM, 0); | |
280 | if (sock[0] < 0) | |
281 | { | |
282 | perror (_("cannot create socket")); | |
283 | exit (1); | |
284 | } | |
285 | /* Bind a name to the socket. */ | |
286 | sock_addr.sun_family = AF_UNIX; | |
287 | strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET); | |
288 | if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) | |
289 | { | |
290 | dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno)); | |
291 | exit (1); | |
292 | } | |
293 | /* Set permissions for the socket. */ | |
294 | chmod (_PATH_NSCDSOCKET, 0666); | |
295 | ||
296 | /* Set the socket up to accept connections. */ | |
297 | if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0) | |
298 | { | |
299 | perror (_("cannot enable socket to accept connections")); | |
300 | exit (1); | |
301 | } | |
302 | ||
303 | /* Add the socket to the server's set of active sockets. */ | |
304 | FD_SET (sock[0], &read_set); | |
305 | ++socks_active; | |
306 | } | |
307 | ||
308 | void | |
309 | pw_send_answer (int conn, struct passwd *pwd) | |
310 | { | |
311 | pw_response_header resp; | |
312 | ||
313 | resp.version = NSCD_VERSION; | |
314 | if (pwd != NULL) | |
315 | { | |
316 | resp.found = 1; | |
317 | resp.pw_name_len = strlen (pwd->pw_name); | |
318 | resp.pw_passwd_len = strlen (pwd->pw_passwd); | |
319 | resp.pw_uid = pwd->pw_uid; | |
320 | resp.pw_gid = pwd->pw_gid; | |
321 | resp.pw_gecos_len = strlen (pwd->pw_gecos); | |
322 | resp.pw_dir_len = strlen (pwd->pw_dir); | |
323 | resp.pw_shell_len = strlen (pwd->pw_shell); | |
324 | } | |
325 | else | |
326 | { | |
327 | resp.found = 0; | |
328 | resp.pw_name_len = 0; | |
329 | resp.pw_passwd_len = 0; | |
330 | resp.pw_uid = -1; | |
331 | resp.pw_gid = -1; | |
332 | resp.pw_gecos_len = 0; | |
333 | resp.pw_dir_len = 0; | |
334 | resp.pw_shell_len = 0; | |
335 | } | |
336 | if (sock[conn] == 0) | |
337 | { | |
338 | dbg_log (_("bad connection id on send response [%d|%d]"), | |
339 | conn, sock[conn]); | |
340 | return; | |
341 | } | |
342 | ||
343 | /* Send response header. */ | |
344 | if (write (sock[conn], &resp, sizeof (pw_response_header)) != | |
345 | sizeof (pw_response_header)) | |
346 | { | |
347 | dbg_log (_("write incomplete on send response: %s"), strerror (errno)); | |
348 | return; | |
349 | } | |
350 | ||
351 | if (resp.found) | |
352 | { | |
353 | struct iovec vec[5]; | |
354 | ||
355 | /* Send pw_name. */ | |
356 | vec[0].iov_base = pwd->pw_name; | |
357 | vec[0].iov_len = resp.pw_name_len; | |
358 | /* Send pw_passwd. */ | |
359 | vec[1].iov_base = pwd->pw_passwd; | |
360 | vec[1].iov_len = resp.pw_passwd_len; | |
361 | /* Send pw_gecos. */ | |
362 | vec[2].iov_base = pwd->pw_gecos; | |
363 | vec[2].iov_len = resp.pw_gecos_len; | |
364 | /* Send pw_dir. */ | |
365 | vec[3].iov_base = pwd->pw_dir; | |
366 | vec[3].iov_len = resp.pw_dir_len; | |
367 | /* Send pw_shell. */ | |
368 | vec[4].iov_base = pwd->pw_shell; | |
369 | vec[4].iov_len = resp.pw_shell_len; | |
370 | ||
371 | if (writev (sock[conn], vec, 5) != (resp.pw_name_len + resp.pw_passwd_len | |
372 | + resp.pw_gecos_len + resp.pw_dir_len | |
373 | + resp.pw_shell_len)) | |
374 | dbg_log (_("write incomplete on send passwd answer: %s"), | |
375 | strerror (errno)); | |
376 | } | |
377 | } | |
378 | ||
379 | void | |
380 | pw_send_disabled (int conn) | |
381 | { | |
382 | pw_response_header resp; | |
383 | ||
384 | resp.version = NSCD_VERSION; | |
385 | resp.found = -1; | |
386 | resp.pw_name_len = 0; | |
387 | resp.pw_passwd_len = 0; | |
388 | resp.pw_uid = -1; | |
389 | resp.pw_gid = -1; | |
390 | resp.pw_gecos_len = 0; | |
391 | resp.pw_dir_len = 0; | |
392 | resp.pw_shell_len = 0; | |
393 | ||
394 | if (sock[conn] == 0) | |
395 | { | |
396 | dbg_log ("bad connection id on send response [%d|%d]", | |
397 | conn, sock[conn]); | |
398 | return; | |
399 | } | |
400 | ||
401 | /* Send response header. */ | |
402 | if (write (sock[conn], &resp, sizeof (pw_response_header)) | |
403 | != sizeof (pw_response_header)) | |
404 | dbg_log (_("write incomplete on send response: %s"), strerror (errno)); | |
405 | } | |
406 | ||
407 | void | |
408 | gr_send_answer (int conn, struct group *grp) | |
409 | { | |
410 | gr_response_header resp; | |
411 | ||
412 | resp.version = NSCD_VERSION; | |
413 | if (grp != NULL) | |
414 | { | |
415 | resp.found = 1; | |
416 | resp.gr_name_len = strlen (grp->gr_name); | |
417 | resp.gr_passwd_len = strlen (grp->gr_passwd); | |
418 | resp.gr_gid = grp->gr_gid; | |
419 | resp.gr_mem_len = 0; | |
420 | while (grp->gr_mem[resp.gr_mem_len]) | |
421 | ++resp.gr_mem_len; | |
422 | } | |
423 | else | |
424 | { | |
425 | resp.found = 0; | |
426 | resp.gr_name_len = 0; | |
427 | resp.gr_passwd_len = 0; | |
428 | resp.gr_gid = -1; | |
429 | resp.gr_mem_len = 0; | |
430 | } | |
431 | if (sock[conn] == 0) | |
432 | { | |
433 | dbg_log (_("bad connection id on send response [%d|%d]"), | |
434 | conn, sock[conn]); | |
435 | return; | |
436 | } | |
437 | ||
438 | /* Send response header. */ | |
439 | if (write (sock[conn], &resp, sizeof (gr_response_header)) | |
440 | != sizeof (gr_response_header)) | |
441 | { | |
442 | dbg_log (_("write incomplete on send response: %s"), strerror (errno)); | |
443 | return; | |
444 | } | |
445 | ||
446 | if (resp.found) | |
447 | { | |
448 | unsigned int l = 0; | |
449 | ||
450 | /* Send gr_name. */ | |
451 | if (write (sock[conn], grp->gr_name, resp.gr_name_len) | |
452 | != resp.gr_name_len) | |
453 | { | |
454 | dbg_log (_("write incomplete on send response: %s"), | |
455 | strerror (errno)); | |
456 | return; | |
457 | } | |
458 | /* Send gr_passwd. */ | |
459 | if (write (sock[conn], grp->gr_passwd, resp.gr_passwd_len) | |
460 | != resp.gr_passwd_len) | |
461 | { | |
462 | dbg_log (_("write incomplete on send response: %s"), | |
463 | strerror (errno)); | |
464 | return; | |
465 | } | |
466 | ||
467 | while (grp->gr_mem[l]) | |
468 | { | |
469 | size_t len = strlen (grp->gr_mem[l]); | |
470 | ||
471 | if (write (sock[conn], &len, sizeof (len)) != sizeof (len)) | |
472 | { | |
473 | dbg_log (_("write incomplete on send response: %s"), | |
474 | strerror (errno)); | |
475 | return; | |
476 | } | |
477 | if (write (sock[conn], grp->gr_mem[l], len) != len) | |
478 | { | |
479 | dbg_log (_("write incomplete on send response: %s"), | |
480 | strerror (errno)); | |
481 | return; | |
482 | } | |
483 | ++l; | |
484 | } | |
485 | } | |
486 | } | |
487 | ||
488 | void | |
489 | gr_send_disabled (int conn) | |
490 | { | |
491 | gr_response_header resp; | |
492 | ||
493 | resp.version = NSCD_VERSION; | |
494 | resp.found = -1; | |
495 | resp.gr_name_len = 0; | |
496 | resp.gr_passwd_len = 0; | |
497 | resp.gr_gid = -1; | |
498 | resp.gr_mem_len = 0; | |
499 | ||
500 | if (sock[conn] == 0) | |
501 | { | |
502 | dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"), | |
503 | conn, sock[conn]); | |
504 | return; | |
505 | } | |
506 | ||
507 | /* Send response header. */ | |
508 | if (write (sock[conn], &resp, sizeof (gr_response_header)) | |
509 | != sizeof (gr_response_header)) | |
510 | dbg_log (_("write incomplete on send gr_disabled response: %s"), | |
511 | strerror (errno)); | |
512 | } | |
513 | ||
514 | void | |
515 | stat_send (int conn, stat_response_header *resp) | |
516 | { | |
517 | if (sock[conn] == 0) | |
518 | { | |
519 | dbg_log (_("bad connection id on send stat response [%d|%d]"), | |
520 | conn, sock[conn]); | |
521 | return; | |
522 | } | |
523 | ||
524 | /* send response header. */ | |
525 | if (write (sock[conn], resp, sizeof (stat_response_header)) | |
526 | != sizeof (stat_response_header)) | |
527 | dbg_log (_("write incomplete on send stat response: %s"), | |
528 | strerror (errno)); | |
529 | } |