/* Copyright (c) 1998 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <errno.h> #include <error.h> #include <fcntl.h> #include <libintl.h> #include <locale.h> #include <pthread.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/un.h> #include "nscd.h" #include "dbg_log.h" /* Socket 0 in the array is named and exported into the file namespace as a connection point for clients. */ static int sock[MAX_NUM_CONNECTIONS]; static int socks_active; static fd_set read_set; static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; /* Cleanup. */ void close_sockets (void) { int i; if (debug_flag) dbg_log (_("close_sockets called")); pthread_mutex_lock (&sock_lock); /* Close sockets. */ for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) if (sock[i] != 0) { if (close (sock[i])) dbg_log (_("socket [%d|%d] close: %s"), strerror (errno)); sock[i] = 0; --socks_active; } pthread_mutex_unlock (&sock_lock); } void close_socket (int conn) { if (debug_flag > 2) dbg_log (_("close socket (%d|%d)"), conn, sock[conn]); pthread_mutex_lock (&sock_lock); close (sock[conn]); sock[conn] = 0; --socks_active; pthread_mutex_unlock (&sock_lock); } /* Local rountine, assigns a socket to a new connection request. */ static void handle_new_connection (void) { int i; if (debug_flag > 2) dbg_log (_("handle_new_connection")); pthread_mutex_lock (&sock_lock); if (socks_active < MAX_NUM_CONNECTIONS) /* Find a free socket entry to use. */ for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) { if (sock[i] == 0) { if ((sock[i] = accept (sock[0], NULL, NULL)) < 0) { dbg_log (_("socket accept: %s"), strerror (errno)); return; } ++socks_active; FD_SET (sock[i], &read_set); if (debug_flag > 2) dbg_log (_("handle_new_connection used socket %d|%d"), i, sock[i]); break; } } else { int black_widow_sock; dbg_log (_("Supported number of simultainious connections exceeded")); dbg_log (_("Ignoring client connect request")); /* There has to be a better way to ignore a connection request,.. when I get my hands on a sockets wiz I'll modify this. */ black_widow_sock = accept (sock[0], NULL, NULL); close (black_widow_sock); } pthread_mutex_unlock (&sock_lock); } /* Local routine, reads a request off a socket indicated by a selectset. */ static int handle_new_request (fd_set read_selects, int **connp, request_header **reqp, char **key) { ssize_t nbytes; int i; if (debug_flag) dbg_log ("handle_new_request"); /* Find the descriptor. */ for (i = 1; i < MAX_NUM_CONNECTIONS; ++i) if (FD_ISSET(sock[i], &read_selects)) break; if (debug_flag > 2) dbg_log (_("handle_new_request uses socket %d"), i); /* Read from it. */ nbytes = read (sock[i], *reqp, sizeof (request_header)); if (nbytes != sizeof (request_header)) { /* Handle non-data read cases. */ if (nbytes == 0) { /* Close socket down. */ if (debug_flag > 2) dbg_log (_("Real close socket %d|%d"), i, sock[i]); pthread_mutex_lock (&sock_lock); FD_CLR (sock[i], &read_set); close (sock[i]); sock[i] = 0; --socks_active; pthread_mutex_unlock (&sock_lock); } else if (nbytes < 0) { dbg_log (_("Read(%d|%d) error on get request: %s"), i, sock[i], strerror (errno)); exit (1); } else dbg_log (_("Read, data < request buf size, ignoring data")); return -1; } else { *key = malloc ((*reqp)->key_len + 1); /* Read the key from it */ nbytes = read (sock[i], *key, (*reqp)->key_len); if (nbytes != (*reqp)->key_len) { /* Handle non-data read cases. */ if (nbytes == 0) { /* Close socket down. */ if (debug_flag > 2) dbg_log (_("Real close socket %d|%d"), i, sock[i]); pthread_mutex_lock (&sock_lock); FD_CLR (sock[i], &read_set); close (sock[i]); sock[i] = 0; --socks_active; pthread_mutex_unlock (&sock_lock); } else if (nbytes < 0) { perror (_("Read() error on get request")); return 0; } else fputs (_("Read, data < request buf size, ignoring data"), stderr); free (*key); return -1; } else { /* Ok, have a live one, A real data req buf has been obtained. */ (*key)[(*reqp)->key_len] = '\0'; **connp = i; return 0; } } } void get_request (int *conn, request_header *req, char **key) { int i, nr, done = 0; fd_set read_selects; if (debug_flag) dbg_log ("get_request"); /* loop, processing new connection requests until a client buffer is read in on an existing connection. */ while (!done) { /* Set up the socket descriptor mask for the select. copy read_set into the local copy. */ FD_ZERO (&read_selects); pthread_mutex_lock (&sock_lock); for (i = 0; i < MAX_NUM_CONNECTIONS; ++i) { if (FD_ISSET (sock[i], &read_set)) FD_SET (sock[i], &read_selects); } pthread_mutex_unlock (&sock_lock); /* Poll active connections using select(). */ nr = select (FD_SETSIZE, &read_selects, NULL, NULL, NULL); if (nr <= 0) { perror (_("Select new reads")); exit (1); } if (FD_ISSET (sock[0], &read_selects)) /* Handle the case of a new connection request on the named socket. */ handle_new_connection (); else { /* Read data from client specific descriptor. */ if (handle_new_request (read_selects, &conn, &req, key) == 0) { FD_CLR (sock[*conn], &read_set); done = 1; } } } /* While not_done. */ } void init_sockets (void) { struct sockaddr_un sock_addr; /* Initialize the connections db. */ socks_active = 0; FD_ZERO (&read_set); /* Create the socket. */ sock[0] = socket (AF_UNIX, SOCK_STREAM, 0); if (sock[0] < 0) { perror (_("cannot create socket")); exit (1); } /* Bind a name to the socket. */ sock_addr.sun_family = AF_UNIX; strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET); if (bind (sock[0], (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) { dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno)); exit (1); } /* Set permissions for the socket. */ chmod (_PATH_NSCDSOCKET, 0666); /* Set the socket up to accept connections. */ if (listen (sock[0], MAX_NUM_CONNECTIONS) < 0) { perror (_("cannot enable socket to accept connections")); exit (1); } /* Add the socket to the server's set of active sockets. */ FD_SET (sock[0], &read_set); ++socks_active; } void pw_send_answer (int conn, struct passwd *pwd) { pw_response_header resp; resp.version = NSCD_VERSION; if (pwd != NULL) { resp.found = 1; resp.pw_name_len = strlen (pwd->pw_name); resp.pw_passwd_len = strlen (pwd->pw_passwd); resp.pw_uid = pwd->pw_uid; resp.pw_gid = pwd->pw_gid; resp.pw_gecos_len = strlen (pwd->pw_gecos); resp.pw_dir_len = strlen (pwd->pw_dir); resp.pw_shell_len = strlen (pwd->pw_shell); } else { resp.found = 0; resp.pw_name_len = 0; resp.pw_passwd_len = 0; resp.pw_uid = -1; resp.pw_gid = -1; resp.pw_gecos_len = 0; resp.pw_dir_len = 0; resp.pw_shell_len = 0; } if (sock[conn] == 0) { dbg_log (_("bad connection id on send response [%d|%d]"), conn, sock[conn]); return; } /* Send response header. */ if (write (sock[conn], &resp, sizeof (pw_response_header)) != sizeof (pw_response_header)) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } if (resp.found) { struct iovec vec[5]; /* Send pw_name. */ vec[0].iov_base = pwd->pw_name; vec[0].iov_len = resp.pw_name_len; /* Send pw_passwd. */ vec[1].iov_base = pwd->pw_passwd; vec[1].iov_len = resp.pw_passwd_len; /* Send pw_gecos. */ vec[2].iov_base = pwd->pw_gecos; vec[2].iov_len = resp.pw_gecos_len; /* Send pw_dir. */ vec[3].iov_base = pwd->pw_dir; vec[3].iov_len = resp.pw_dir_len; /* Send pw_shell. */ vec[4].iov_base = pwd->pw_shell; vec[4].iov_len = resp.pw_shell_len; if (writev (sock[conn], vec, 5) != (resp.pw_name_len + resp.pw_passwd_len + resp.pw_gecos_len + resp.pw_dir_len + resp.pw_shell_len)) dbg_log (_("write incomplete on send passwd answer: %s"), strerror (errno)); } } void pw_send_disabled (int conn) { pw_response_header resp; resp.version = NSCD_VERSION; resp.found = -1; resp.pw_name_len = 0; resp.pw_passwd_len = 0; resp.pw_uid = -1; resp.pw_gid = -1; resp.pw_gecos_len = 0; resp.pw_dir_len = 0; resp.pw_shell_len = 0; if (sock[conn] == 0) { dbg_log (_("bad connection id on send response [%d|%d]"), conn, sock[conn]); return; } /* Send response header. */ if (write (sock[conn], &resp, sizeof (pw_response_header)) != sizeof (pw_response_header)) dbg_log (_("write incomplete on send response: %s"), strerror (errno)); } void gr_send_answer (int conn, struct group *grp) { gr_response_header resp; resp.version = NSCD_VERSION; if (grp != NULL) { resp.found = 1; resp.gr_name_len = strlen (grp->gr_name); resp.gr_passwd_len = strlen (grp->gr_passwd); resp.gr_gid = grp->gr_gid; resp.gr_mem_len = 0; while (grp->gr_mem[resp.gr_mem_len]) ++resp.gr_mem_len; } else { resp.found = 0; resp.gr_name_len = 0; resp.gr_passwd_len = 0; resp.gr_gid = -1; resp.gr_mem_len = 0; } if (sock[conn] == 0) { dbg_log (_("bad connection id on send response [%d|%d]"), conn, sock[conn]); return; } /* Send response header. */ if (write (sock[conn], &resp, sizeof (gr_response_header)) != sizeof (gr_response_header)) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } if (resp.found) { unsigned int l = 0; /* Send gr_name. */ if (write (sock[conn], grp->gr_name, resp.gr_name_len) != resp.gr_name_len) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } /* Send gr_passwd. */ if (write (sock[conn], grp->gr_passwd, resp.gr_passwd_len) != resp.gr_passwd_len) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } while (grp->gr_mem[l]) { size_t len = strlen (grp->gr_mem[l]); if (write (sock[conn], &len, sizeof (len)) != sizeof (len)) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } if (write (sock[conn], grp->gr_mem[l], len) != len) { dbg_log (_("write incomplete on send response: %s"), strerror (errno)); return; } ++l; } } } void gr_send_disabled (int conn) { gr_response_header resp; resp.version = NSCD_VERSION; resp.found = -1; resp.gr_name_len = 0; resp.gr_passwd_len = 0; resp.gr_gid = -1; resp.gr_mem_len = 0; if (sock[conn] == 0) { dbg_log (_("bad connection id on send gr_disabled response [%d|%d]"), conn, sock[conn]); return; } /* Send response header. */ if (write (sock[conn], &resp, sizeof (gr_response_header)) != sizeof (gr_response_header)) dbg_log (_("write incomplete on send gr_disabled response: %s"), strerror (errno)); } void stat_send (int conn, stat_response_header *resp) { if (sock[conn] == 0) { dbg_log (_("bad connection id on send stat response [%d|%d]"), conn, sock[conn]); return; } /* send response header. */ if (write (sock[conn], resp, sizeof (stat_response_header)) != sizeof (stat_response_header)) dbg_log (_("write incomplete on send stat response: %s"), strerror (errno)); }