#ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dispatch.h" #include "map.h" #include "nis.h" #include "plugin.h" #include "portmap.h" #define PACKAGE_VERSION "0.0" /* the module initialization function */ static Slapi_PluginDesc plugin_description = { .spd_id = "nis-plugin", .spd_vendor = "badvocacy.net", .spd_version = PACKAGE_VERSION, .spd_description = "NIS Server Plugin", }; /* Start the plugin's work thread. */ static int 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_description.spd_id, "plugin starting\n"); /* Register the listener sockets with the portmapper. */ if (state->pmap_client_socket != -1) { for (i = 0; i < state->n_listeners; i++) { switch (state->listener[i].type) { case SOCK_DGRAM: protocol = IPPROTO_UDP; pname = "UDP"; break; case SOCK_STREAM: protocol = IPPROTO_TCP; pname = "TCP"; break; default: /* never reached */ assert(0); break; } if (protocol == IPPROTO_IP) { continue; } if (!portmap_register(plugin_description.spd_id, state->pmap_client_socket, YPPROG, YPVERS, protocol, state->listener[i].port)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error registering %s service " "with portmap\n", pname); } } } /* Start a new listening thread to handle incoming traffic. FIXME: * switch to using NSPR's threading facilities. */ 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"); return -1; } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "plugin startup completed\n"); return 0; } /* Stop the plugin's work thread. */ static int plugin_shutdown(Slapi_PBlock *pb) { struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); if (state->pmap_client_socket != -1) { /* Clear our registration with the portmapper. */ portmap_unregister(plugin_description.spd_id, 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 plugin_state_init(struct plugin_state **lstate) { int sockfd = -1, err, i; struct plugin_state *state = NULL; PLArenaPool *arena = NULL; struct sockaddr_in sin; struct sockaddr_in6 sin6; arena = PORT_NewArena(sizeof(double)); if (arena == NULL) { goto failed; } state = PORT_ArenaZAlloc(arena, sizeof(*state)); if (state == NULL) { goto failed; } state->arena = arena; /* Create a socket for use in communicating with the portmapper. */ sockfd = socket(PF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { goto failed; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (bindresvport(sockfd, &sin) != 0) { close(sockfd); goto failed; } state->pmap_client_socket = sockfd; /* 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 < 2; i++) { int pf, type, one, port, ret; const char *sock_desc; /* 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; } /* 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, "error creating a %s socket\n", sock_desc); continue; } /* Mark the socket as reusable. */ one = 1; if (setsockopt(sockfd, IPPROTO_IP, SO_REUSEADDR, &one, sizeof(one)) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error marking %s socket for reuse, " "continuing\n", sock_desc); } /* Bind to a reserved port. */ memset(&sin, 0, sizeof(sin)); memset(&sin6, 0, sizeof(sin6)); sin.sin_family = AF_INET; sin6.sin6_family = AF_INET6; ret = (pf == PF_INET6) ? bindresvport(sockfd, (struct sockaddr_in*) &sin6) : bindresvport(sockfd, &sin); if (ret != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error binding %s socket to a " "privileged port: %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 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 clients\n", state->listener[state->n_listeners].port, sock_desc); state->n_listeners++; } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "set up %d listening sockets\n", state->n_listeners); map_init(state); slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "initialized maps\n"); *lstate = state; return 0; failed: err = errno; if (arena != NULL) { PORT_FreeArena(arena, PR_TRUE); } errno = err; return -1; } int nis_plugin_init(Slapi_PBlock *pb) { struct plugin_state *state = NULL; /* 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 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_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); slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "registered plugin hooks\n"); return 0; }