From bdd74a017f4f1456b63e3b621064adde8e739ac8 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 27 Mar 2008 14:36:34 -0400 Subject: try to clean this up a bit --- src/Makefile | 2 +- src/dispatch.c | 109 +++++++++++++++++++++++++ src/dispatch.h | 1 + src/plugin.c | 248 +++++++++++++++++++++------------------------------------ src/plugin.h | 9 +-- src/portmap.c | 35 ++++++-- 6 files changed, 234 insertions(+), 170 deletions(-) create mode 100644 src/dispatch.c create mode 100644 src/dispatch.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 73f2a54..ce610e9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ LDFLAGS = -lnsl -lpthread all:: plugin.so portmap -plugin.so: plugin.c portmap.c schema.c stream.c nis.c +plugin.so: dispatch.c plugin.c portmap.c schema.c stream.c nis.c $(CC) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) portmap: portmap.c $(CC) $(CFLAGS) -o $@ -DPORTMAP_MAIN $^ $(LDFLAGS) diff --git a/src/dispatch.c b/src/dispatch.c new file mode 100644 index 0000000..9435193 --- /dev/null +++ b/src/dispatch.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "dispatch.h" +#include "nis.h" +#include "plugin.h" +#include "portmap.h" +#include "schema.h" +#include "stream.h" + +/* Handle a datagram client -- read the request and handle it immediately. */ +static void +dispatch_dgram(struct plugin_state *state, int fd) +{ + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + char dgram[65536]; + int reqsize; + + /* Read the request. */ + client_addrlen = sizeof(client_addr); + reqsize = recvfrom(fd, dgram, sizeof(dgram), 0, + (struct sockaddr *) &client_addr, &client_addrlen); + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request\n"); + + /* Handle the request. */ + nis_process_request(state, fd, + (struct sockaddr *) &client_addr, client_addrlen, + dgram, reqsize); +} + +/* Handle a stream client -- answer the connection and spawn a thread to handle + * its requests. */ +static void +dispatch_stream(struct plugin_state *state, int fd) +{ + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + int client; + client = accept(fd, (struct sockaddr *) &client_addr, &client_addrlen); + if (client != -1) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "new stream request\n"); + stream_client_start(state, client); + } +} + +void * +dispatch_thread(void *p) +{ + struct plugin_state *state = p; + struct pollfd fds[4]; + int i; + for (;;) { + /* Set up for polling. */ + memset(&fds, 0, sizeof(fds)); + for (i = 0; i < state->n_listeners; i++) { + fds[i].fd = state->listener[i].fd; + fds[i].events = POLLIN; + } + switch (poll(fds, state->n_listeners, -1)) { + case -1: + return NULL; + break; + case 0: + continue; + default: + /* Iterate over listening sockets which have work for + * us to do. */ + for (i = 0; i < state->n_listeners; i++) { + if ((fds[i].revents & POLLIN) == 0) { + continue; + } + switch (state->listener[i].type) { + case SOCK_DGRAM: + dispatch_dgram(state, fds[i].fd); + break; + case SOCK_STREAM: + dispatch_stream(state, fds[i].fd); + break; + default: + /* never reached */ + assert(0); + break; + } + } + } + } + return state; +} diff --git a/src/dispatch.h b/src/dispatch.h new file mode 100644 index 0000000..b477cfd --- /dev/null +++ b/src/dispatch.h @@ -0,0 +1 @@ +void *dispatch_thread(void *p); 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 #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include +#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); diff --git a/src/plugin.h b/src/plugin.h index 102f2ad..3701173 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -14,12 +14,11 @@ struct plugin_state { pthread_t tid; Slapi_ComponentId *plugin_identity; Slapi_PluginDesc *plugin_desc; - int resvport; + int pmap_client_socket; int n_listeners; - int listenfd[4]; - int listenport[4]; - int sock_pf[4]; - int sock_type[4]; + struct { + int fd, port, pf, type; + } listener[4]; }; #endif diff --git a/src/portmap.c b/src/portmap.c index 07e32ef..ebb85de 100644 --- a/src/portmap.c +++ b/src/portmap.c @@ -18,7 +18,8 @@ #ifdef PORTMAP_MAIN #include #include -int slapi_log_error(int i, char *f, char *fmt, ...) +int +slapi_log_error(int i, char *f, char *fmt, ...) { va_list va; va_start(va, fmt); @@ -56,7 +57,7 @@ portmap_register_work(int resv_sock, XDR portmap_xdrs, auth_xdrs; struct rpc_msg msg; struct pmap map; - bool_t ret; + bool_t ret = FALSE; struct sockaddr addr; struct sockaddr_in addr4; union { @@ -68,12 +69,9 @@ portmap_register_work(int resv_sock, static u_long xid; memset(&addr, 0, sizeof(addr)); - memset(&addr4, 0, sizeof(addr4)); addr.sa_family = AF_UNSPEC; - addr4.sin_family = AF_INET; - addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr4.sin_port = htons(PMAPPORT); + /* Build the RPC header. */ memset(&msg, 0, sizeof(msg)); msg.rm_xid = xid = (time(NULL) ^ port ^ protocol ^ proc); msg.rm_direction = CALL; @@ -82,17 +80,20 @@ portmap_register_work(int resv_sock, msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = proc; + /* Build an authenticator. */ xdrmem_create(&auth_xdrs, auth_buf, sizeof(auth_buf), XDR_ENCODE); auth = authnone_create(); auth_marshall(auth, &auth_xdrs); msg.rm_call.cb_cred = auth->ah_cred; msg.rm_call.cb_verf = auth->ah_verf; + /* Populate the arguments for this request. */ map.pm_prog = program; map.pm_vers = version; map.pm_prot = protocol; map.pm_port = port; + /* Encode the header and the arguments, then clean up temporaries. */ xdrmem_create(&portmap_xdrs, portmap_buf, sizeof(portmap_buf), XDR_ENCODE); xdr_callmsg(&portmap_xdrs, &msg); @@ -103,25 +104,39 @@ portmap_register_work(int resv_sock, xdr_destroy(&portmap_xdrs); memset(&portmap_xdrs, 0, sizeof(portmap_xdrs)); - client_addrlen = 0; + /* Connect to the local portmapper. */ + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_family = AF_INET; + addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr4.sin_port = htons(PMAPPORT); connect(resv_sock, (struct sockaddr*) &addr4, sizeof(addr4)); + + /* Transmit our request. Retry a few times if it doesn't go through. */ for (i = 1; i < 32; i *= 2) { + /* Try to send our request. If there's any problem, + * immediately retry. */ if (send(resv_sock, &portmap_buf, portmap_length, 0) != portmap_length) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", "error sending request to portmap\n"); continue; } + + /* Wait for a response. */ pollfd.fd = resv_sock; pollfd.events = POLLIN | POLLERR; if ((poll(&pollfd, 1, i * 1000) > 0) && (pollfd.revents & POLLIN)) { + /* Read the response. */ client_addrlen = sizeof(client_addr); reply_length = recvfrom(resv_sock, reply_buf, sizeof(reply_buf), 0, (struct sockaddr *)&client_addr, &client_addrlen); + /* Decode the response. */ if (reply_length > 0) { + /* Decode an RPC header and the returned + * boolean from the buffer. */ memset(&msg, 0, sizeof(msg)); xdrmem_create(&portmap_xdrs, reply_buf, reply_length, @@ -141,6 +156,7 @@ portmap_register_work(int resv_sock, } } + /* Disconnect from the portmapper, but keep our socket around. */ connect(resv_sock, &addr, sizeof(addr)); if (i == 32) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", @@ -148,6 +164,8 @@ portmap_register_work(int resv_sock, return FALSE; } + /* Check that the portmapper didn't just reject the request out of + * hand. */ if (msg.rm_reply.rp_stat != MSG_ACCEPTED) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", "portmap request not accepted\n"); @@ -196,6 +214,7 @@ portmap_register_work(int resv_sock, xdr_destroy(&portmap_xdrs); } + /* Validate the portmapper's credentials. */ auth = authunix_create_default(); if (auth_validate(auth, &msg.rm_reply.rp_acpt.ar_verf)) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", @@ -206,6 +225,7 @@ portmap_register_work(int resv_sock, } auth_destroy(auth); + /* Check if we the portmapper gave us a reply argument. */ if (msg.rm_reply.rp_acpt.ar_stat != SUCCESS) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", "portmap request not processed\n"); @@ -213,6 +233,7 @@ portmap_register_work(int resv_sock, return FALSE; } + /* Check what happened. */ if (ret) { slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", "portmap request succeeded\n"); -- cgit