diff options
Diffstat (limited to 'nscd/connections.c')
-rw-r--r-- | nscd/connections.c | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/nscd/connections.c b/nscd/connections.c new file mode 100644 index 0000000..abde747 --- /dev/null +++ b/nscd/connections.c @@ -0,0 +1,529 @@ +/* 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)); +} |