diff options
Diffstat (limited to 'src/plugin.c')
-rw-r--r-- | src/plugin.c | 248 |
1 files changed, 91 insertions, 157 deletions
diff --git a/src/plugin.c b/src/plugin.c index b4a662d..7741593 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -5,6 +5,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> +#include <assert.h> #include <errno.h> #include <poll.h> #include <pthread.h> @@ -22,6 +23,7 @@ #include <plarenas.h> #include <dirsrv/slapi-plugin.h> +#include "dispatch.h" #include "nis.h" #include "plugin.h" #include "portmap.h" @@ -34,93 +36,24 @@ static Slapi_PluginDesc plugin_description = { .spd_id = "nis-plugin", - .spd_vendor = "hamdingers.org", + .spd_vendor = "badvocacy.net", .spd_version = PACKAGE_VERSION, - .spd_description = "NIS Service Plugin", + .spd_description = "NIS Server Plugin", }; -static void * -listener_thread(void *p) -{ - struct plugin_state *state = p; - struct pollfd fds[4]; - int client, reqsize, i; - char dgram[65536]; - struct sockaddr_storage client_addr; - socklen_t client_addrlen; - for (;;) { - /* Set up for polling. */ - memset(&fds, 0, sizeof(fds)); - for (i = 0; i < state->n_listeners; i++) { - fds[i].fd = state->listenfd[i]; - fds[i].events = POLLIN; - } - switch (poll(fds, state->n_listeners, -1) != -1) { - case -1: - return NULL; - break; - case 0: - continue; - default: - /* Iterate over listening sockets with work for us. */ - for (i = 0; i < state->n_listeners; i++) { - if ((fds[i].revents & POLLIN) == 0) { - continue; - } - switch (state->sock_type[i]) { - case SOCK_DGRAM: - client_addrlen = sizeof(client_addr); - reqsize = recvfrom(fds[i].fd, - dgram, - sizeof(dgram), - 0, - (struct sockaddr *) &client_addr, - &client_addrlen); - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "datagram request\n"); - nis_process_request(state, fds[i].fd, - (struct sockaddr *) - &client_addr, - client_addrlen, - dgram, reqsize); - break; - case SOCK_STREAM: - client = accept(fds[i].fd, NULL, NULL); - if (client != -1) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "new stream request\n"); - stream_client_start(state, - client); - } - break; - default: - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "unknown request\n"); - break; - } - } - } - } - return state; -} - /* Start the plugin's work thread. */ static int -plugin_start(Slapi_PBlock *pb) +plugin_startup(Slapi_PBlock *pb) { struct plugin_state *state; const char *pname; int i, protocol; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); - slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", - "plugin starting\n"); + slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", "plugin starting\n"); /* Register the listener sockets with the portmapper. */ - if (state->resvport != -1) { + if (state->pmap_client_socket != -1) { for (i = 0; i < state->n_listeners; i++) { - switch (state->sock_type[i]) { + switch (state->listener[i].type) { case SOCK_DGRAM: protocol = IPPROTO_UDP; pname = "UDP"; @@ -130,16 +63,16 @@ plugin_start(Slapi_PBlock *pb) pname = "TCP"; break; default: - protocol = IPPROTO_IP; - pname = "IP"; + /* never reached */ + assert(0); break; } if (protocol == IPPROTO_IP) { continue; } - if (!portmap_register(state->resvport, + if (!portmap_register(state->pmap_client_socket, YPPROG, YPVERS, protocol, - state->listenport[i])) { + state->listener[i].port)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error registering %s service " @@ -149,7 +82,7 @@ plugin_start(Slapi_PBlock *pb) } /* Start a new listening thread to handle incoming traffic. FIXME: * switch to using NSPR's threading facilities. */ - if (pthread_create(&state->tid, NULL, &listener_thread, state) != 0) { + if (pthread_create(&state->tid, NULL, &dispatch_thread, state) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error starting listener thread\n"); @@ -162,24 +95,27 @@ plugin_start(Slapi_PBlock *pb) /* Stop the plugin's work thread. */ static int -plugin_close(Slapi_PBlock *pb) +plugin_shutdown(Slapi_PBlock *pb) { struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); - if (state->resvport != -1) { + if (state->pmap_client_socket != -1) { /* Clear our registration with the portmapper. */ - portmap_unregister(state->resvport, YPPROG, YPVERS); - close(state->resvport); + portmap_unregister(state->pmap_client_socket, YPPROG, YPVERS); } return 0; } +/* Handle the part of startup that needs to be done before we drop privileges: + * bind to listening ports. */ static int -setup_listener_sockets(struct plugin_state **lstate) +plugin_state_init(struct plugin_state **lstate) { - int sockfd = -1, err, i; - struct plugin_state *state; + int sockfd = -1, err, i, j; + struct plugin_state *state = NULL; PLArenaPool *arena = NULL; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; arena = PORT_NewArena(sizeof(double)); if (arena == NULL) { @@ -191,61 +127,44 @@ setup_listener_sockets(struct plugin_state **lstate) } state->arena = arena; - /* Bind to a privileged port so that the portmapper will let us - * register after we've dropped to a non-root account. We could reuse - * the datagram listening socket we'll create later. */ - state->resvport = socket(PF_INET, SOCK_DGRAM, 0); - if (bindresvport(state->resvport, NULL) != 0) { + /* Bind to a privileged port now so that the portmapper will let us + * register after the server's dropped privileges. */ + state->pmap_client_socket = socket(PF_INET, SOCK_DGRAM, 0); + memset(&sin, 0, sizeof(sin)); + if (bindresvport(state->pmap_client_socket, &sin) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, - "error setting up portmap client socket, %d\n", - (int)getuid()); - close(state->resvport); - state->resvport = -1; + "error setting up portmap client socket, %ld\n", + (long) getuid()); + close(state->pmap_client_socket); + state->pmap_client_socket = -1; + goto failed; } - /* Privileged port, datagram and stream, IPv4 and IPv6. */ + /* We need to bind on privileged ports for both datagram and connected + * listeners, over both IPv4 and IPv6. */ + state->n_listeners = 0; for (i = 0; i < 4; i++) { - int sock_pf, sock_type, one = 1; - struct sockaddr *addr; - socklen_t addrlen; - struct sockaddr_storage ss; - struct sockaddr_in *addr4 = (struct sockaddr_in *) &ss; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &ss; + int pf, type, fd, one, port, ret; const char *sock_desc; - u_int16_t *port; - memset(&ss, 0, sizeof(ss)); - switch (i) { - case 0: - sock_pf = PF_INET; - sock_type = SOCK_DGRAM; - sock_desc = "udp"; - addr4->sin_family = AF_INET; - port = &addr4->sin_port; - break; - case 1: - sock_pf = PF_INET; - sock_type = SOCK_STREAM; - sock_desc = "tcp"; - addr4->sin_family = AF_INET; - port = &addr4->sin_port; - break; - case 2: - sock_pf = PF_INET6; - sock_type = SOCK_DGRAM; - sock_desc = "udp6"; - addr6->sin6_family = AF_INET6; - port = &addr6->sin6_port; - break; - case 3: - sock_pf = PF_INET6; - sock_type = SOCK_STREAM; - sock_desc = "tcp6"; - addr6->sin6_family = AF_INET6; - port = &addr6->sin6_port; - break; + /* Before we do anything else, on our second trip through, make + * sure that the first socket was created, because we'll need + * it for communicating with the portmapper. */ + if ((i > 0) && (state->n_listeners == 0)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "no socket available to use for " + "communicating with portmapper\n"); + continue; } - sockfd = socket(sock_pf, sock_type, 0); + /* Figure out what kind of socket we need, and a textual + * term to use in log messages. */ + pf = (i & 2) ? PF_INET6 : PF_INET; + type = (i & 1) ? SOCK_STREAM : SOCK_DGRAM; + sock_desc = (i & 2) ? ((i & 1) ? "tcp6" : "udp6") : + ((i & 1) ? "tcp" : "udp"); + /* Allocate the socket. */ + sockfd = socket(pf, type, 0); if (sockfd == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, @@ -253,6 +172,7 @@ setup_listener_sockets(struct plugin_state **lstate) sock_desc); continue; } + /* Mark the socket as reusable. */ if (setsockopt(sockfd, IPPROTO_IP, SO_REUSEADDR, &one, sizeof(one)) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, @@ -260,7 +180,10 @@ setup_listener_sockets(struct plugin_state **lstate) "error marking %s socket for reuse\n", sock_desc); } - if (bindresvport(sockfd, (struct sockaddr_in *) &ss) != 0) { + /* Bind to a reserved port. */ + ret = (pf == PF_INET6) ? bindresvport6(sockfd, &sin6) : + bindresvport(sockfd, &sin); + if (ret != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error binding %s socket to a " @@ -269,28 +192,37 @@ setup_listener_sockets(struct plugin_state **lstate) close(sockfd); continue; } - if ((sock_type == SOCK_STREAM) && (listen(sockfd, 128) == -1)) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "error marking %s socket for " - "listening: %s\n", sock_desc, - strerror(errno)); - close(sockfd); - continue; + /* Pick out the port number that we got back. */ + port = (pf == PF_INET6) ? htons(sin6.sin6_port) : + htons(sin.sin_port); + /* If it's a listening socket, let the kernel know that we're + * ready to accept client connections. */ + if (type == SOCK_STREAM) { + if (listen(sockfd, 128) == -1) { + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "error marking %s socket for " + "listening: %s\n", sock_desc, + strerror(errno)); + close(sockfd); + continue; + } } + /* Save the first socket for reuse as the portmap client + * socket. */ if (i == 0) { - state->resvport = sockfd; + state->pmap_client_socket = sockfd; } - state->listenfd[state->n_listeners] = sockfd; - state->listenport[state->n_listeners] = ntohs(*port); - state->sock_pf[state->n_listeners] = sock_pf; - state->sock_type[state->n_listeners] = sock_type; + /* Save the other info. */ + state->listener[state->n_listeners].fd = sockfd; + state->listener[state->n_listeners].port = port; + state->listener[state->n_listeners].pf = pf; + state->listener[state->n_listeners].type = type; slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, - "listening on port %d for %s%s clients\n", - state->listenport[state->n_listeners], - sock_type == SOCK_STREAM ? "tcp" : "udp", - sock_pf == PF_INET6 ? "6" : ""); + "listening on port %d for %s clients\n", + state->listener[state->n_listeners].port, + sock_desc); state->n_listeners++; } slapi_log_error(SLAPI_LOG_PLUGIN, @@ -311,15 +243,17 @@ int nis_plugin_init(Slapi_PBlock *pb) { struct plugin_state *state = NULL; - if (setup_listener_sockets(&state) == -1) { + /* Allocate a memory pool and start listening for connections. */ + if (plugin_state_init(&state) == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, - "error setting up listening sockets\n"); + "error setting up plugin\n"); return -1; } + /* Register the plugin with the server. */ slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03); slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, &plugin_description); - slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, &plugin_start); - slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, &plugin_close); + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, &plugin_startup); + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, &plugin_shutdown); slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &state->plugin_identity); state->plugin_desc = &plugin_description; slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, state); |