summaryrefslogtreecommitdiff
path: root/src/tests/gss-threads/gss-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/gss-threads/gss-server.c')
-rw-r--r--src/tests/gss-threads/gss-server.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/src/tests/gss-threads/gss-server.c b/src/tests/gss-threads/gss-server.c
new file mode 100644
index 000000000000..9bae19643844
--- /dev/null
+++ b/src/tests/gss-threads/gss-server.c
@@ -0,0 +1,853 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Copyright (C) 2004,2008 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "autoconf.h"
+#include <stdio.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <gssapi/gssapi_generic.h>
+#include "gss-misc.h"
+#include "port-sockets.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: gss-server [-port port] [-verbose] [-once]");
+#ifdef _WIN32
+ fprintf(stderr, " [-threads num]");
+#endif
+ fprintf(stderr, "\n");
+ fprintf(stderr, " [-inetd] [-export] [-logfile file] "
+ "service_name\n");
+ exit(1);
+}
+
+FILE *logfile;
+
+int verbose = 0;
+
+/*
+ * Function: server_acquire_creds
+ *
+ * Purpose: imports a service name and acquires credentials for it
+ *
+ * Arguments:
+ *
+ * service_name (r) the ASCII service name
+ * server_creds (w) the GSS-API service credentials
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Effects:
+ *
+ * The service name is imported with gss_import_name, and service
+ * credentials are acquired with gss_acquire_cred. If either opertion
+ * fails, an error message is displayed and -1 is returned; otherwise,
+ * 0 is returned.
+ */
+static int
+server_acquire_creds(char *service_name, gss_cred_id_t *server_creds)
+{
+ gss_buffer_desc name_buf;
+ gss_name_t server_name;
+ OM_uint32 maj_stat, min_stat;
+
+ name_buf.value = service_name;
+ name_buf.length = strlen(name_buf.value) + 1;
+ maj_stat = gss_import_name(&min_stat, &name_buf,
+ (gss_OID)gss_nt_service_name, &server_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("importing name", maj_stat, min_stat);
+ return -1;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+ server_creds, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("acquiring credentials", maj_stat, min_stat);
+ return -1;
+ }
+
+ (void)gss_release_name(&min_stat, &server_name);
+
+ return 0;
+}
+
+/*
+ * Function: server_establish_context
+ *
+ * Purpose: establishses a GSS-API context as a specified service with
+ * an incoming client, and returns the context handle and associated
+ * client name
+ *
+ * Arguments:
+ *
+ * s (r) an established TCP connection to the client
+ * service_creds (r) server credentials, from gss_acquire_cred
+ * context (w) the established GSS-API context
+ * client_name (w) the client's ASCII name
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Effects:
+ *
+ * Any valid client request is accepted. If a context is established,
+ * its handle is returned in context and the client name is returned
+ * in client_name and 0 is returned. If unsuccessful, an error
+ * message is displayed and -1 is returned.
+ */
+static int
+server_establish_context(int s, gss_cred_id_t server_creds,
+ gss_ctx_id_t *context, gss_buffer_t client_name,
+ OM_uint32 *ret_flags)
+{
+ gss_buffer_desc send_tok, recv_tok, oid_name;
+ gss_name_t client;
+ gss_OID doid;
+ OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
+ int token_flags;
+
+ if (recv_token(s, &token_flags, &recv_tok) < 0)
+ return -1;
+
+ if (recv_tok.value) {
+ free(recv_tok.value);
+ recv_tok.value = NULL;
+ }
+
+ if (!(token_flags & TOKEN_NOOP)) {
+ if (logfile) {
+ fprintf(logfile, "Expected NOOP token, got %d token instead\n",
+ token_flags);
+ }
+ return -1;
+ }
+
+ *context = GSS_C_NO_CONTEXT;
+
+ if (token_flags & TOKEN_CONTEXT_NEXT) {
+ do {
+ if (recv_token(s, &token_flags, &recv_tok) < 0)
+ return -1;
+
+ if (verbose && logfile) {
+ fprintf(logfile, "Received token (size=%d): \n",
+ (int)recv_tok.length);
+ print_token(&recv_tok);
+ }
+
+ maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,
+ server_creds, &recv_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client, &doid, &send_tok,
+ ret_flags, NULL, NULL);
+
+ if (recv_tok.value) {
+ free(recv_tok.value);
+ recv_tok.value = NULL;
+ }
+
+ if (send_tok.length != 0) {
+ if (verbose && logfile) {
+ fprintf(logfile,
+ "Sending accept_sec_context token (size=%d):\n",
+ (int)send_tok.length);
+ print_token(&send_tok);
+ }
+ if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {
+ if (logfile)
+ fprintf(logfile, "failure sending token\n");
+ return -1;
+ }
+
+ (void)gss_release_buffer(&min_stat, &send_tok);
+ }
+ if (maj_stat != GSS_S_COMPLETE &&
+ maj_stat != GSS_S_CONTINUE_NEEDED) {
+ display_status("accepting context", maj_stat,
+ acc_sec_min_stat);
+ if (*context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(&min_stat, context,
+ GSS_C_NO_BUFFER);
+ }
+ return -1;
+ }
+
+ if (verbose && logfile) {
+ if (maj_stat == GSS_S_CONTINUE_NEEDED)
+ fprintf(logfile, "continue needed...\n");
+ else
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ /* display the flags */
+ display_ctx_flags(*ret_flags);
+
+ if (verbose && logfile) {
+ maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("converting oid->string", maj_stat, min_stat);
+ return -1;
+ }
+ fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",
+ (int)oid_name.length, (char *)oid_name.value);
+ (void)gss_release_buffer(&min_stat, &oid_name);
+ }
+
+ maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("displaying name", maj_stat, min_stat);
+ return -1;
+ }
+ maj_stat = gss_release_name(&min_stat, &client);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("releasing name", maj_stat, min_stat);
+ return -1;
+ }
+ } else {
+ client_name->length = *ret_flags = 0;
+
+ if (logfile)
+ fprintf(logfile, "Accepted unauthenticated connection.\n");
+ }
+
+ return 0;
+}
+
+/*
+ * Function: create_socket
+ *
+ * Purpose: Opens a listening TCP socket.
+ *
+ * Arguments:
+ *
+ * port (r) the port number on which to listen
+ *
+ * Returns: the listening socket file descriptor, or -1 on failure
+ *
+ * Effects:
+ *
+ * A listening socket on the specified port and created and returned.
+ * On error, an error message is displayed and -1 is returned.
+ */
+static int
+create_socket(u_short port)
+{
+ struct sockaddr_in saddr;
+ int s, on = 1;
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(port);
+ saddr.sin_addr.s_addr = INADDR_ANY;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("creating socket");
+ return -1;
+ }
+ /* Let the socket be reused right away. */
+ (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
+ if (bind(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ perror("binding socket");
+ (void)close(s);
+ return -1;
+ }
+ if (listen(s, 5) < 0) {
+ perror("listening on socket");
+ (void)close(s);
+ return -1;
+ }
+ return s;
+}
+
+static float
+timeval_subtract(struct timeval *tv1, struct timeval *tv2)
+{
+ return ((tv1->tv_sec - tv2->tv_sec) +
+ ((float)(tv1->tv_usec - tv2->tv_usec)) / 1000000);
+}
+
+/*
+ * Yes, yes, this isn't the best place for doing this test.
+ * DO NOT REMOVE THIS UNTIL A BETTER TEST HAS BEEN WRITTEN, THOUGH.
+ * -TYT
+ */
+static int
+test_import_export_context(gss_ctx_id_t *context)
+{
+ OM_uint32 min_stat, maj_stat;
+ gss_buffer_desc context_token, copied_token;
+ struct timeval tm1, tm2;
+
+ /* Attempt to save and then restore the context. */
+ gettimeofday(&tm1, (struct timezone *)0);
+ maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("exporting context", maj_stat, min_stat);
+ return 1;
+ }
+ gettimeofday(&tm2, NULL);
+ if (verbose && logfile) {
+ fprintf(logfile, "Exported context: %d bytes, %7.4f seconds\n",
+ (int)context_token.length, timeval_subtract(&tm2, &tm1));
+ }
+ copied_token.length = context_token.length;
+ copied_token.value = malloc(context_token.length);
+ if (copied_token.value == 0) {
+ if (logfile) {
+ fprintf(logfile, "Couldn't allocate memory to copy context "
+ "token.\n");
+ }
+ return 1;
+ }
+ memcpy(copied_token.value, context_token.value, copied_token.length);
+ maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("importing context", maj_stat, min_stat);
+ return 1;
+ }
+ free(copied_token.value);
+ gettimeofday(&tm1, NULL);
+ if (verbose && logfile) {
+ fprintf(logfile, "Importing context: %7.4f seconds\n",
+ timeval_subtract(&tm1, &tm2));
+ }
+ (void)gss_release_buffer(&min_stat, &context_token);
+ return 0;
+}
+
+/*
+ * Function: sign_server
+ *
+ * Purpose: Performs the "sign" service.
+ *
+ * Arguments:
+ *
+ * s (r) a TCP socket on which a connection has been
+ * accept()ed
+ * service_name (r) the ASCII name of the GSS-API service to
+ * establish a context as
+ * export (r) whether to test context exporting
+ *
+ * Returns: -1 on error
+ *
+ * Effects:
+ *
+ * sign_server establishes a context, and performs a single sign request.
+ *
+ * A sign request is a single GSS-API sealed token. The token is
+ * unsealed and a signature block, produced with gss_sign, is returned
+ * to the sender. The context is the destroyed and the connection
+ * closed.
+ *
+ * If any error occurs, -1 is returned.
+ */
+static int
+sign_server(int s, gss_cred_id_t server_creds, int export)
+{
+ gss_buffer_desc client_name, xmit_buf, msg_buf;
+ gss_ctx_id_t context;
+ OM_uint32 maj_stat, min_stat, ret_flags;
+ int i, conf_state, token_flags;
+ char *cp;
+
+ /* Establish a context with the client */
+ if (server_establish_context(s, server_creds, &context, &client_name,
+ &ret_flags) < 0)
+ return -1;
+
+ if (context == GSS_C_NO_CONTEXT) {
+ printf("Accepted unauthenticated connection.\n");
+ } else {
+ printf("Accepted connection: \"%.*s\"\n", (int)client_name.length,
+ (char *)client_name.value);
+ (void)gss_release_buffer(&min_stat, &client_name);
+
+ if (export) {
+ for (i = 0; i < 3; i++) {
+ if (test_import_export_context(&context))
+ return -1;
+ }
+ }
+ }
+
+ do {
+ /* Receive the message token */
+ if (recv_token(s, &token_flags, &xmit_buf) < 0)
+ return -1;
+
+ if (token_flags & TOKEN_NOOP) {
+ if (logfile)
+ fprintf(logfile, "NOOP token\n");
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ break;
+ }
+
+ if (verbose && logfile) {
+ fprintf(logfile, "Message token (flags=%d):\n", token_flags);
+ print_token(&xmit_buf);
+ }
+
+ if (context == GSS_C_NO_CONTEXT &&
+ (token_flags &
+ (TOKEN_WRAPPED | TOKEN_ENCRYPTED | TOKEN_SEND_MIC))) {
+ if (logfile) {
+ fprintf(logfile, "Unauthenticated client requested "
+ "authenticated services!\n");
+ }
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ return -1;
+ }
+
+ if (token_flags & TOKEN_WRAPPED) {
+ maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,
+ &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("unsealing message", maj_stat, min_stat);
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ return -1;
+ } else if (!conf_state && (token_flags & TOKEN_ENCRYPTED)) {
+ fprintf(stderr, "Warning! Message not encrypted.\n");
+ }
+
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ } else {
+ msg_buf = xmit_buf;
+ }
+
+ if (logfile) {
+ fprintf(logfile, "Received message: ");
+ cp = msg_buf.value;
+ if (isprint((unsigned char)cp[0]) &&
+ isprint((unsigned char)cp[1])) {
+ fprintf(logfile, "\"%.*s\"\n", (int)msg_buf.length,
+ (char *)msg_buf.value);
+ } else {
+ fprintf(logfile, "\n");
+ print_token(&msg_buf);
+ }
+ }
+
+ if (token_flags & TOKEN_SEND_MIC) {
+ /* Produce a signature block for the message. */
+ maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
+ &msg_buf, &xmit_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("signing message", maj_stat, min_stat);
+ return -1;
+ }
+
+ if (msg_buf.value) {
+ free(msg_buf.value);
+ msg_buf.value = 0;
+ }
+
+ /* Send the signature block to the client. */
+ if (send_token(s, TOKEN_MIC, &xmit_buf) < 0)
+ return -1;
+
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ } else {
+ if (msg_buf.value) {
+ free(msg_buf.value);
+ msg_buf.value = 0;
+ }
+ if (send_token(s, TOKEN_NOOP, empty_token) < 0)
+ return -1;
+ }
+ } while (1 /* loop will break if NOOP received */);
+
+ if (context != GSS_C_NO_CONTEXT) {
+ /* Delete context. */
+ maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("deleting context", maj_stat, min_stat);
+ return -1;
+ }
+ }
+
+ if (logfile)
+ fflush(logfile);
+
+ return 0;
+}
+
+static int max_threads = 1;
+
+#ifdef _WIN32
+static thread_count = 0;
+static HANDLE hMutex = NULL;
+static HANDLE hEvent = NULL;
+
+void
+init_handles(void)
+{
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+}
+
+void
+cleanup_handles(void)
+{
+ CloseHandle(hMutex);
+ CloseHandle(hEvent);
+}
+
+BOOL
+wait_and_increment_thread_counter(void)
+{
+ for (;;) {
+ if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
+ if (thread_count < max_threads) {
+ thread_count++;
+ ReleaseMutex(hMutex);
+ return TRUE;
+ } else {
+ ReleaseMutex(hMutex);
+
+ if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)
+ continue;
+ else
+ return FALSE;
+ }
+ } else {
+ return FALSE;
+ }
+ }
+}
+
+BOOL
+decrement_and_signal_thread_counter(void)
+{
+ if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {
+ if (thread_count == max_threads)
+ SetEvent(hEvent);
+ thread_count--;
+ ReleaseMutex(hMutex);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+#else /* assume pthread */
+
+static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER;
+int counter = 0;
+
+static int
+wait_and_increment_thread_counter(void)
+{
+ int err;
+
+ err = pthread_mutex_lock(&counter_mutex);
+ if (err) {
+ perror("pthread_mutex_lock");
+ return 0;
+ }
+ if (counter == max_threads) {
+ err = pthread_cond_wait(&counter_cond, &counter_mutex);
+ if (err) {
+ pthread_mutex_unlock(&counter_mutex);
+ perror("pthread_cond_wait");
+ return 0;
+ }
+ }
+ counter++;
+ pthread_mutex_unlock(&counter_mutex);
+ return 1;
+}
+
+static void
+decrement_and_signal_thread_counter(void)
+{
+ int err;
+
+ err = pthread_mutex_lock(&counter_mutex);
+ if (err) {
+ perror("pthread_mutex_lock");
+ return;
+ }
+ if (counter == max_threads)
+ pthread_cond_broadcast(&counter_cond);
+ counter--;
+ pthread_mutex_unlock(&counter_mutex);
+}
+
+#endif
+
+struct _work_plan {
+ int s;
+ gss_cred_id_t server_creds;
+ int export;
+};
+
+static void *
+worker_bee(void *param)
+{
+ struct _work_plan *work = param;
+
+ /* This return value is not checked, because there's not really anything to
+ * do if it fails. */
+ sign_server(work->s, work->server_creds, work->export);
+ closesocket(work->s);
+ free(work);
+
+#if defined _WIN32 || 1
+ if (max_threads > 1)
+ decrement_and_signal_thread_counter();
+#endif
+ return NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *service_name;
+ gss_cred_id_t server_creds;
+ OM_uint32 min_stat;
+ u_short port = 4444;
+ int once = 0;
+ int do_inetd = 0;
+ int export = 0;
+
+ signal(SIGPIPE, SIG_IGN);
+ logfile = stdout;
+ display_file = stdout;
+ argc--;
+ argv++;
+ while (argc) {
+ if (strcmp(*argv, "-port") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ port = atoi(*argv);
+ } else if (strcmp(*argv, "-threads") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ max_threads = atoi(*argv);
+ } else if (strcmp(*argv, "-verbose") == 0) {
+ verbose = 1;
+ } else if (strcmp(*argv, "-once") == 0) {
+ once = 1;
+ } else if (strcmp(*argv, "-inetd") == 0) {
+ do_inetd = 1;
+ } else if (strcmp(*argv, "-export") == 0) {
+ export = 1;
+ } else if (strcmp(*argv, "-logfile") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ /*
+ * Gross hack, but it makes it unnecessary to add an extra argument
+ * to disable logging, and makes the code more efficient because it
+ * doesn't actually write data to /dev/null.
+ */
+ if (!strcmp(*argv, "/dev/null")) {
+ logfile = display_file = NULL;
+ } else {
+ logfile = fopen(*argv, "a");
+ display_file = logfile;
+ if (!logfile) {
+ perror(*argv);
+ exit(1);
+ }
+ }
+ } else {
+ break;
+ }
+ argc--;
+ argv++;
+ }
+ if (argc != 1)
+ usage();
+
+ if ((*argv)[0] == '-')
+ usage();
+
+#ifdef _WIN32
+ if (max_threads < 1) {
+ fprintf(stderr, "warning: there must be at least one thread\n");
+ max_threads = 1;
+ }
+
+ if (max_threads > 1 && do_inetd) {
+ fprintf(stderr, "warning: one thread may be used in conjunction "
+ "with inetd\n");
+ }
+
+ init_handles();
+#endif
+
+ service_name = *argv;
+
+ if (server_acquire_creds(service_name, &server_creds) < 0)
+ return -1;
+
+ if (do_inetd) {
+ close(1);
+ close(2);
+
+ sign_server(0, server_creds, export);
+ close(0);
+ } else {
+ int stmp;
+
+ stmp = create_socket(port);
+ if (stmp >= 0) {
+ if (listen(stmp, max_threads == 1 ? 0 : max_threads) < 0)
+ perror("listening on socket");
+
+ do {
+ struct _work_plan * work = malloc(sizeof(struct _work_plan));
+
+ if (work == NULL) {
+ fprintf(stderr, "fatal error: out of memory");
+ break;
+ }
+
+ /* Accept a TCP connection */
+ work->s = accept(stmp, NULL, 0);
+ if (work->s < 0) {
+ perror("accepting connection");
+ continue;
+ }
+
+ work->server_creds = server_creds;
+ work->export = export;
+
+ if (max_threads == 1) {
+ worker_bee(work);
+ } else {
+ if (wait_and_increment_thread_counter()) {
+#ifdef _WIN32
+ uintptr_t handle = _beginthread(worker_bee, 0, work);
+ if (handle == (uintptr_t)-1) {
+ closesocket(work->s);
+ free(work);
+ }
+#else
+ int err;
+ pthread_t thr;
+ err = pthread_create(&thr, 0, worker_bee, work);
+ if (err) {
+ perror("pthread_create");
+ closesocket(work->s);
+ free(work);
+ }
+ (void)pthread_detach(thr);
+#endif
+ } else {
+ fprintf(stderr, "fatal error incrementing thread "
+ "counter");
+ closesocket(work->s);
+ free(work);
+ break;
+ }
+ }
+ } while (!once);
+
+ closesocket(stmp);
+ }
+ }
+
+ (void)gss_release_cred(&min_stat, &server_creds);
+
+#ifdef _WIN32
+ cleanup_handles();
+#else
+ if (max_threads > 1) {
+ while (1)
+ sleep(999999);
+ }
+#endif
+
+ return 0;
+}