From 1b8a08a0f0bb956bcdd0dd92e19c9a7c5a1fbe02 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 30 Jun 2008 11:56:23 -0400 Subject: - rename dispatch to disp-nis --- src/Makefile.am | 8 +- src/backend.c | 2 +- src/disp-nis.c | 993 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/disp-nis.h | 38 +++ src/dispatch.c | 993 -------------------------------------------------------- src/dispatch.h | 38 --- src/dummymap.c | 2 +- src/map.c | 2 +- src/nis.c | 2 +- src/nis.h | 2 +- src/plug-nis.c | 2 +- 11 files changed, 1041 insertions(+), 1041 deletions(-) create mode 100644 src/disp-nis.c create mode 100644 src/disp-nis.h delete mode 100644 src/dispatch.c delete mode 100644 src/dispatch.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 1d8e6e3..4bf1c6e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,8 +12,8 @@ nisserver_plugin_la_SOURCES = \ backend.h \ defaults.c \ defaults.h \ - dispatch.c \ - dispatch.h \ + disp-nis.c \ + disp-nis.h \ format.c \ format.h \ map.c \ @@ -30,8 +30,8 @@ nisserver_plugin_la_LIBADD = $(RUNTIME_LIBS) $(LIBWRAP) -lnsl $(LIBPTHREAD) noinst_LTLIBRARIES = dummy-nis-plugin.la dummy_nis_plugin_la_SOURCES = \ - dispatch.c \ - dispatch.h \ + disp-nis.c \ + disp-nis.h \ dummymap.c \ map.h \ nis.c \ diff --git a/src/backend.c b/src/backend.c index e8409e8..2bfa02e 100644 --- a/src/backend.c +++ b/src/backend.c @@ -45,7 +45,7 @@ #include "backend.h" #include "defaults.h" -#include "dispatch.h" +#include "disp-nis.h" #include "format.h" #include "plug-nis.h" #include "map.h" diff --git a/src/disp-nis.c b/src/disp-nis.c new file mode 100644 index 0000000..f2a6139 --- /dev/null +++ b/src/disp-nis.c @@ -0,0 +1,993 @@ +/* + * Copyright 2008 Red Hat, Inc. + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This Program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; if not, write to the + * + * Free Software Foundation, Inc. + * 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_TCPD_H +#include +#endif + +#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H +#include +#include +#include +#else +#include +#endif + +#include +#include +#include + +#include "plug-nis.h" +#include "disp-nis.h" +#include "nis.h" +#include "portmap.h" + +#define MAX_CLIENT_IDLE (60 * 1000) + +#ifndef TCPD_H +struct request_info; +#endif + +/* Handle all incoming data. */ +struct dispatch_client { + /* The client socket and address. */ + int client_fd; + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + bool_t client_secure; + /* The client state. */ + enum { + client_invalid, + client_closing, + client_reading, + client_replying_with_more, + client_replying_final, + } client_state; + /* The client's request while we're reading it. */ + char client_inbuf[4096]; + ssize_t client_inbuf_used; + char *client_query; + ssize_t client_query_size; + void *client_query_cookie; + /* The outgoing replies to the client, when we're sending one. */ + char *client_outbuf; + ssize_t client_outbuf_size, client_outbuf_used; + /* Working space for a client reply. */ + char *client_workbuf; + ssize_t client_workbuf_size; + /* This is a linked list. */ + struct dispatch_client *client_next; +}; + +struct dispatch_client_data { + struct dispatch_client *connected; + struct { + int client_fd; + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + bool_t client_secure; + char *reply_buf; + size_t reply_buf_size; + } dgram; +}; + +struct securenet_info { + int sn_family; + union { + struct { + struct in_addr address, netmask; + } sin; + struct { + struct in6_addr address, netmask; + } sin6; + } sn_addr; + struct securenet_info *sn_next; +}; + +/* Perform securenets access control. */ +void +dispatch_securenets_clear(struct plugin_state *state) +{ + struct securenet_info *sn, *next; + next = state->securenet_info; + while (next != NULL) { + sn = next; + next = sn->sn_next; + free(sn); + } + state->securenet_info = NULL; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "cleared securenets access list\n"); +} +void +dispatch_securenets_add(struct plugin_state *state, const char *value) +{ + struct securenet_info *sn; + const char *p, *q; + char *tmp; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "adding securenets access entry \"%s\"\n", value); + sn = malloc(sizeof(*sn)); + if (sn == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "out of memory parsing securenets entry " + "\"%s\"\n", value); + return; + } + tmp = strdup(value); + if (tmp == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "out of memory parsing securenets entry " + "\"%s\"\n", value); + free(sn); + return; + } + + /* Pull out the first token. */ + p = value + strspn(value, " \t"); + q = p + strcspn(p, " \t"); + strncpy(tmp, p, q - p); + tmp[q - p] = '\0'; + + /* Try to parse it. */ + sn->sn_family = AF_UNSPEC; + if (inet_pton(AF_INET, tmp, &sn->sn_addr.sin.netmask) > 0) { + sn->sn_family = AF_INET; + } else { + if (inet_pton(AF_INET6, tmp, &sn->sn_addr.sin6.netmask) > 0) { + sn->sn_family = AF_INET6; + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error parsing \"%s\" as an address, " + "ignoring\n", tmp); + } + } + if (sn->sn_family == AF_UNSPEC) { + free(tmp); + free(sn); + return; + } + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "parsed netmask(?) \"%s\" family=%d\n", tmp, + sn->sn_family); + + /* Pull out the second token. */ + p = q + strspn(q, " \t"); + q = p + strcspn(p, " \t#"); + strncpy(tmp, p, q - p); + tmp[q - p] = '\0'; + switch (sn->sn_family) { + case AF_INET: + if (inet_pton(AF_INET, tmp, &sn->sn_addr.sin.address) <= 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error parsing %s as an IPv4 address, " + "ignoring\n", tmp); + sn->sn_family = AF_UNSPEC; + } + break; + case AF_INET6: + if (inet_pton(AF_INET6, tmp, &sn->sn_addr.sin6.address) <= 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error parsing %s as an IPv6 address, " + "ignoring\n", tmp); + sn->sn_family = AF_UNSPEC; + } + break; + default: + break; + } + + if (sn->sn_family == AF_UNSPEC) { + free(sn); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "parsed address \"%s\" family=%d\n", tmp, + sn->sn_family); + sn->sn_next = state->securenet_info; + state->securenet_info = sn; + } + + free(tmp); +} +static bool_t +dispatch_access(struct plugin_state *state, + struct sockaddr_storage *client_addr) +{ + struct securenet_info *sn; + struct in_addr addr; + struct in6_addr addr6, mask6, masked6; + int i; + for (sn = state->securenet_info; sn != NULL; sn = sn->sn_next) { + switch (client_addr->ss_family) { + case AF_INET: + if (sn->sn_family != AF_INET) { + continue; + } + addr = ((struct sockaddr_in*) client_addr)->sin_addr; + if ((addr.s_addr & + sn->sn_addr.sin.netmask.s_addr) == + (sn->sn_addr.sin.address.s_addr & + sn->sn_addr.sin.netmask.s_addr)) { + return TRUE; + } + break; + case AF_INET6: + addr6 = ((struct sockaddr_in6*) client_addr)->sin6_addr; + if ((sn->sn_family == AF_INET) && + IN6_IS_ADDR_V4MAPPED(&addr6)) { + if ((((uint32_t*)addr6.s6_addr)[3] & + sn->sn_addr.sin.netmask.s_addr) == + (sn->sn_addr.sin.address.s_addr & + sn->sn_addr.sin.netmask.s_addr)) { + return TRUE; + } + } + if (sn->sn_family == AF_INET6) { + mask6 = sn->sn_addr.sin6.netmask; + for (i = 0; i < 16; i++) { + addr6.s6_addr[i] &= mask6.s6_addr[i]; + masked6.s6_addr[i] &= mask6.s6_addr[i]; + if (addr6.s6_addr[i] != + masked6.s6_addr[i]) { + break; + } + } + if (i == 16) { + return TRUE; + } + } + break; + default: + break; + } + } + return state->securenet_info ? FALSE : TRUE; +} + +/* Send a reply, unbuffered datagram version. */ +static bool_t +dispatch_reply_fragment_dgram(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, + XDR *reply_xdrs, + bool_t first_fragment, bool_t last_fragment) +{ + if (xdr_replymsg(reply_xdrs, reply)) { + if (!first_fragment || !last_fragment) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "trying to sending datagram reply (%d " + "bytes), even though the reply is not " + "suitable for transmission as a " + "datagram\n", xdr_getpos(reply_xdrs)); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sending datagram reply (%d bytes)\n", + xdr_getpos(reply_xdrs)); + } + sendto(cdata->dgram.client_fd, + cdata->dgram.reply_buf, xdr_getpos(reply_xdrs), + 0, + (struct sockaddr *) &cdata->dgram.client_addr, + cdata->dgram.client_addrlen); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error sending datagram reply -- too large?\n"); + xdr_setpos(reply_xdrs, 0); + reply->rm_reply.rp_acpt.ar_stat = SYSTEM_ERR; + reply->rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) &xdr_void; + reply->rm_reply.rp_acpt.ar_results.where = NULL; + if (xdr_replymsg(reply_xdrs, reply)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "sending system-error response\n"); + sendto(cdata->dgram.client_fd, + cdata->dgram.reply_buf, xdr_getpos(reply_xdrs), + 0, + (struct sockaddr *) &cdata->dgram.client_addr, + cdata->dgram.client_addrlen); + } + } + return TRUE; +} +static void +dispatch_reply_dgram(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, XDR *reply_xdrs) +{ + dispatch_reply_fragment_dgram(state, cdata, + reply, reply_xdrs, + TRUE, TRUE); +} + +static bool_t +dispatch_reply_fragment_connected(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, + XDR *reply_xdrs, + bool_t first_fragment, bool_t last_fragment) +{ + uint32_t len; + ssize_t next_size; + /* Record reply - first fragment. */ + if (first_fragment) { + xdr_replymsg(reply_xdrs, reply); + } + /* If we don't have space for the data, stop now. */ + next_size = cdata->connected->client_outbuf_used + 4 + + xdr_getpos(reply_xdrs); + if (next_size > cdata->connected->client_outbuf_size) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "failed to queue stream reply (4+%d bytes)!\n", + xdr_getpos(reply_xdrs)); + return FALSE; + } + /* If we already have data in the buffer, and this would put us over + * the target size, then punt it until next time. */ + if ((cdata->connected->client_outbuf_used > 4) && + (next_size > DEFAULT_TARGET_REPLY_SIZE)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "saving stream reply (4+%d bytes) for later\n", + xdr_getpos(reply_xdrs)); + return FALSE; + } + /* Calculate the fragment length bytes. */ + len = htonl(xdr_getpos(reply_xdrs) | (last_fragment ? 0x80000000 : 0)); + /* Queue the data. */ + memcpy(cdata->connected->client_outbuf + + cdata->connected->client_outbuf_used, + &len, 4); + memcpy(cdata->connected->client_outbuf + + cdata->connected->client_outbuf_used + 4, + cdata->connected->client_workbuf, + xdr_getpos(reply_xdrs)); + cdata->connected->client_outbuf_used += (4 + xdr_getpos(reply_xdrs)); + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "queued stream reply (4+%d bytes), %d total in queue\n", + xdr_getpos(reply_xdrs), + cdata->connected->client_outbuf_used); + return TRUE; +} +/* Send an entire reply record at once. */ +static void +dispatch_reply_connected(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, + XDR *reply_xdrs) +{ + dispatch_reply_fragment_connected(state, cdata, + reply, reply_xdrs, + TRUE, TRUE); +} + +/* Handle a datagram client -- read the request and handle it immediately. */ +static void +dispatch_dgram(struct plugin_state *state, int fd) +{ + struct dispatch_client_data cdata; + char dgram[65536]; + int reqsize; + + /* Read the request. */ + cdata.dgram.client_fd = fd; + cdata.dgram.client_addrlen = sizeof(cdata.dgram.client_addr); + cdata.dgram.reply_buf = malloc(state->max_dgram_size); + cdata.dgram.reply_buf_size = state->max_dgram_size; + if (cdata.dgram.reply_buf == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "error reading datagram request: " + "out of memory\n"); + return; + } + reqsize = recvfrom(cdata.dgram.client_fd, dgram, sizeof(dgram), 0, + (struct sockaddr *) &cdata.dgram.client_addr, + &cdata.dgram.client_addrlen); + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request (%d bytes)\n", reqsize); +#ifdef HAVE_TCPD + if ((request_set(state->request_info, + RQ_CLIENT_SIN, &cdata.dgram.client_addr, + 0) == NULL) || + (fromhost(state->request_info), + hosts_access(state->request_info) == 0)) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request denied by tcp_wrappers\n"); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request permitted by tcp_wrappers\n"); + } +#endif + if (dispatch_access(state, &cdata.dgram.client_addr) == 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request denied by securenets\n"); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "datagram request permitted by securenets\n"); + } + + switch (cdata.dgram.client_addr.ss_family) { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + case AF_INET: + sin = (struct sockaddr_in *) &cdata.dgram.client_addr; + cdata.dgram.client_secure = ntohs(sin->sin_port) < 1024; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) &cdata.dgram.client_addr; + cdata.dgram.client_secure = ntohs(sin6->sin6_port) < 1024; + break; + default: + cdata.dgram.client_secure = FALSE; + break; + } + + /* Handle the request. */ + nis_process_request(state, dgram, reqsize, + &dispatch_reply_fragment_dgram, + &dispatch_reply_dgram, + &cdata, cdata.dgram.client_secure, + cdata.dgram.reply_buf, + cdata.dgram.reply_buf_size, + NULL); + free(cdata.dgram.reply_buf); +} + +/* Set the client's record up to start reading a new query. */ +static void +client_set_reading(struct plugin_state *state, struct dispatch_client *client) +{ + client->client_inbuf_used = 0; + free(client->client_query); + client->client_query = NULL; + client->client_query_size = 0; + client->client_outbuf_used = 0; + client->client_state = client_reading; +} + +/* Set the client's record up to be cleaned up. */ +static void +client_set_closing(struct plugin_state *state, struct dispatch_client *client) +{ + client->client_inbuf_used = 0; + free(client->client_query); + client->client_query = NULL; + client->client_query_size = 0; + free(client->client_outbuf); + client->client_outbuf = NULL; + client->client_outbuf_size = 0; + client->client_outbuf_used = 0; + free(client->client_workbuf); + client->client_workbuf = NULL; + client->client_workbuf_size = 0; + client->client_state = client_closing; +} + +/* Set the client's record up. */ +static struct dispatch_client * +dispatch_accept_client(struct plugin_state *state, int fd) +{ + struct dispatch_client *client; + int flags; + char *outbuf, *workbuf; + client = malloc(sizeof(*client)); + if (client == NULL) { + return NULL; + } + outbuf = malloc(state->max_dgram_size + state->max_value_size); + if (outbuf == NULL) { + free(client); + return NULL; + } + workbuf = malloc(state->max_dgram_size + state->max_value_size); + if (workbuf == NULL) { + free(outbuf); + free(client); + return NULL; + } + fd = accept(fd, (struct sockaddr *)&client->client_addr, + &client->client_addrlen); + if (fd == -1) { + free(workbuf); + free(outbuf); + free(client); + return NULL; + } + switch (client->client_addr.ss_family) { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + case AF_INET: + sin = (struct sockaddr_in *) &client->client_addr; + client->client_secure = ntohs(sin->sin_port) < 1024; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) &client->client_addr; + client->client_secure = ntohs(sin6->sin6_port) < 1024; + break; + default: + client->client_secure = FALSE; + break; + } +#ifdef HAVE_TCPD_H + if ((request_set(state->request_info, + RQ_CLIENT_SIN, &client->client_addr, + 0) == NULL) || + (fromhost(state->request_info), + hosts_access(state->request_info) == 0)) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "tcp_wrappers rejected client on %d\n", fd); + close(fd); + free(workbuf); + free(outbuf); + free(client); + return NULL; + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "tcp_wrappers allows client on %d\n", fd); + } +#endif + if (dispatch_access(state, &client->client_addr) == 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "securenets rejected client on %d\n", fd); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "securenets allows client on %d\n", fd); + } + flags = fcntl(fd, F_GETFL); + if ((flags & O_NONBLOCK) == 0) { + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "new connected client on %d\n", fd); + memset(client, 0, sizeof(*client)); + client->client_fd = fd; + client->client_outbuf = outbuf; + client->client_outbuf_size = state->max_dgram_size + + state->max_value_size; + client->client_workbuf = workbuf; + client->client_workbuf_size = state->max_dgram_size + + state->max_value_size; + client_set_reading(state, client); + return client; +} + +/* Decide what to do next. */ +static void +client_interpret_nis_result(struct plugin_state *state, + struct dispatch_client *client) +{ + if (client->client_query_cookie != NULL) { + client->client_state = client_replying_with_more; + } else { + if (client->client_outbuf_used > 0) { + client->client_state = client_replying_final; + } else { + client_set_reading(state, client); + } + } +} + +/* Handle reading state. */ +static void +client_read(struct plugin_state *state, struct dispatch_client *client) +{ + ssize_t count; + int32_t len, nlen; + int last; + char *query; + struct dispatch_client_data client_data; + /* Try to read some data. */ + count = read(client->client_fd, + client->client_inbuf + client->client_inbuf_used, + sizeof(client->client_inbuf) - client->client_inbuf_used); + if (count <= 0) { + if ((count != -1) || (errno != EAGAIN)) { + /* Disconnect the client. */ + if (count == 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "no more data from %d, " + "marking for closing\n", + client->client_fd); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error reading from %d, " + "marking for closing\n", + client->client_fd); + } + client_set_closing(state, client); + } + } else { + /* Record the data as added to the fragment buffer. */ + client->client_inbuf_used += count; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "have %d bytes from %d\n", + client->client_inbuf_used, + client->client_fd); + /* Check if we've got a complete fragment. */ + if (client->client_inbuf_used < 4) { + /* We don't even have a length, so continue reading. */ + return; + } + /* Read the length of the first fragment in the buffer. */ + memcpy(&nlen, client->client_inbuf, 4); + len = ntohl(nlen); + last = ((len & 0x80000000) != 0); + len &= 0x7fffffff; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "fragment is %d bytes long%s, " + "have %d bytes pending, on %d\n", + len, last ? " (last one)" : "", + client->client_inbuf_used, client->client_fd); + if ((len + 4) <= client->client_inbuf_used) { + /* Got at least one fragment! */ + nlen = len + client->client_query_size; + query = malloc(nlen); + if (query == NULL) { + /* Out of memory, we'll have to try again + * later. */ + return; + } + /* Copy any previously-received fragments and append + * this one. */ + if (client->client_query_size > 0) { + memcpy(query, + client->client_query, + client->client_query_size); + } + memcpy(query + client->client_query_size, + client->client_inbuf + 4, + len); + /* Save the new query-in-progress. */ + free(client->client_query); + client->client_query = query; + client->client_query_size = nlen; + /* Drop the fragment from the incoming + * buffer. */ + memmove(client->client_inbuf, + client->client_inbuf + (len + 4), + client->client_inbuf_used - (len + 4)); + client->client_inbuf_used -= (len + 4); + } + if (last) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "query is %d bytes long on %d\n", + client->client_query_size, + client->client_fd); + /* We have a complete query. Pass it on down. */ + memset(&client_data, 0, sizeof(client_data)); + client_data.connected = client; + nis_process_request(state, + client->client_query, + client->client_query_size, + &dispatch_reply_fragment_connected, + &dispatch_reply_connected, + &client_data, client->client_secure, + client->client_workbuf, + client->client_workbuf_size, + &client->client_query_cookie); + /* Decide what to do next. */ + client_interpret_nis_result(state, client); + } + } +} + +/* Handle replying states. */ +static void +client_write(struct plugin_state *state, struct dispatch_client *client) +{ + ssize_t count; + int32_t len; + struct dispatch_client_data client_data; + + /* Try to send some of the pending data. */ + len = client->client_outbuf_used; + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "attempting to send %d bytes to %d\n", + len, client->client_fd); + count = write(client->client_fd, client->client_outbuf, len); + if (count <= 0) { + if ((count != -1) || (errno != EAGAIN)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "error sending %d bytes to %d\n", + client->client_outbuf_used, + client->client_fd); + /* Fail, disconnect because we're out of sync. */ + client_set_closing(state, client); + } + return; + } + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "sent %d bytes to %d\n", count, client->client_fd); + if (count == client->client_outbuf_used) { + /* There's no more data to send. */ + if (client->client_state == client_replying_final) { + /* Done. Go back to reading next time. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "waiting for next query on %d\n", + client->client_fd); + client_set_reading(state, client); + } else { + /* More to send, so ask for more reply data. */ + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "fetching more data for %d\n", + client->client_fd); + client->client_outbuf_used = 0; + memset(&client_data, 0, sizeof(client_data)); + client_data.connected = client; + nis_process_request(state, + client->client_query, + client->client_query_size, + &dispatch_reply_fragment_connected, + &dispatch_reply_connected, + &client_data, client->client_secure, + client->client_workbuf, + client->client_workbuf_size, + &client->client_query_cookie); + client_interpret_nis_result(state, client); + } + } else { + /* Partial write, adjust outgoing buffer for next time. */ + memmove(client->client_outbuf, + client->client_outbuf + count, + client->client_outbuf_used - count); + client->client_outbuf_used -= count; + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "%d bytes to go for %d\n", + client->client_outbuf_used, + client->client_fd); + } +} + +static void +dispatch_service_client(struct plugin_state *state, + struct dispatch_client *client, + struct pollfd *fd) +{ + switch (client->client_state) { + case client_reading: + if (fd->revents & POLLIN) { + client_read(state, client); + } else { + client_set_closing(state, client); + } + break; + case client_replying_with_more: + case client_replying_final: + if (fd->revents & POLLOUT) { + client_write(state, client); + } else { + client_set_closing(state, client); + } + break; + case client_closing: + case client_invalid: + /* never reached */ + assert(0); + break; + } +} + +void * +dispatch_thread(struct wrapped_thread *t) +{ + struct dispatch_client *clients, *client, *next, **list; + struct plugin_state *state = wrap_thread_arg(t); + struct pollfd *fds; + int i, n_fds, client_count; + bool_t stop; + + clients = NULL; + client_count = 0; + fds = NULL; + + for (stop = FALSE; !stop;) { + /* Prune out recently-disconnected clients. */ + list = &clients; + while (*list != NULL) { + client = *list; + next = client->client_next; + if (client->client_state == client_closing) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "pruning client %d\n", + client->client_fd); + if (client->client_fd != -1) { + close(client->client_fd); + } + free(client); + *list = next; + } else { + list = &(client->client_next); + } + } + /* Count the number of connected clients we have. */ + client = clients; + i = 0; + while (client != NULL) { + next = client->client_next; + client = next; + i++; + } + /* If the "fds" block isn't big enough (or doesn't exist yet), + * reallocate it. */ + if (i > client_count) { + free(fds); + fds = NULL; + client_count = i; + } + if (fds == NULL) { + fds = malloc((state->n_listeners + client_count + 1) * + sizeof(fds[0])); + if (fds == NULL) { + /* Wait a bit, then try again? */ + poll(NULL, 0, 10000); + continue; + } + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "%d connected clients\n", i); + + /* Fill in the set of polling descriptors. */ + memset(fds, 0, + sizeof((state->n_listeners + client_count + 1) * + sizeof(fds[0]))); + for (i = 0; i < state->n_listeners; i++) { + fds[i].fd = state->listener[i].fd; + fds[i].events = POLLIN; + } + + /* Add the shutdown pipe reader. */ + fds[i].fd = wrap_thread_stopfd(t); + fds[i].events = POLLIN; + if (fds[i].fd != -1) { + i++; + } + + /* Add the client list. */ + client = clients; + while (client != NULL) { + fds[i].fd = client->client_fd; + switch (client->client_state) { + case client_reading: + fds[i].events = POLLIN; + break; + case client_replying_with_more: + case client_replying_final: + fds[i].events = POLLOUT; + break; + case client_closing: + case client_invalid: + /* shouldn't happen */ + assert(0); + break; + } + client = client->client_next; + i++; + } + + /* Check for status updates. */ + slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, + "listening\n"); + n_fds = i; + switch (poll(fds, n_fds, -1)) { + case -1: + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "done waiting\n"); + return NULL; + break; + case 0: + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "no request(timeout?)\n"); + continue; + default: + break; + } + + /* Save the head of the existing clients list. */ + client = clients; + + /* Iterate over listening sockets which have work for us. */ + for (i = 0; i < state->n_listeners; i++) { + if ((fds[i].revents & POLLIN) == 0) { + continue; + } + switch (state->listener[i].type) { + case SOCK_DGRAM: + /* Datagram requests we handle right + * here, right now. */ + dispatch_dgram(state, fds[i].fd); + break; + case SOCK_STREAM: + /* Try to accept a new client. */ + next = dispatch_accept_client(state, fds[i].fd); + if (next != NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "new client on %d\n", + next->client_fd); + next->client_next = clients; + clients = next; + } + break; + default: + /* never reached */ + assert(0); + break; + } + } + + /* Add the shutdown pipe reader. */ + if (fds[i].revents & POLLIN) { + stop = TRUE; + } + i++; + + /* Service the already-connected clients. */ + for (; i < n_fds; i++, client = client->client_next) { + assert(client != NULL); + assert(client->client_fd == fds[i].fd); + dispatch_service_client(state, client, &fds[i]); + } + } + slapi_log_error(SLAPI_LOG_PLUGIN, + state->plugin_desc->spd_id, + "listening thread stopping\n"); + return state; +} diff --git a/src/disp-nis.h b/src/disp-nis.h new file mode 100644 index 0000000..67bd547 --- /dev/null +++ b/src/disp-nis.h @@ -0,0 +1,38 @@ +/* + * Copyright 2008 Red Hat, Inc. + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This Program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; if not, write to the + * + * Free Software Foundation, Inc. + * 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + * + */ + +#ifndef disp_nis_h +#define disp_nis_h +struct plugin_state; +struct wrapped_thread; +struct dispatch_client_data; +void *dispatch_thread(struct wrapped_thread *t); +typedef bool_t (dispatch_reply_fragment)(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, XDR *reply_xdrs, + bool_t first_fragment, + bool_t last_fragment); +typedef void (dispatch_reply)(struct plugin_state *state, + struct dispatch_client_data *cdata, + struct rpc_msg *reply, XDR *reply_xdrs); +void dispatch_securenets_clear(struct plugin_state *state); +void dispatch_securenets_add(struct plugin_state *state, const char *value); +#endif diff --git a/src/dispatch.c b/src/dispatch.c deleted file mode 100644 index 3a4ae6b..0000000 --- a/src/dispatch.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - * Copyright 2008 Red Hat, Inc. - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This Program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; if not, write to the - * - * Free Software Foundation, Inc. - * 59 Temple Place, Suite 330 - * Boston, MA 02111-1307 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_TCPD_H -#include -#endif - -#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H -#include -#include -#include -#else -#include -#endif - -#include -#include -#include - -#include "plug-nis.h" -#include "dispatch.h" -#include "nis.h" -#include "portmap.h" - -#define MAX_CLIENT_IDLE (60 * 1000) - -#ifndef TCPD_H -struct request_info; -#endif - -/* Handle all incoming data. */ -struct dispatch_client { - /* The client socket and address. */ - int client_fd; - struct sockaddr_storage client_addr; - socklen_t client_addrlen; - bool_t client_secure; - /* The client state. */ - enum { - client_invalid, - client_closing, - client_reading, - client_replying_with_more, - client_replying_final, - } client_state; - /* The client's request while we're reading it. */ - char client_inbuf[4096]; - ssize_t client_inbuf_used; - char *client_query; - ssize_t client_query_size; - void *client_query_cookie; - /* The outgoing replies to the client, when we're sending one. */ - char *client_outbuf; - ssize_t client_outbuf_size, client_outbuf_used; - /* Working space for a client reply. */ - char *client_workbuf; - ssize_t client_workbuf_size; - /* This is a linked list. */ - struct dispatch_client *client_next; -}; - -struct dispatch_client_data { - struct dispatch_client *connected; - struct { - int client_fd; - struct sockaddr_storage client_addr; - socklen_t client_addrlen; - bool_t client_secure; - char *reply_buf; - size_t reply_buf_size; - } dgram; -}; - -struct securenet_info { - int sn_family; - union { - struct { - struct in_addr address, netmask; - } sin; - struct { - struct in6_addr address, netmask; - } sin6; - } sn_addr; - struct securenet_info *sn_next; -}; - -/* Perform securenets access control. */ -void -dispatch_securenets_clear(struct plugin_state *state) -{ - struct securenet_info *sn, *next; - next = state->securenet_info; - while (next != NULL) { - sn = next; - next = sn->sn_next; - free(sn); - } - state->securenet_info = NULL; - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "cleared securenets access list\n"); -} -void -dispatch_securenets_add(struct plugin_state *state, const char *value) -{ - struct securenet_info *sn; - const char *p, *q; - char *tmp; - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "adding securenets access entry \"%s\"\n", value); - sn = malloc(sizeof(*sn)); - if (sn == NULL) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "out of memory parsing securenets entry " - "\"%s\"\n", value); - return; - } - tmp = strdup(value); - if (tmp == NULL) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "out of memory parsing securenets entry " - "\"%s\"\n", value); - free(sn); - return; - } - - /* Pull out the first token. */ - p = value + strspn(value, " \t"); - q = p + strcspn(p, " \t"); - strncpy(tmp, p, q - p); - tmp[q - p] = '\0'; - - /* Try to parse it. */ - sn->sn_family = AF_UNSPEC; - if (inet_pton(AF_INET, tmp, &sn->sn_addr.sin.netmask) > 0) { - sn->sn_family = AF_INET; - } else { - if (inet_pton(AF_INET6, tmp, &sn->sn_addr.sin6.netmask) > 0) { - sn->sn_family = AF_INET6; - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error parsing \"%s\" as an address, " - "ignoring\n", tmp); - } - } - if (sn->sn_family == AF_UNSPEC) { - free(tmp); - free(sn); - return; - } - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "parsed netmask(?) \"%s\" family=%d\n", tmp, - sn->sn_family); - - /* Pull out the second token. */ - p = q + strspn(q, " \t"); - q = p + strcspn(p, " \t#"); - strncpy(tmp, p, q - p); - tmp[q - p] = '\0'; - switch (sn->sn_family) { - case AF_INET: - if (inet_pton(AF_INET, tmp, &sn->sn_addr.sin.address) <= 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error parsing %s as an IPv4 address, " - "ignoring\n", tmp); - sn->sn_family = AF_UNSPEC; - } - break; - case AF_INET6: - if (inet_pton(AF_INET6, tmp, &sn->sn_addr.sin6.address) <= 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error parsing %s as an IPv6 address, " - "ignoring\n", tmp); - sn->sn_family = AF_UNSPEC; - } - break; - default: - break; - } - - if (sn->sn_family == AF_UNSPEC) { - free(sn); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "parsed address \"%s\" family=%d\n", tmp, - sn->sn_family); - sn->sn_next = state->securenet_info; - state->securenet_info = sn; - } - - free(tmp); -} -static bool_t -dispatch_access(struct plugin_state *state, - struct sockaddr_storage *client_addr) -{ - struct securenet_info *sn; - struct in_addr addr; - struct in6_addr addr6, mask6, masked6; - int i; - for (sn = state->securenet_info; sn != NULL; sn = sn->sn_next) { - switch (client_addr->ss_family) { - case AF_INET: - if (sn->sn_family != AF_INET) { - continue; - } - addr = ((struct sockaddr_in*) client_addr)->sin_addr; - if ((addr.s_addr & - sn->sn_addr.sin.netmask.s_addr) == - (sn->sn_addr.sin.address.s_addr & - sn->sn_addr.sin.netmask.s_addr)) { - return TRUE; - } - break; - case AF_INET6: - addr6 = ((struct sockaddr_in6*) client_addr)->sin6_addr; - if ((sn->sn_family == AF_INET) && - IN6_IS_ADDR_V4MAPPED(&addr6)) { - if ((((uint32_t*)addr6.s6_addr)[3] & - sn->sn_addr.sin.netmask.s_addr) == - (sn->sn_addr.sin.address.s_addr & - sn->sn_addr.sin.netmask.s_addr)) { - return TRUE; - } - } - if (sn->sn_family == AF_INET6) { - mask6 = sn->sn_addr.sin6.netmask; - for (i = 0; i < 16; i++) { - addr6.s6_addr[i] &= mask6.s6_addr[i]; - masked6.s6_addr[i] &= mask6.s6_addr[i]; - if (addr6.s6_addr[i] != - masked6.s6_addr[i]) { - break; - } - } - if (i == 16) { - return TRUE; - } - } - break; - default: - break; - } - } - return state->securenet_info ? FALSE : TRUE; -} - -/* Send a reply, unbuffered datagram version. */ -static bool_t -dispatch_reply_fragment_dgram(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, - XDR *reply_xdrs, - bool_t first_fragment, bool_t last_fragment) -{ - if (xdr_replymsg(reply_xdrs, reply)) { - if (!first_fragment || !last_fragment) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "trying to sending datagram reply (%d " - "bytes), even though the reply is not " - "suitable for transmission as a " - "datagram\n", xdr_getpos(reply_xdrs)); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "sending datagram reply (%d bytes)\n", - xdr_getpos(reply_xdrs)); - } - sendto(cdata->dgram.client_fd, - cdata->dgram.reply_buf, xdr_getpos(reply_xdrs), - 0, - (struct sockaddr *) &cdata->dgram.client_addr, - cdata->dgram.client_addrlen); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error sending datagram reply -- too large?\n"); - xdr_setpos(reply_xdrs, 0); - reply->rm_reply.rp_acpt.ar_stat = SYSTEM_ERR; - reply->rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) &xdr_void; - reply->rm_reply.rp_acpt.ar_results.where = NULL; - if (xdr_replymsg(reply_xdrs, reply)) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "sending system-error response\n"); - sendto(cdata->dgram.client_fd, - cdata->dgram.reply_buf, xdr_getpos(reply_xdrs), - 0, - (struct sockaddr *) &cdata->dgram.client_addr, - cdata->dgram.client_addrlen); - } - } - return TRUE; -} -static void -dispatch_reply_dgram(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, XDR *reply_xdrs) -{ - dispatch_reply_fragment_dgram(state, cdata, - reply, reply_xdrs, - TRUE, TRUE); -} - -static bool_t -dispatch_reply_fragment_connected(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, - XDR *reply_xdrs, - bool_t first_fragment, bool_t last_fragment) -{ - uint32_t len; - ssize_t next_size; - /* Record reply - first fragment. */ - if (first_fragment) { - xdr_replymsg(reply_xdrs, reply); - } - /* If we don't have space for the data, stop now. */ - next_size = cdata->connected->client_outbuf_used + 4 + - xdr_getpos(reply_xdrs); - if (next_size > cdata->connected->client_outbuf_size) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "failed to queue stream reply (4+%d bytes)!\n", - xdr_getpos(reply_xdrs)); - return FALSE; - } - /* If we already have data in the buffer, and this would put us over - * the target size, then punt it until next time. */ - if ((cdata->connected->client_outbuf_used > 4) && - (next_size > DEFAULT_TARGET_REPLY_SIZE)) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "saving stream reply (4+%d bytes) for later\n", - xdr_getpos(reply_xdrs)); - return FALSE; - } - /* Calculate the fragment length bytes. */ - len = htonl(xdr_getpos(reply_xdrs) | (last_fragment ? 0x80000000 : 0)); - /* Queue the data. */ - memcpy(cdata->connected->client_outbuf + - cdata->connected->client_outbuf_used, - &len, 4); - memcpy(cdata->connected->client_outbuf + - cdata->connected->client_outbuf_used + 4, - cdata->connected->client_workbuf, - xdr_getpos(reply_xdrs)); - cdata->connected->client_outbuf_used += (4 + xdr_getpos(reply_xdrs)); - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "queued stream reply (4+%d bytes), %d total in queue\n", - xdr_getpos(reply_xdrs), - cdata->connected->client_outbuf_used); - return TRUE; -} -/* Send an entire reply record at once. */ -static void -dispatch_reply_connected(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, - XDR *reply_xdrs) -{ - dispatch_reply_fragment_connected(state, cdata, - reply, reply_xdrs, - TRUE, TRUE); -} - -/* Handle a datagram client -- read the request and handle it immediately. */ -static void -dispatch_dgram(struct plugin_state *state, int fd) -{ - struct dispatch_client_data cdata; - char dgram[65536]; - int reqsize; - - /* Read the request. */ - cdata.dgram.client_fd = fd; - cdata.dgram.client_addrlen = sizeof(cdata.dgram.client_addr); - cdata.dgram.reply_buf = malloc(state->max_dgram_size); - cdata.dgram.reply_buf_size = state->max_dgram_size; - if (cdata.dgram.reply_buf == NULL) { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "error reading datagram request: " - "out of memory\n"); - return; - } - reqsize = recvfrom(cdata.dgram.client_fd, dgram, sizeof(dgram), 0, - (struct sockaddr *) &cdata.dgram.client_addr, - &cdata.dgram.client_addrlen); - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "datagram request (%d bytes)\n", reqsize); -#ifdef HAVE_TCPD - if ((request_set(state->request_info, - RQ_CLIENT_SIN, &cdata.dgram.client_addr, - 0) == NULL) || - (fromhost(state->request_info), - hosts_access(state->request_info) == 0)) { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "datagram request denied by tcp_wrappers\n"); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "datagram request permitted by tcp_wrappers\n"); - } -#endif - if (dispatch_access(state, &cdata.dgram.client_addr) == 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "datagram request denied by securenets\n"); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "datagram request permitted by securenets\n"); - } - - switch (cdata.dgram.client_addr.ss_family) { - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - case AF_INET: - sin = (struct sockaddr_in *) &cdata.dgram.client_addr; - cdata.dgram.client_secure = ntohs(sin->sin_port) < 1024; - break; - case AF_INET6: - sin6 = (struct sockaddr_in6 *) &cdata.dgram.client_addr; - cdata.dgram.client_secure = ntohs(sin6->sin6_port) < 1024; - break; - default: - cdata.dgram.client_secure = FALSE; - break; - } - - /* Handle the request. */ - nis_process_request(state, dgram, reqsize, - &dispatch_reply_fragment_dgram, - &dispatch_reply_dgram, - &cdata, cdata.dgram.client_secure, - cdata.dgram.reply_buf, - cdata.dgram.reply_buf_size, - NULL); - free(cdata.dgram.reply_buf); -} - -/* Set the client's record up to start reading a new query. */ -static void -client_set_reading(struct plugin_state *state, struct dispatch_client *client) -{ - client->client_inbuf_used = 0; - free(client->client_query); - client->client_query = NULL; - client->client_query_size = 0; - client->client_outbuf_used = 0; - client->client_state = client_reading; -} - -/* Set the client's record up to be cleaned up. */ -static void -client_set_closing(struct plugin_state *state, struct dispatch_client *client) -{ - client->client_inbuf_used = 0; - free(client->client_query); - client->client_query = NULL; - client->client_query_size = 0; - free(client->client_outbuf); - client->client_outbuf = NULL; - client->client_outbuf_size = 0; - client->client_outbuf_used = 0; - free(client->client_workbuf); - client->client_workbuf = NULL; - client->client_workbuf_size = 0; - client->client_state = client_closing; -} - -/* Set the client's record up. */ -static struct dispatch_client * -dispatch_accept_client(struct plugin_state *state, int fd) -{ - struct dispatch_client *client; - int flags; - char *outbuf, *workbuf; - client = malloc(sizeof(*client)); - if (client == NULL) { - return NULL; - } - outbuf = malloc(state->max_dgram_size + state->max_value_size); - if (outbuf == NULL) { - free(client); - return NULL; - } - workbuf = malloc(state->max_dgram_size + state->max_value_size); - if (workbuf == NULL) { - free(outbuf); - free(client); - return NULL; - } - fd = accept(fd, (struct sockaddr *)&client->client_addr, - &client->client_addrlen); - if (fd == -1) { - free(workbuf); - free(outbuf); - free(client); - return NULL; - } - switch (client->client_addr.ss_family) { - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - case AF_INET: - sin = (struct sockaddr_in *) &client->client_addr; - client->client_secure = ntohs(sin->sin_port) < 1024; - break; - case AF_INET6: - sin6 = (struct sockaddr_in6 *) &client->client_addr; - client->client_secure = ntohs(sin6->sin6_port) < 1024; - break; - default: - client->client_secure = FALSE; - break; - } -#ifdef HAVE_TCPD_H - if ((request_set(state->request_info, - RQ_CLIENT_SIN, &client->client_addr, - 0) == NULL) || - (fromhost(state->request_info), - hosts_access(state->request_info) == 0)) { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "tcp_wrappers rejected client on %d\n", fd); - close(fd); - free(workbuf); - free(outbuf); - free(client); - return NULL; - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "tcp_wrappers allows client on %d\n", fd); - } -#endif - if (dispatch_access(state, &client->client_addr) == 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "securenets rejected client on %d\n", fd); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "securenets allows client on %d\n", fd); - } - flags = fcntl(fd, F_GETFL); - if ((flags & O_NONBLOCK) == 0) { - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - } - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "new connected client on %d\n", fd); - memset(client, 0, sizeof(*client)); - client->client_fd = fd; - client->client_outbuf = outbuf; - client->client_outbuf_size = state->max_dgram_size + - state->max_value_size; - client->client_workbuf = workbuf; - client->client_workbuf_size = state->max_dgram_size + - state->max_value_size; - client_set_reading(state, client); - return client; -} - -/* Decide what to do next. */ -static void -client_interpret_nis_result(struct plugin_state *state, - struct dispatch_client *client) -{ - if (client->client_query_cookie != NULL) { - client->client_state = client_replying_with_more; - } else { - if (client->client_outbuf_used > 0) { - client->client_state = client_replying_final; - } else { - client_set_reading(state, client); - } - } -} - -/* Handle reading state. */ -static void -client_read(struct plugin_state *state, struct dispatch_client *client) -{ - ssize_t count; - int32_t len, nlen; - int last; - char *query; - struct dispatch_client_data client_data; - /* Try to read some data. */ - count = read(client->client_fd, - client->client_inbuf + client->client_inbuf_used, - sizeof(client->client_inbuf) - client->client_inbuf_used); - if (count <= 0) { - if ((count != -1) || (errno != EAGAIN)) { - /* Disconnect the client. */ - if (count == 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "no more data from %d, " - "marking for closing\n", - client->client_fd); - } else { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error reading from %d, " - "marking for closing\n", - client->client_fd); - } - client_set_closing(state, client); - } - } else { - /* Record the data as added to the fragment buffer. */ - client->client_inbuf_used += count; - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "have %d bytes from %d\n", - client->client_inbuf_used, - client->client_fd); - /* Check if we've got a complete fragment. */ - if (client->client_inbuf_used < 4) { - /* We don't even have a length, so continue reading. */ - return; - } - /* Read the length of the first fragment in the buffer. */ - memcpy(&nlen, client->client_inbuf, 4); - len = ntohl(nlen); - last = ((len & 0x80000000) != 0); - len &= 0x7fffffff; - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "fragment is %d bytes long%s, " - "have %d bytes pending, on %d\n", - len, last ? " (last one)" : "", - client->client_inbuf_used, client->client_fd); - if ((len + 4) <= client->client_inbuf_used) { - /* Got at least one fragment! */ - nlen = len + client->client_query_size; - query = malloc(nlen); - if (query == NULL) { - /* Out of memory, we'll have to try again - * later. */ - return; - } - /* Copy any previously-received fragments and append - * this one. */ - if (client->client_query_size > 0) { - memcpy(query, - client->client_query, - client->client_query_size); - } - memcpy(query + client->client_query_size, - client->client_inbuf + 4, - len); - /* Save the new query-in-progress. */ - free(client->client_query); - client->client_query = query; - client->client_query_size = nlen; - /* Drop the fragment from the incoming - * buffer. */ - memmove(client->client_inbuf, - client->client_inbuf + (len + 4), - client->client_inbuf_used - (len + 4)); - client->client_inbuf_used -= (len + 4); - } - if (last) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "query is %d bytes long on %d\n", - client->client_query_size, - client->client_fd); - /* We have a complete query. Pass it on down. */ - memset(&client_data, 0, sizeof(client_data)); - client_data.connected = client; - nis_process_request(state, - client->client_query, - client->client_query_size, - &dispatch_reply_fragment_connected, - &dispatch_reply_connected, - &client_data, client->client_secure, - client->client_workbuf, - client->client_workbuf_size, - &client->client_query_cookie); - /* Decide what to do next. */ - client_interpret_nis_result(state, client); - } - } -} - -/* Handle replying states. */ -static void -client_write(struct plugin_state *state, struct dispatch_client *client) -{ - ssize_t count; - int32_t len; - struct dispatch_client_data client_data; - - /* Try to send some of the pending data. */ - len = client->client_outbuf_used; - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "attempting to send %d bytes to %d\n", - len, client->client_fd); - count = write(client->client_fd, client->client_outbuf, len); - if (count <= 0) { - if ((count != -1) || (errno != EAGAIN)) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "error sending %d bytes to %d\n", - client->client_outbuf_used, - client->client_fd); - /* Fail, disconnect because we're out of sync. */ - client_set_closing(state, client); - } - return; - } - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "sent %d bytes to %d\n", count, client->client_fd); - if (count == client->client_outbuf_used) { - /* There's no more data to send. */ - if (client->client_state == client_replying_final) { - /* Done. Go back to reading next time. */ - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "waiting for next query on %d\n", - client->client_fd); - client_set_reading(state, client); - } else { - /* More to send, so ask for more reply data. */ - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "fetching more data for %d\n", - client->client_fd); - client->client_outbuf_used = 0; - memset(&client_data, 0, sizeof(client_data)); - client_data.connected = client; - nis_process_request(state, - client->client_query, - client->client_query_size, - &dispatch_reply_fragment_connected, - &dispatch_reply_connected, - &client_data, client->client_secure, - client->client_workbuf, - client->client_workbuf_size, - &client->client_query_cookie); - client_interpret_nis_result(state, client); - } - } else { - /* Partial write, adjust outgoing buffer for next time. */ - memmove(client->client_outbuf, - client->client_outbuf + count, - client->client_outbuf_used - count); - client->client_outbuf_used -= count; - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "%d bytes to go for %d\n", - client->client_outbuf_used, - client->client_fd); - } -} - -static void -dispatch_service_client(struct plugin_state *state, - struct dispatch_client *client, - struct pollfd *fd) -{ - switch (client->client_state) { - case client_reading: - if (fd->revents & POLLIN) { - client_read(state, client); - } else { - client_set_closing(state, client); - } - break; - case client_replying_with_more: - case client_replying_final: - if (fd->revents & POLLOUT) { - client_write(state, client); - } else { - client_set_closing(state, client); - } - break; - case client_closing: - case client_invalid: - /* never reached */ - assert(0); - break; - } -} - -void * -dispatch_thread(struct wrapped_thread *t) -{ - struct dispatch_client *clients, *client, *next, **list; - struct plugin_state *state = wrap_thread_arg(t); - struct pollfd *fds; - int i, n_fds, client_count; - bool_t stop; - - clients = NULL; - client_count = 0; - fds = NULL; - - for (stop = FALSE; !stop;) { - /* Prune out recently-disconnected clients. */ - list = &clients; - while (*list != NULL) { - client = *list; - next = client->client_next; - if (client->client_state == client_closing) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "pruning client %d\n", - client->client_fd); - if (client->client_fd != -1) { - close(client->client_fd); - } - free(client); - *list = next; - } else { - list = &(client->client_next); - } - } - /* Count the number of connected clients we have. */ - client = clients; - i = 0; - while (client != NULL) { - next = client->client_next; - client = next; - i++; - } - /* If the "fds" block isn't big enough (or doesn't exist yet), - * reallocate it. */ - if (i > client_count) { - free(fds); - fds = NULL; - client_count = i; - } - if (fds == NULL) { - fds = malloc((state->n_listeners + client_count + 1) * - sizeof(fds[0])); - if (fds == NULL) { - /* Wait a bit, then try again? */ - poll(NULL, 0, 10000); - continue; - } - } - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "%d connected clients\n", i); - - /* Fill in the set of polling descriptors. */ - memset(fds, 0, - sizeof((state->n_listeners + client_count + 1) * - sizeof(fds[0]))); - for (i = 0; i < state->n_listeners; i++) { - fds[i].fd = state->listener[i].fd; - fds[i].events = POLLIN; - } - - /* Add the shutdown pipe reader. */ - fds[i].fd = wrap_thread_stopfd(t); - fds[i].events = POLLIN; - if (fds[i].fd != -1) { - i++; - } - - /* Add the client list. */ - client = clients; - while (client != NULL) { - fds[i].fd = client->client_fd; - switch (client->client_state) { - case client_reading: - fds[i].events = POLLIN; - break; - case client_replying_with_more: - case client_replying_final: - fds[i].events = POLLOUT; - break; - case client_closing: - case client_invalid: - /* shouldn't happen */ - assert(0); - break; - } - client = client->client_next; - i++; - } - - /* Check for status updates. */ - slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, - "listening\n"); - n_fds = i; - switch (poll(fds, n_fds, -1)) { - case -1: - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "done waiting\n"); - return NULL; - break; - case 0: - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "no request(timeout?)\n"); - continue; - default: - break; - } - - /* Save the head of the existing clients list. */ - client = clients; - - /* Iterate over listening sockets which have work for us. */ - for (i = 0; i < state->n_listeners; i++) { - if ((fds[i].revents & POLLIN) == 0) { - continue; - } - switch (state->listener[i].type) { - case SOCK_DGRAM: - /* Datagram requests we handle right - * here, right now. */ - dispatch_dgram(state, fds[i].fd); - break; - case SOCK_STREAM: - /* Try to accept a new client. */ - next = dispatch_accept_client(state, fds[i].fd); - if (next != NULL) { - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "new client on %d\n", - next->client_fd); - next->client_next = clients; - clients = next; - } - break; - default: - /* never reached */ - assert(0); - break; - } - } - - /* Add the shutdown pipe reader. */ - if (fds[i].revents & POLLIN) { - stop = TRUE; - } - i++; - - /* Service the already-connected clients. */ - for (; i < n_fds; i++, client = client->client_next) { - assert(client != NULL); - assert(client->client_fd == fds[i].fd); - dispatch_service_client(state, client, &fds[i]); - } - } - slapi_log_error(SLAPI_LOG_PLUGIN, - state->plugin_desc->spd_id, - "listening thread stopping\n"); - return state; -} diff --git a/src/dispatch.h b/src/dispatch.h deleted file mode 100644 index ca94404..0000000 --- a/src/dispatch.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2008 Red Hat, Inc. - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This Program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; if not, write to the - * - * Free Software Foundation, Inc. - * 59 Temple Place, Suite 330 - * Boston, MA 02111-1307 USA - * - */ - -#ifndef dispatch_h -#define dispatch_h -struct plugin_state; -struct wrapped_thread; -struct dispatch_client_data; -void *dispatch_thread(struct wrapped_thread *t); -typedef bool_t (dispatch_reply_fragment)(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, XDR *reply_xdrs, - bool_t first_fragment, - bool_t last_fragment); -typedef void (dispatch_reply)(struct plugin_state *state, - struct dispatch_client_data *cdata, - struct rpc_msg *reply, XDR *reply_xdrs); -void dispatch_securenets_clear(struct plugin_state *state); -void dispatch_securenets_add(struct plugin_state *state, const char *value); -#endif diff --git a/src/dummymap.c b/src/dummymap.c index 009f3ae..9eb0c0a 100644 --- a/src/dummymap.c +++ b/src/dummymap.c @@ -39,7 +39,7 @@ #include -#include "dispatch.h" +#include "disp-nis.h" #include "map.h" #include "nis.h" #include "plug-nis.h" diff --git a/src/map.c b/src/map.c index 74028c8..5494886 100644 --- a/src/map.c +++ b/src/map.c @@ -41,7 +41,7 @@ #include #include "backend.h" -#include "dispatch.h" +#include "disp-nis.h" #include "map.h" #include "portmap.h" #include "wrap.h" diff --git a/src/nis.c b/src/nis.c index 2a4d9f3..4c69f41 100644 --- a/src/nis.c +++ b/src/nis.c @@ -44,7 +44,7 @@ #include #include -#include "dispatch.h" +#include "disp-nis.h" #include "map.h" #include "nis.h" #include "plug-nis.h" diff --git a/src/nis.h b/src/nis.h index 561bd4a..1d89e0e 100644 --- a/src/nis.h +++ b/src/nis.h @@ -22,7 +22,7 @@ #ifndef nis_h #define nis_h -#include "dispatch.h" +#include "disp-nis.h" struct plugin_state; struct dispatch_client_data; diff --git a/src/plug-nis.c b/src/plug-nis.c index 0d9fbdf..55c8b48 100644 --- a/src/plug-nis.c +++ b/src/plug-nis.c @@ -53,7 +53,7 @@ #endif #include "wrap.h" -#include "dispatch.h" +#include "disp-nis.h" #include "map.h" #include "nis.h" #include "plug-nis.h" -- cgit