Path: blob/main/crypto/krb5/src/tests/gss-threads/gss-client.c
34914 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 1994 by OpenVision Technologies, Inc.3*4* Permission to use, copy, modify, distribute, and sell this software5* and its documentation for any purpose is hereby granted without fee,6* provided that the above copyright notice appears in all copies and7* that both that copyright notice and this permission notice appear in8* supporting documentation, and that the name of OpenVision not be used9* in advertising or publicity pertaining to distribution of the software10* without specific, written prior permission. OpenVision makes no11* representations about the suitability of this software for any12* purpose. It is provided "as is" without express or implied warranty.13*14* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,15* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO16* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR17* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF18* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR19* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR20* PERFORMANCE OF THIS SOFTWARE.21*/22/*23* Copyright (C) 2003, 2004, 2008 by the Massachusetts Institute of Technology.24* All rights reserved.25*26* Export of this software from the United States of America may27* require a specific license from the United States Government.28* It is the responsibility of any person or organization contemplating29* export to obtain such a license before exporting.30*31* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and32* distribute this software and its documentation for any purpose and33* without fee is hereby granted, provided that the above copyright34* notice appear in all copies and that both that copyright notice and35* this permission notice appear in supporting documentation, and that36* the name of M.I.T. not be used in advertising or publicity pertaining37* to distribution of the software without specific, written prior38* permission. Furthermore if you modify this software you must label39* your software as modified software and not distribute it in such a40* fashion that it might be confused with the original M.I.T. software.41* M.I.T. makes no representations about the suitability of42* this software for any purpose. It is provided "as is" without express43* or implied warranty.44*/4546#include "k5-platform.h"47#ifdef _WIN3248#include <windows.h>49#include <winsock.h>50#else51#include <unistd.h>52#include <ctype.h>53#include <sys/types.h>54#include <sys/socket.h>55#include <netinet/in.h>56#include <netdb.h>57#include <sys/stat.h>58#include <fcntl.h>59#include <pthread.h>60#endif6162#include <gssapi/gssapi_generic.h>63#include "gss-misc.h"64#include "port-sockets.h"65#include "fake-addrinfo.h"6667static int verbose = 1;6869static void70usage(void)71{72fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n");73fprintf(stderr, " [-seq] [-noreplay] [-nomutual]");74fprintf(stderr, " [-threads num]");75fprintf(stderr, "\n");76fprintf(stderr, " [-f] [-q] [-ccount count] [-mcount count]\n");77fprintf(stderr, " [-v1] [-na] [-nw] [-nx] [-nm] host service msg\n");78exit(1);79}8081/*82* Function: get_server_info83*84* Purpose: Sets up a socket address for the named host and port.85*86* Arguments:87*88* host (r) the target host name89* port (r) the target port, in host byte order90*91* Returns: 0 on success, or -1 on failure92*93* Effects:94*95* The host name is resolved with gethostbyname(), and "saddr" is set96* to the desired socket address. If an error occurs, an error97* message is displayed and -1 is returned.98*/99struct sockaddr_in saddr;100static int101get_server_info(char *host, u_short port)102{103struct hostent *hp;104105hp = gethostbyname(host);106if (hp == NULL) {107fprintf(stderr, "Unknown host: %s\n", host);108return -1;109}110111saddr.sin_family = hp->h_addrtype;112memcpy(&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));113saddr.sin_port = htons(port);114return 0;115}116117/*118* Function: connect_to_server119*120* Purpose: Opens a TCP connection to the name host and port.121*122* Arguments:123*124* host (r) the target host name125* port (r) the target port, in host byte order126*127* Returns: the established socket file descriptor, or -1 on failure128*129* Effects:130*131* The host name is resolved with gethostbyname(), and the socket is132* opened and connected. If an error occurs, an error message is133* displayed and -1 is returned.134*/135static int136connect_to_server(void)137{138int s;139140s = socket(AF_INET, SOCK_STREAM, 0);141if (s < 0) {142perror("creating socket");143return -1;144}145if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {146perror("connecting to server");147(void)closesocket(s);148return -1;149}150return s;151}152153/*154* Function: client_establish_context155*156* Purpose: establishes a GSS-API context with a specified service and157* returns the context handle158*159* Arguments:160*161* s (r) an established TCP connection to the service162* service_name (r) the ASCII service name of the service163* gss_flags (r) GSS-API delegation flag (if any)164* auth_flag (r) whether to actually do authentication165* v1_format (r) whether the v1 sample protocol should be used166* oid (r) OID of the mechanism to use167* context (w) the established GSS-API context168* ret_flags (w) the returned flags from init_sec_context169*170* Returns: 0 on success, -1 on failure171*172* Effects:173*174* service_name is imported as a GSS-API name and a GSS-API context is175* established with the corresponding service; the service should be176* listening on the TCP connection s. The default GSS-API mechanism177* is used, and mutual authentication and replay detection are178* requested.179*180* If successful, the context handle is returned in context. If181* unsuccessful, the GSS-API error messages are displayed on stderr182* and -1 is returned.183*/184static int185client_establish_context(int s, char *service_name, OM_uint32 gss_flags,186int auth_flag, int v1_format, gss_OID oid,187gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)188{189if (auth_flag) {190gss_buffer_desc send_tok, recv_tok, *token_ptr;191gss_name_t target_name;192OM_uint32 maj_stat, min_stat, init_sec_min_stat;193int token_flags;194195/*196* Import the name into target_name. Use send_tok to save197* local variable space.198*/199send_tok.value = service_name;200send_tok.length = strlen(service_name);201maj_stat = gss_import_name(&min_stat, &send_tok,202(gss_OID)gss_nt_service_name, &target_name);203if (maj_stat != GSS_S_COMPLETE) {204display_status("parsing name", maj_stat, min_stat);205return -1;206}207208if (!v1_format) {209if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT,210empty_token) < 0) {211(void)gss_release_name(&min_stat, &target_name);212return -1;213}214}215216/*217* Perform the context-establishement loop.218*219* On each pass through the loop, token_ptr points to the token220* to send to the server (or GSS_C_NO_BUFFER on the first pass).221* Every generated token is stored in send_tok which is then222* transmitted to the server; every received token is stored in223* recv_tok, which token_ptr is then set to, to be processed by224* the next call to gss_init_sec_context.225*226* GSS-API guarantees that send_tok's length will be non-zero227* if and only if the server is expecting another token from us,228* and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if229* and only if the server has another token to send us.230*/231232token_ptr = GSS_C_NO_BUFFER;233*gss_context = GSS_C_NO_CONTEXT;234235do {236maj_stat = gss_init_sec_context(&init_sec_min_stat,237GSS_C_NO_CREDENTIAL, gss_context,238target_name, oid, gss_flags, 0,239NULL, token_ptr, NULL, &send_tok,240ret_flags, NULL);241242if (token_ptr != GSS_C_NO_BUFFER)243free(recv_tok.value);244245if (send_tok.length != 0) {246if (verbose) {247printf("Sending init_sec_context token (size=%d)...",248(int)send_tok.length);249}250if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT,251&send_tok) < 0) {252(void)gss_release_buffer(&min_stat, &send_tok);253(void)gss_release_name(&min_stat, &target_name);254if (*gss_context != GSS_C_NO_CONTEXT) {255gss_delete_sec_context(&min_stat, gss_context,256GSS_C_NO_BUFFER);257*gss_context = GSS_C_NO_CONTEXT;258}259return -1;260}261}262(void)gss_release_buffer(&min_stat, &send_tok);263264if (maj_stat != GSS_S_COMPLETE &&265maj_stat != GSS_S_CONTINUE_NEEDED) {266display_status("initializing context", maj_stat,267init_sec_min_stat);268(void)gss_release_name(&min_stat, &target_name);269if (*gss_context != GSS_C_NO_CONTEXT) {270gss_delete_sec_context(&min_stat, gss_context,271GSS_C_NO_BUFFER);272}273return -1;274}275276if (maj_stat == GSS_S_CONTINUE_NEEDED) {277if (verbose)278printf("continue needed...");279if (recv_token(s, &token_flags, &recv_tok) < 0) {280(void)gss_release_name(&min_stat, &target_name);281return -1;282}283token_ptr = &recv_tok;284}285if (verbose)286printf("\n");287} while (maj_stat == GSS_S_CONTINUE_NEEDED);288289(void)gss_release_name(&min_stat, &target_name);290} else if (send_token(s, TOKEN_NOOP, empty_token) < 0) {291return -1;292}293294return 0;295}296297static void298read_file(char *file_name, gss_buffer_t in_buf)299{300int fd, count;301struct stat stat_buf;302303fd = open(file_name, O_RDONLY, 0);304if (fd < 0) {305perror("open");306fprintf(stderr, "Couldn't open file %s\n", file_name);307exit(2);308}309if (fstat(fd, &stat_buf) < 0) {310perror("fstat");311exit(3);312}313in_buf->length = stat_buf.st_size;314315if (in_buf->length == 0) {316in_buf->value = NULL;317return;318}319320in_buf->value = malloc(in_buf->length);321if (in_buf->value == NULL) {322fprintf(stderr, "Couldn't allocate %d byte buffer for reading file\n",323(int)in_buf->length);324exit(4);325}326327/* This code used to check for incomplete reads, but you can't get328* an incomplete read on any file for which fstat() is meaningful. */329330count = read(fd, in_buf->value, in_buf->length);331if (count < 0) {332perror("read");333exit(5);334}335if ((size_t)count < in_buf->length) {336fprintf(stderr, "Warning, only read in %d bytes, expected %d\n",337count, (int)in_buf->length);338}339}340341/*342* Function: call_server343*344* Purpose: Call the "sign" service.345*346* Arguments:347*348* host (r) the host providing the service349* port (r) the port to connect to on host350* service_name (r) the GSS-API service name to authenticate to351* gss_flags (r) GSS-API delegation flag (if any)352* auth_flag (r) whether to do authentication353* wrap_flag (r) whether to do message wrapping at all354* encrypt_flag (r) whether to do encryption while wrapping355* mic_flag (r) whether to request a MIC from the server356* msg (r) the message to have "signed"357* use_file (r) whether to treat msg as an input file name358* mcount (r) the number of times to send the message359*360* Returns: 0 on success, -1 on failure361*362* Effects:363*364* call_server opens a TCP connection to <host:port> and establishes a365* GSS-API context with service_name over the connection. It then366* seals msg in a GSS-API token with gss_wrap, sends it to the server,367* reads back a GSS-API signature block for msg from the server, and368* verifies it with gss_verify. -1 is returned if any step fails,369* otherwise 0 is returned.370*/371static int372call_server(char *host, u_short port, gss_OID oid, char *service_name,373OM_uint32 gss_flags, int auth_flag, int wrap_flag,374int encrypt_flag, int mic_flag, int v1_format, char *msg,375int use_file, size_t mcount)376{377gss_ctx_id_t context;378gss_buffer_desc in_buf, out_buf, sname, tname, oid_name;379int s, state, is_local, is_open, flags, token_flags;380OM_uint32 ret_flags, maj_stat, min_stat, lifetime, context_flags;381gss_name_t src_name, targ_name;382gss_OID mechanism, name_type;383gss_qop_t qop_state;384gss_OID_set mech_names;385size_t i;386387/* Open connection. */388s = connect_to_server();389if (s < 0)390return -1;391392/* Establish context. */393if (client_establish_context(s, service_name, gss_flags, auth_flag,394v1_format, oid, &context, &ret_flags) < 0) {395(void)closesocket(s);396return -1;397}398399if (auth_flag && verbose) {400/* Display the flags. */401display_ctx_flags(ret_flags);402403/* Get context information. */404maj_stat = gss_inquire_context(&min_stat, context, &src_name,405&targ_name, &lifetime, &mechanism,406&context_flags, &is_local, &is_open);407if (maj_stat != GSS_S_COMPLETE) {408display_status("inquiring context", maj_stat, min_stat);409return -1;410}411412maj_stat = gss_display_name(&min_stat, src_name, &sname, &name_type);413if (maj_stat != GSS_S_COMPLETE) {414display_status("displaying source name", maj_stat, min_stat);415return -1;416}417maj_stat = gss_display_name(&min_stat, targ_name, &tname, NULL);418if (maj_stat != GSS_S_COMPLETE) {419display_status("displaying target name", maj_stat, min_stat);420return -1;421}422printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n",423(int)sname.length, (char *)sname.value,424(int)tname.length, (char *)tname.value, lifetime, context_flags,425is_local ? "locally initiated" : "remotely initiated",426is_open ? "open" : "closed");427428(void)gss_release_name(&min_stat, &src_name);429(void)gss_release_name(&min_stat, &targ_name);430(void)gss_release_buffer(&min_stat, &sname);431(void)gss_release_buffer(&min_stat, &tname);432433maj_stat = gss_oid_to_str(&min_stat, name_type, &oid_name);434if (maj_stat != GSS_S_COMPLETE) {435display_status("converting oid->string", maj_stat, min_stat);436return -1;437}438printf("Name type of source name is %.*s.\n", (int)oid_name.length,439(char *)oid_name.value);440(void)gss_release_buffer(&min_stat, &oid_name);441442/* Now get the names supported by the mechanism. */443maj_stat = gss_inquire_names_for_mech(&min_stat, mechanism,444&mech_names);445if (maj_stat != GSS_S_COMPLETE) {446display_status("inquiring mech names", maj_stat, min_stat);447return -1;448}449450maj_stat = gss_oid_to_str(&min_stat, mechanism, &oid_name);451if (maj_stat != GSS_S_COMPLETE) {452display_status("converting oid->string", maj_stat, min_stat);453return -1;454}455printf("Mechanism %.*s supports %d names\n", (int)oid_name.length,456(char *)oid_name.value, (int)mech_names->count);457(void)gss_release_buffer(&min_stat, &oid_name);458459for (i = 0; i < mech_names->count; i++) {460maj_stat = gss_oid_to_str(&min_stat, &mech_names->elements[i],461&oid_name);462if (maj_stat != GSS_S_COMPLETE) {463display_status("converting oid->string", maj_stat, min_stat);464return -1;465}466printf(" %d: %.*s\n", (int)i, (int)oid_name.length,467(char *)oid_name.value);468469(void)gss_release_buffer(&min_stat, &oid_name);470}471(void)gss_release_oid_set(&min_stat, &mech_names);472}473474if (use_file) {475read_file(msg, &in_buf);476} else {477/* Seal the message. */478in_buf.value = msg;479in_buf.length = strlen(msg);480}481482for (i = 0; i < mcount; i++) {483if (wrap_flag) {484maj_stat = gss_wrap(&min_stat, context, encrypt_flag,485GSS_C_QOP_DEFAULT, &in_buf, &state, &out_buf);486if (maj_stat != GSS_S_COMPLETE) {487display_status("wrapping message", maj_stat, min_stat);488(void)closesocket(s);489(void)gss_delete_sec_context(&min_stat, &context,490GSS_C_NO_BUFFER);491return -1;492} else if (encrypt_flag && !state) {493fprintf(stderr, "Warning! Message not encrypted.\n");494}495} else {496out_buf = in_buf;497}498499/* Send to server. */500flags = 0;501if (!v1_format) {502flags = TOKEN_DATA | (wrap_flag ? TOKEN_WRAPPED : 0) |503(encrypt_flag ? TOKEN_ENCRYPTED : 0) |504(mic_flag ? TOKEN_SEND_MIC : 0);505}506if (send_token(s, flags, &out_buf) < 0) {507(void)closesocket(s);508(void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);509return -1;510}511if (out_buf.value != in_buf.value)512(void)gss_release_buffer(&min_stat, &out_buf);513514/* Read signature block into out_buf. */515if (recv_token(s, &token_flags, &out_buf) < 0) {516(void)closesocket(s);517(void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);518return -1;519}520521if (mic_flag) {522/* Verify signature block. */523maj_stat = gss_verify_mic(&min_stat, context, &in_buf, &out_buf,524&qop_state);525if (maj_stat != GSS_S_COMPLETE) {526display_status("verifying signature", maj_stat, min_stat);527(void)closesocket(s);528(void)gss_delete_sec_context(&min_stat, &context,529GSS_C_NO_BUFFER);530return -1;531}532533if (verbose)534printf("Signature verified.\n");535} else if (verbose) {536printf("Response received.\n");537}538539free(out_buf.value);540}541542if (use_file)543free(in_buf.value);544545/* Send NOOP. */546if (!v1_format)547(void)send_token(s, TOKEN_NOOP, empty_token);548549if (auth_flag) {550/* Delete context. */551maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf);552if (maj_stat != GSS_S_COMPLETE) {553display_status("deleting context", maj_stat, min_stat);554(void)closesocket(s);555(void)gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);556return -1;557}558559(void)gss_release_buffer(&min_stat, &out_buf);560}561562(void)closesocket(s);563return 0;564}565566static void567parse_oid(char *mechanism, gss_OID *oid)568{569char *mechstr = 0, *cp;570gss_buffer_desc tok;571OM_uint32 maj_stat, min_stat;572573if (isdigit((unsigned char)mechanism[0])) {574if (asprintf(&mechstr, "{ %s }", mechanism) < 0) {575fprintf(stderr, "Couldn't allocate mechanism scratch!\n");576return;577}578for (cp = mechstr; *cp; cp++) {579if (*cp == '.')580*cp = ' ';581}582tok.value = mechstr;583} else {584tok.value = mechanism;585}586tok.length = strlen(tok.value);587maj_stat = gss_str_to_oid(&min_stat, &tok, oid);588if (maj_stat != GSS_S_COMPLETE) {589display_status("str_to_oid", maj_stat, min_stat);590return;591}592if (mechstr)593free(mechstr);594}595596static int max_threads = 1;597598#ifdef _WIN32599static thread_count = 0;600static HANDLE hMutex = NULL;601static HANDLE hEvent = NULL;602603void604init_handles(void)605{606hMutex = CreateMutex(NULL, FALSE, NULL);607hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);608}609610void611cleanup_handles(void)612{613CloseHandle(hMutex);614CloseHandle(hEvent);615}616617BOOL618wait_and_increment_thread_counter(void)619{620for (;;) {621if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {622if (thread_count < max_threads) {623thread_count++;624ReleaseMutex(hMutex);625return TRUE;626} else {627ReleaseMutex(hMutex);628629if (WaitForSingleObject(hEvent, INFINITE) == WAIT_OBJECT_0)630continue;631else632return FALSE;633}634} else {635return FALSE;636}637}638}639640BOOL641decrement_and_signal_thread_counter(void)642{643if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0) {644if (thread_count == max_threads)645SetEvent(hEvent);646thread_count--;647ReleaseMutex(hMutex);648return TRUE;649} else {650return FALSE;651}652}653654#else /* assume pthread */655656static pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;657static pthread_cond_t counter_cond = PTHREAD_COND_INITIALIZER;658int counter = 0;659660static int661wait_and_increment_thread_counter(void)662{663int err;664665err = pthread_mutex_lock(&counter_mutex);666if (err) {667perror("pthread_mutex_lock");668return 0;669}670if (counter == max_threads) {671err = pthread_cond_wait(&counter_cond, &counter_mutex);672if (err) {673pthread_mutex_unlock(&counter_mutex);674perror("pthread_cond_wait");675return 0;676}677}678counter++;679pthread_mutex_unlock(&counter_mutex);680return 1;681}682683static void684decrement_and_signal_thread_counter(void)685{686int err;687688sleep(1);689err = pthread_mutex_lock(&counter_mutex);690if (err) {691perror("pthread_mutex_lock");692return;693}694if (counter == max_threads)695pthread_cond_broadcast(&counter_cond);696counter--;697pthread_mutex_unlock(&counter_mutex);698}699700#endif701702static char *service_name, *server_host, *msg;703static char *mechanism = 0;704static u_short port = 4444;705static int use_file = 0;706static OM_uint32 gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;707static OM_uint32 min_stat;708static gss_OID oid = GSS_C_NULL_OID;709static int mcount = 1, ccount = 1;710static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;711712static void *713worker_bee(void *unused)714{715printf("worker bee!\n");716if (call_server(server_host, port, oid, service_name,717gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,718v1_format, msg, use_file, mcount) < 0) {719if (max_threads == 1)720exit(6);721}722723if (max_threads > 1)724decrement_and_signal_thread_counter();725free(unused);726return NULL;727}728729int730main(int argc, char **argv)731{732int i;733734display_file = stdout;735auth_flag = wrap_flag = encrypt_flag = mic_flag = 1;736v1_format = 0;737738/* Parse arguments. */739argc--;740argv++;741while (argc) {742if (strcmp(*argv, "-port") == 0) {743argc--;744argv++;745if (!argc)746usage();747port = atoi(*argv);748} else if (strcmp(*argv, "-mech") == 0) {749argc--;750argv++;751if (!argc)752usage();753mechanism = *argv;754} else if (strcmp(*argv, "-threads") == 0) {755argc--;756argv++;757if (!argc)758usage();759max_threads = atoi(*argv);760} else if (strcmp(*argv, "-d") == 0) {761gss_flags |= GSS_C_DELEG_FLAG;762} else if (strcmp(*argv, "-seq") == 0) {763gss_flags |= GSS_C_SEQUENCE_FLAG;764} else if (strcmp(*argv, "-noreplay") == 0) {765gss_flags &= ~GSS_C_REPLAY_FLAG;766} else if (strcmp(*argv, "-nomutual") == 0) {767gss_flags &= ~GSS_C_MUTUAL_FLAG;768} else if (strcmp(*argv, "-f") == 0) {769use_file = 1;770} else if (strcmp(*argv, "-q") == 0) {771verbose = 0;772} else if (strcmp(*argv, "-ccount") == 0) {773argc--;774argv++;775if (!argc)776usage();777ccount = atoi(*argv);778if (ccount <= 0)779usage();780} else if (strcmp(*argv, "-mcount") == 0) {781argc--;782argv++;783if (!argc)784usage();785mcount = atoi(*argv);786if (mcount < 0)787usage();788} else if (strcmp(*argv, "-na") == 0) {789auth_flag = wrap_flag = encrypt_flag = mic_flag = 0;790} else if (strcmp(*argv, "-nw") == 0) {791wrap_flag = 0;792} else if (strcmp(*argv, "-nx") == 0) {793encrypt_flag = 0;794} else if (strcmp(*argv, "-nm") == 0) {795mic_flag = 0;796} else if (strcmp(*argv, "-v1") == 0) {797v1_format = 1;798} else {799break;800}801argc--;802argv++;803}804if (argc != 3)805usage();806807#ifdef _WIN32808if (max_threads < 1) {809fprintf(stderr, "warning: there must be at least one thread\n");810max_threads = 1;811}812813init_handles();814SetEnvironmentVariable("KERBEROSLOGIN_NEVER_PROMPT", "1");815#endif816817server_host = *argv++;818service_name = *argv++;819msg = *argv++;820821if (mechanism)822parse_oid(mechanism, &oid);823824if (get_server_info(server_host, port) < 0)825exit(1);826827if (max_threads == 1) {828for (i = 0; i < ccount; i++)829worker_bee(0);830} else {831for (i = 0; i < ccount; i++) {832if (wait_and_increment_thread_counter()) {833#ifdef _WIN32834uintptr_t handle = _beginthread(worker_bee, 0, (void *)NULL);835if (handle == (uintptr_t)-1)836exit(7);837#else838int err;839pthread_t thr;840err = pthread_create(&thr, 0, worker_bee, malloc(12));841if (err) {842perror("pthread_create");843exit(7);844}845(void)pthread_detach(thr);846#endif847} else {848exit(8);849}850}851}852853if (oid != GSS_C_NULL_OID)854(void)gss_release_oid(&min_stat, &oid);855856#ifdef _WIN32857cleanup_handles();858#else859if (max_threads > 1)860sleep(10);861#endif862863return 0;864}865866867