#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 "nis.h" #include "plugin.h" #include "portmap.h" #include "schema.h" #include "stream.h" #define PACKAGE_VERSION "0.0" /* the module initialization function */ static Slapi_PluginDesc plugin_description = { .spd_id = "nis-plugin", .spd_vendor = "hamdingers.org", .spd_version = PACKAGE_VERSION, .spd_description = "NIS Service Plugin", }; struct search_stream_data { struct plugin_state *state; int client; }; static void cb_stream_result(int rc, void *callback_data) { struct search_stream_data *data = callback_data; char buf[sizeof(rc) * 4 + 1]; slapi_log_error(SLAPI_LOG_PLUGIN, "cb_result", "returning result %d\n", rc); sprintf(buf, "%d\n", rc); write(data->client, buf, strlen(buf)); } static int cb_stream_entry(Slapi_Entry *e, void *callback_data) { struct search_stream_data *data = callback_data; Slapi_Attr *attr; Slapi_ValueSet *values; Slapi_Value *value; const char *dn; const struct berval *val; int i; attr = NULL; if (slapi_entry_attr_find(e, "ou", &attr) == 0) { dn = slapi_entry_get_dn(e); slapi_log_error(SLAPI_LOG_PLUGIN, "cb_entry", "returning entry \"%s\"\n", dn); write(data->client, "[", 1); write(data->client, dn, strlen(dn)); write(data->client, "]\n", 2); values = NULL; if (slapi_attr_get_valueset(attr, &values) == 0) { i = slapi_valueset_first_value(values, &value); while (i != -1) { val = slapi_value_get_berval(value); if (val != NULL) { write(data->client, val->bv_val, val->bv_len); write(data->client, "\n", 1); } i = slapi_valueset_next_value(values, i, &value); } } } return 0; } static int cb_stream_referral(char *referral, void *callback_data) { struct search_stream_data *data = callback_data; slapi_log_error(SLAPI_LOG_PLUGIN, "cb_referral", "returning referral to \"%s\"\n", referral); write(data->client, "See also ", 9); write(data->client, referral, strlen(referral)); write(data->client, "\n", 1); return 0; } #if 0 pblock = slapi_pblock_new(); slapi_pblock_set(pblock, SLAPI_CONN_DN, ""); slapi_pblock_set(pblock, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE); slapi_search_internal_set_pb(pblock, "dc=boston,dc=redhat,dc=com", LDAP_SCOPE_ONE, "(objectclass=*)", attrs, 0, NULL, NULL, state->plugin_identity, 0); i = slapi_search_internal_callback_pb(pblock, &data, cb_stream_result, cb_stream_entry, cb_stream_referral); slapi_pblock_destroy(pblock); close(client); #endif 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) { 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"); /* Register the listener sockets with the portmapper. */ if (state->resvport != -1) { for (i = 0; i < state->n_listeners; i++) { switch (state->sock_type[i]) { case SOCK_DGRAM: protocol = IPPROTO_UDP; pname = "UDP"; break; case SOCK_STREAM: protocol = IPPROTO_TCP; pname = "TCP"; break; default: protocol = IPPROTO_IP; pname = "IP"; break; } if (protocol == IPPROTO_IP) { continue; } if (!portmap_register(state->resvport, YPPROG, YPVERS, protocol, state->listenport[i])) { 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, &listener_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_close(Slapi_PBlock *pb) { struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); if (state->resvport != -1) { /* Clear our registration with the portmapper. */ portmap_unregister(state->resvport, YPPROG, YPVERS); close(state->resvport); } return 0; } static int setup_listener_sockets(struct plugin_state **lstate) { int sockfd = -1, err, i; struct plugin_state *state; PLArenaPool *arena = NULL; arena = PORT_NewArena(sizeof(double)); if (arena == NULL) { goto failed; } state = PORT_ArenaZAlloc(arena, sizeof(*state)); if (state == NULL) { goto failed; } 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) { 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; } /* Privileged port, datagram and stream, IPv4 and IPv6. */ 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; 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; } sockfd = socket(sock_pf, sock_type, 0); if (sockfd == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error creating a %s socket\n", sock_desc); continue; } 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\n", sock_desc); } if (bindresvport(sockfd, (struct sockaddr_in *) &ss) != 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; } 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; } if (i == 0) { state->resvport = 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; 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" : ""); state->n_listeners++; } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "set up %d listening sockets\n", state->n_listeners); *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; if (setup_listener_sockets(&state) == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error setting up listening sockets\n"); return -1; } 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_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; }