/* * stunnel Universal SSL tunnel * Copyright (C) 1998-2012 Michal Trojnara * * 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; either version 2 of the License, or (at your * option) any later version. * * 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, see . * * Linking stunnel statically or dynamically with other modules is making * a combined work based on stunnel. Thus, the terms and conditions of * the GNU General Public License cover the whole combination. * * In addition, as a special exception, the copyright holder of stunnel * gives you permission to combine stunnel with free software programs or * libraries that are released under the GNU LGPL and with code included * in the standard release of OpenSSL under the OpenSSL License (or * modified versions of such code, with unchanged license). You may copy * and distribute such a system following the terms of the GNU GPL for * stunnel and the licenses of the other code concerned. * * Note that people who make modified versions of stunnel are not obligated * to grant this special exception for their modified versions; it is their * choice whether to do so. The GNU General Public License gives permission * to release a modified version without this exception; this exception * also makes it possible to release a modified version which carries * forward this exception. */ #ifdef USE_OS2 #define INCL_DOSPROCESS #include #endif #include "common.h" #include "prototypes.h" #if defined(USE_UCONTEXT) || defined(USE_FORK) /* no need for critical sections */ void enter_critical_section(SECTION_CODE i) { (void)i; /* skip warning about unused parameter */ /* empty */ } void leave_critical_section(SECTION_CODE i) { (void)i; /* skip warning about unused parameter */ /* empty */ } #endif /* USE_UCONTEXT || USE_FORK */ #ifdef USE_UCONTEXT #if defined(CPU_SPARC) && ( \ defined(OS_SOLARIS2_0) || \ defined(OS_SOLARIS2_1) || \ defined(OS_SOLARIS2_2) || \ defined(OS_SOLARIS2_3) || \ defined(OS_SOLARIS2_4) || \ defined(OS_SOLARIS2_5) || \ defined(OS_SOLARIS2_6) || \ defined(OS_SOLARIS2_7) || \ defined(OS_SOLARIS2_8)) #define ARGC 2 #else #define ARGC 1 #endif /* first context on the ready list is the active context */ CONTEXT *ready_head=NULL, *ready_tail=NULL; /* ready to execute */ CONTEXT *waiting_head=NULL, *waiting_tail=NULL; /* waiting on poll() */ unsigned long stunnel_process_id(void) { return (unsigned long)getpid(); } unsigned long stunnel_thread_id(void) { return ready_head ? ready_head->id : 0; } static CONTEXT *new_context(void) { static int next_id=1; CONTEXT *context; /* allocate and fill the CONTEXT structure */ context=str_alloc(sizeof(CONTEXT)); str_detach(context); context->id=next_id++; context->fds=NULL; context->ready=0; /* append to the tail of the ready queue */ context->next=NULL; if(ready_tail) ready_tail->next=context; ready_tail=context; if(!ready_head) ready_head=context; return context; } int sthreads_init(void) { /* create the first (listening) context and put it in the running queue */ if(!new_context()) { s_log(LOG_ERR, "Unable create the listening context"); return 1; } /* no need to initialize ucontext_t structure here it will be initialied with swapcontext() call */ return 0; } int create_client(int ls, int s, CLI *arg, void *(*cli)(void *)) { CONTEXT *context; (void)ls; /* this parameter is only used with USE_FORK */ s_log(LOG_DEBUG, "Creating a new context"); context=new_context(); if(!context) { if(arg) str_free(arg); if(s>=0) closesocket(s); return -1; } /* initialize context_t structure */ if(getcontext(&context->context)<0) { str_free(context); if(arg) str_free(arg); if(s>=0) closesocket(s); ioerror("getcontext"); return -1; } context->context.uc_link=NULL; /* stunnel does not use uc_link */ /* create stack */ context->stack=str_alloc(arg->opt->stack_size); str_detach(context->stack); #if defined(__sgi) || ARGC==2 /* obsolete ss_sp semantics */ context->context.uc_stack.ss_sp=context->stack+arg->opt->stack_size-8; #else context->context.uc_stack.ss_sp=context->stack; #endif context->context.uc_stack.ss_size=arg->opt->stack_size; context->context.uc_stack.ss_flags=0; makecontext(&context->context, (void(*)(void))cli, ARGC, arg); s_log(LOG_DEBUG, "New context created"); return 0; } #endif /* USE_UCONTEXT */ #ifdef USE_FORK int sthreads_init(void) { return 0; } unsigned long stunnel_process_id(void) { return (unsigned long)getpid(); } unsigned long stunnel_thread_id(void) { return 0L; } static void null_handler(int sig) { (void)sig; /* skip warning about unused parameter */ signal(SIGCHLD, null_handler); } int create_client(int ls, int s, CLI *arg, void *(*cli)(void *)) { switch(fork()) { case -1: /* error */ if(arg) str_free(arg); if(s>=0) closesocket(s); return -1; case 0: /* child */ if(ls>=0) closesocket(ls); signal(SIGCHLD, null_handler); cli(arg); _exit(0); default: /* parent */ if(arg) str_free(arg); if(s>=0) closesocket(s); } return 0; } #endif /* USE_FORK */ #ifdef USE_PTHREAD static pthread_mutex_t stunnel_cs[CRIT_SECTIONS]; static pthread_mutex_t lock_cs[CRYPTO_NUM_LOCKS]; void enter_critical_section(SECTION_CODE i) { pthread_mutex_lock(stunnel_cs+i); } void leave_critical_section(SECTION_CODE i) { pthread_mutex_unlock(stunnel_cs+i); } static void locking_callback(int mode, int type, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ if(mode&CRYPTO_LOCK) pthread_mutex_lock(lock_cs+type); else pthread_mutex_unlock(lock_cs+type); } struct CRYPTO_dynlock_value { pthread_mutex_t mutex; }; static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line) { struct CRYPTO_dynlock_value *value; (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ value=str_alloc(sizeof(struct CRYPTO_dynlock_value)); str_detach(value); pthread_mutex_init(&value->mutex, NULL); return value; } static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *value, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ if(mode&CRYPTO_LOCK) pthread_mutex_lock(&value->mutex); else pthread_mutex_unlock(&value->mutex); } static void dyn_destroy_function(struct CRYPTO_dynlock_value *value, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ pthread_mutex_destroy(&value->mutex); str_free(value); } unsigned long stunnel_process_id(void) { return (unsigned long)getpid(); } unsigned long stunnel_thread_id(void) { return (unsigned long)pthread_self(); } int sthreads_init(void) { int i; /* initialize stunnel critical sections */ for(i=0; iopt->stack_size); error=pthread_create(&thread, &pth_attr, cli, arg); pthread_attr_destroy(&pth_attr); #if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__) pthread_sigmask(SIG_SETMASK, &old_set, NULL); /* unblock signals */ #endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/ if(error) { errno=error; ioerror("pthread_create"); if(arg) str_free(arg); if(s>=0) closesocket(s); return -1; } return 0; } #endif /* USE_PTHREAD */ #ifdef USE_WIN32 static CRITICAL_SECTION stunnel_cs[CRIT_SECTIONS]; static CRITICAL_SECTION lock_cs[CRYPTO_NUM_LOCKS]; void enter_critical_section(SECTION_CODE i) { EnterCriticalSection(stunnel_cs+i); } void leave_critical_section(SECTION_CODE i) { LeaveCriticalSection(stunnel_cs+i); } static void locking_callback(int mode, int type, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ if(mode&CRYPTO_LOCK) EnterCriticalSection(lock_cs+type); else LeaveCriticalSection(lock_cs+type); } struct CRYPTO_dynlock_value { CRITICAL_SECTION mutex; }; static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line) { struct CRYPTO_dynlock_value *value; (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ value=str_alloc(sizeof(struct CRYPTO_dynlock_value)); str_detach(value); InitializeCriticalSection(&value->mutex); return value; } static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *value, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ if(mode&CRYPTO_LOCK) EnterCriticalSection(&value->mutex); else LeaveCriticalSection(&value->mutex); } static void dyn_destroy_function(struct CRYPTO_dynlock_value *value, const char *file, int line) { (void)file; /* skip warning about unused parameter */ (void)line; /* skip warning about unused parameter */ DeleteCriticalSection(&value->mutex); str_free(value); } unsigned long stunnel_process_id(void) { return GetCurrentProcessId() & 0x00ffffff; } unsigned long stunnel_thread_id(void) { return GetCurrentThreadId() & 0x00ffffff; } int sthreads_init(void) { int i; /* initialize stunnel critical sections */ for(i=0; iopt->stack_size, arg)==-1) { ioerror("_beginthread"); if(arg) str_free(arg); if(s>=0) closesocket(s); return -1; } s_log(LOG_DEBUG, "New thread created"); return 0; } #endif /* USE_WIN32 */ #ifdef USE_OS2 void enter_critical_section(SECTION_CODE i) { DosEnterCritSec(); } void leave_critical_section(SECTION_CODE i) { DosExitCritSec(); } int sthreads_init(void) { return 0; } unsigned long stunnel_process_id(void) { PTIB ptib=NULL; DosGetInfoBlocks(&ptib, NULL); return (unsigned long)ptib->tib_ordinal; } unsigned long stunnel_thread_id(void) { PPIB ppib=NULL; DosGetInfoBlocks(NULL, &ppib); return (unsigned long)ppib->pib_ulpid; } int create_client(int ls, int s, CLI *arg, void *(*cli)(void *)) { (void)ls; /* this parameter is only used with USE_FORK */ s_log(LOG_DEBUG, "Creating a new thread"); if((long)_beginthread((void(*)(void *))cli, NULL, arg->opt->stack_size, arg)==-1L) { ioerror("_beginthread"); if(arg) str_free(arg); if(s>=0) closesocket(s); return -1; } s_log(LOG_DEBUG, "New thread created"); return 0; } #endif /* USE_OS2 */ #ifdef _WIN32_WCE long _beginthread(void (*start_address)(void *), int stack_size, void *arglist) { DWORD thread_id; HANDLE handle; handle=CreateThread(NULL, stack_size, (LPTHREAD_START_ROUTINE)start_address, arglist, STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); if(!handle) return -1L; CloseHandle(handle); return 0; } void _endthread(void) { ExitThread(0); } #endif /* _WIN32_WCE */ #ifdef DEBUG_STACK_SIZE #define STACK_RESERVE (STACK_SIZE/8) #define VERIFY_AREA ((STACK_SIZE-STACK_RESERVE)/sizeof(u32)) #define TEST_VALUE 0xdeadbeef /* some heuristic to determine the usage of client stack size */ void stack_info(int init) { /* 1-initialize, 0-display */ u32 table[VERIFY_AREA]; int i, num; static int min_num=VERIFY_AREA; if(init) { for(i=0; inum) /* use the higher value */ num=i; if(num<64) { s_log(LOG_NOTICE, "STACK_RESERVE is too high"); return; } if(num