1 /*
2  *   stunnel       TLS offloading and load-balancing proxy
3  *   Copyright (C) 1998-2021 Michal Trojnara <Michal.Trojnara@stunnel.org>
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU General Public License as published by the
7  *   Free Software Foundation; either version 2 of the License, or (at your
8  *   option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *   See the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License along
16  *   with this program; if not, see <http://www.gnu.org/licenses>.
17  *
18  *   Linking stunnel statically or dynamically with other modules is making
19  *   a combined work based on stunnel. Thus, the terms and conditions of
20  *   the GNU General Public License cover the whole combination.
21  *
22  *   In addition, as a special exception, the copyright holder of stunnel
23  *   gives you permission to combine stunnel with free software programs or
24  *   libraries that are released under the GNU LGPL and with code included
25  *   in the standard release of OpenSSL under the OpenSSL License (or
26  *   modified versions of such code, with unchanged license). You may copy
27  *   and distribute such a system following the terms of the GNU GPL for
28  *   stunnel and the licenses of the other code concerned.
29  *
30  *   Note that people who make modified versions of stunnel are not obligated
31  *   to grant this special exception for their modified versions; it is their
32  *   choice whether to do so. The GNU General Public License gives permission
33  *   to release a modified version without this exception; this exception
34  *   also makes it possible to release a modified version which carries
35  *   forward this exception.
36  */
37 
38 #ifdef USE_OS2
39 #define INCL_DOSPROCESS
40 #include <os2.h>
41 #endif
42 
43 #include "common.h"
44 #include "prototypes.h"
45 
46 #ifndef USE_FORK
47 CLI *thread_head=NULL;
48 NOEXPORT void thread_list_add(CLI *);
49 #endif
50 
51 /**************************************** thread ID callbacks */
52 
53 #ifdef USE_UCONTEXT
54 
stunnel_process_id(void)55 unsigned long stunnel_process_id(void) {
56     return (unsigned long)getpid();
57 }
58 
stunnel_thread_id(void)59 unsigned long stunnel_thread_id(void) {
60     return ready_head ? ready_head->id : 0;
61 }
62 
63 #endif /* USE_UCONTEXT */
64 
65 #ifdef USE_FORK
66 
stunnel_process_id(void)67 unsigned long stunnel_process_id(void) {
68     return (unsigned long)getpid();
69 }
70 
stunnel_thread_id(void)71 unsigned long stunnel_thread_id(void) {
72     return 0L;
73 }
74 
75 #endif /* USE_FORK */
76 
77 #ifdef USE_PTHREAD
78 
stunnel_process_id(void)79 unsigned long stunnel_process_id(void) {
80     return (unsigned long)getpid();
81 }
82 
stunnel_thread_id(void)83 unsigned long stunnel_thread_id(void) {
84 #if defined(SYS_gettid) && defined(__linux__)
85     return (unsigned long)syscall(SYS_gettid);
86 #else
87     return (unsigned long)pthread_self();
88 #endif
89 }
90 
91 #endif /* USE_PTHREAD */
92 
93 #ifdef USE_WIN32
94 
stunnel_process_id(void)95 unsigned long stunnel_process_id(void) {
96     return GetCurrentProcessId() & 0x00ffffff;
97 }
98 
stunnel_thread_id(void)99 unsigned long stunnel_thread_id(void) {
100     return GetCurrentThreadId() & 0x00ffffff;
101 }
102 
103 #endif /* USE_WIN32 */
104 
105 #if (OPENSSL_VERSION_NUMBER>=0x10000000L && OPENSSL_VERSION_NUMBER<0x10100004L) || defined(LIBRESSL_VERSION_NUMBER)
threadid_func(CRYPTO_THREADID * tid)106 NOEXPORT void threadid_func(CRYPTO_THREADID *tid) {
107     CRYPTO_THREADID_set_numeric(tid, stunnel_thread_id());
108 }
109 #endif
110 
thread_id_init(void)111 void thread_id_init(void) {
112 #if (OPENSSL_VERSION_NUMBER>=0x10000000L && OPENSSL_VERSION_NUMBER<0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
113     CRYPTO_THREADID_set_callback(threadid_func);
114 #endif
115 #if OPENSSL_VERSION_NUMBER<0x10000000L || !defined(OPENSSL_NO_DEPRECATED)
116     CRYPTO_set_id_callback(stunnel_thread_id);
117 #endif
118 }
119 
120 /**************************************** locking */
121 
122 /* we only need to initialize locking with OpenSSL older than 1.1.0 */
123 #if OPENSSL_VERSION_NUMBER<0x10100004L
124 
125 #ifdef USE_PTHREAD
126 
s_lock_init_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)127 NOEXPORT void s_lock_init_debug(struct CRYPTO_dynlock_value *lock,
128         const char *file, int line) {
129     pthread_rwlock_init(&lock->rwlock, NULL);
130 #ifdef DEBUG_LOCKS
131     lock->init_file=file;
132     lock->init_line=line;
133 #else
134     (void)file; /* squash the unused parameter warning */
135     (void)line; /* squash the unused parameter warning */
136 #endif
137 }
138 
s_read_lock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)139 NOEXPORT void s_read_lock_debug(struct CRYPTO_dynlock_value *lock,
140         const char *file, int line) {
141     pthread_rwlock_rdlock(&lock->rwlock);
142 #ifdef DEBUG_LOCKS
143     lock->read_lock_file=file;
144     lock->read_lock_line=line;
145 #else
146     (void)file; /* squash the unused parameter warning */
147     (void)line; /* squash the unused parameter warning */
148 #endif
149 }
150 
s_write_lock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)151 NOEXPORT void s_write_lock_debug(struct CRYPTO_dynlock_value *lock,
152         const char *file, int line) {
153     pthread_rwlock_wrlock(&lock->rwlock);
154 #ifdef DEBUG_LOCKS
155     lock->write_lock_file=file;
156     lock->write_lock_line=line;
157 #else
158     (void)file; /* squash the unused parameter warning */
159     (void)line; /* squash the unused parameter warning */
160 #endif
161 }
162 
s_unlock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)163 NOEXPORT void s_unlock_debug(struct CRYPTO_dynlock_value *lock,
164         const char *file, int line) {
165     pthread_rwlock_unlock(&lock->rwlock);
166 #ifdef DEBUG_LOCKS
167     lock->unlock_file=file;
168     lock->unlock_line=line;
169 #else
170     (void)file; /* squash the unused parameter warning */
171     (void)line; /* squash the unused parameter warning */
172 #endif
173 }
174 
s_lock_destroy_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)175 NOEXPORT void s_lock_destroy_debug(struct CRYPTO_dynlock_value *lock,
176         const char *file, int line) {
177     pthread_rwlock_destroy(&lock->rwlock);
178 #ifdef DEBUG_LOCKS
179     lock->destroy_file=file;
180     lock->destroy_line=line;
181 #else
182     (void)file; /* squash the unused parameter warning */
183     (void)line; /* squash the unused parameter warning */
184 #endif
185     str_free(lock);
186 }
187 
188 #endif /* USE_PTHREAD */
189 
190 #ifdef USE_WIN32
191 
192 /* Slim Reader/Writer (SRW) Lock would be better than CRITICAL_SECTION,
193  * but it is unsupported on Windows XP (and earlier versions of Windows):
194  * https://msdn.microsoft.com/en-us/library/windows/desktop/aa904937%28v=vs.85%29.aspx */
195 
s_lock_init_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)196 NOEXPORT void s_lock_init_debug(struct CRYPTO_dynlock_value *lock,
197         const char *file, int line) {
198     InitializeCriticalSection(&lock->critical_section);
199 #ifdef DEBUG_LOCKS
200     lock->init_file=file;
201     lock->init_line=line;
202 #else
203     (void)file; /* squash the unused parameter warning */
204     (void)line; /* squash the unused parameter warning */
205 #endif
206 }
207 
s_read_lock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)208 NOEXPORT void s_read_lock_debug(struct CRYPTO_dynlock_value *lock,
209         const char *file, int line) {
210     EnterCriticalSection(&lock->critical_section);
211 #ifdef DEBUG_LOCKS
212     lock->read_lock_file=file;
213     lock->read_lock_line=line;
214 #else
215     (void)file; /* squash the unused parameter warning */
216     (void)line; /* squash the unused parameter warning */
217 #endif
218 }
219 
s_write_lock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)220 NOEXPORT void s_write_lock_debug(struct CRYPTO_dynlock_value *lock,
221         const char *file, int line) {
222     EnterCriticalSection(&lock->critical_section);
223 #ifdef DEBUG_LOCKS
224     lock->write_lock_file=file;
225     lock->write_lock_line=line;
226 #else
227     (void)file; /* squash the unused parameter warning */
228     (void)line; /* squash the unused parameter warning */
229 #endif
230 }
231 
s_unlock_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)232 NOEXPORT void s_unlock_debug(struct CRYPTO_dynlock_value *lock,
233         const char *file, int line) {
234     LeaveCriticalSection(&lock->critical_section);
235 #ifdef DEBUG_LOCKS
236     lock->unlock_file=file;
237     lock->unlock_line=line;
238 #else
239     (void)file; /* squash the unused parameter warning */
240     (void)line; /* squash the unused parameter warning */
241 #endif
242 }
243 
s_lock_destroy_debug(struct CRYPTO_dynlock_value * lock,const char * file,int line)244 NOEXPORT void s_lock_destroy_debug(struct CRYPTO_dynlock_value *lock,
245         const char *file, int line) {
246     DeleteCriticalSection(&lock->critical_section);
247 #ifdef DEBUG_LOCKS
248     lock->destroy_file=file;
249     lock->destroy_line=line;
250 #else
251     (void)file; /* squash the unused parameter warning */
252     (void)line; /* squash the unused parameter warning */
253 #endif
254     str_free(lock);
255 }
256 
257 #endif /* USE_WIN32 */
258 
s_atomic_add(int * val,int amount,CRYPTO_RWLOCK * lock)259 NOEXPORT int s_atomic_add(int *val, int amount, CRYPTO_RWLOCK *lock) {
260     int ret;
261 
262     (void)lock; /* squash the unused parameter warning */
263 #if !defined(USE_OS_THREADS)
264     /* no synchronization is needed */
265     return *val+=amount;
266 #elif defined(__GNUC__) && defined(__ATOMIC_ACQ_REL)
267     if(__atomic_is_lock_free(sizeof *val, val))
268         return __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL);
269 #elif defined(_MSC_VER)
270     return InterlockedExchangeAdd(val, amount)+amount;
271 #endif
272     CRYPTO_THREAD_write_lock(lock);
273     ret=(*val+=amount);
274     CRYPTO_THREAD_unlock(lock);
275     return ret;
276 }
277 
278 #endif /* OPENSSL_VERSION_NUMBER<0x10100004L */
279 
280 CRYPTO_RWLOCK *stunnel_locks[STUNNEL_LOCKS];
281 
282 #if OPENSSL_VERSION_NUMBER<0x10100004L || defined(LIBRESSL_VERSION_NUMBER)
283 
284 #ifdef USE_OS_THREADS
285 
286 static struct CRYPTO_dynlock_value *lock_cs;
287 
s_dynlock_create_cb(const char * file,int line)288 NOEXPORT struct CRYPTO_dynlock_value *s_dynlock_create_cb(const char *file,
289         int line) {
290     struct CRYPTO_dynlock_value *lock;
291 
292     lock=str_alloc_detached(sizeof(struct CRYPTO_dynlock_value));
293     s_lock_init_debug(lock, file, line);
294     return lock;
295 }
296 
s_dynlock_lock_cb(int mode,struct CRYPTO_dynlock_value * lock,const char * file,int line)297 NOEXPORT void s_dynlock_lock_cb(int mode, struct CRYPTO_dynlock_value *lock,
298         const char *file, int line) {
299     if(mode&CRYPTO_LOCK) {
300         /* either CRYPTO_READ or CRYPTO_WRITE (but not both) are needed */
301         if(!(mode&CRYPTO_READ)==!(mode&CRYPTO_WRITE))
302             fatal("Invalid locking mode");
303         if(mode&CRYPTO_WRITE)
304             s_write_lock_debug(lock, file, line);
305         else
306             s_read_lock_debug(lock, file, line);
307     } else
308         s_unlock_debug(lock, file, line);
309 }
310 
s_dynlock_destroy_cb(struct CRYPTO_dynlock_value * lock,const char * file,int line)311 NOEXPORT void s_dynlock_destroy_cb(struct CRYPTO_dynlock_value *lock,
312         const char *file, int line) {
313     s_lock_destroy_debug(lock, file, line);
314 }
315 
s_locking_cb(int mode,int type,const char * file,int line)316 NOEXPORT void s_locking_cb(int mode, int type, const char *file, int line) {
317     s_dynlock_lock_cb(mode, lock_cs+type, file, line);
318 }
319 
s_add_lock_cb(int * num,int amount,int type,const char * file,int line)320 NOEXPORT int s_add_lock_cb(int *num, int amount, int type,
321         const char *file, int line) {
322     (void)file; /* squash the unused parameter warning */
323     (void)line; /* squash the unused parameter warning */
324     return s_atomic_add(num, amount, lock_cs+type);
325 }
326 
CRYPTO_THREAD_lock_new(void)327 CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void) {
328     struct CRYPTO_dynlock_value *lock;
329 
330     lock=str_alloc_detached(sizeof(CRYPTO_RWLOCK));
331     s_lock_init_debug(lock, __FILE__, __LINE__);
332     return lock;
333 }
334 
CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK * lock)335 int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock) {
336     s_read_lock_debug(lock, __FILE__, __LINE__);
337     return 1;
338 }
339 
CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK * lock)340 int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock) {
341     s_write_lock_debug(lock, __FILE__, __LINE__);
342     return 1;
343 }
344 
CRYPTO_THREAD_unlock(CRYPTO_RWLOCK * lock)345 int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock) {
346     s_unlock_debug(lock, __FILE__, __LINE__);
347     return 1;
348 }
349 
CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK * lock)350 void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock) {
351     s_lock_destroy_debug(lock, __FILE__, __LINE__);
352 }
353 
354 #else /* USE_OS_THREADS */
355 
CRYPTO_THREAD_lock_new(void)356 CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void) {
357     return NULL;
358 }
359 
CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK * lock)360 int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock) {
361     (void)lock; /* squash the unused parameter warning */
362     return 1;
363 }
364 
CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK * lock)365 int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock) {
366     (void)lock; /* squash the unused parameter warning */
367     return 1;
368 }
369 
CRYPTO_THREAD_unlock(CRYPTO_RWLOCK * lock)370 int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock) {
371     (void)lock; /* squash the unused parameter warning */
372     return 1;
373 }
374 
CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK * lock)375 void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock) {
376     (void)lock; /* squash the unused parameter warning */
377 }
378 
379 #endif /* USE_OS_THREADS */
380 
CRYPTO_atomic_add(int * val,int amount,int * ret,CRYPTO_RWLOCK * lock)381 int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock) {
382     *ret=s_atomic_add(val, amount, lock);
383     return 1;
384 }
385 
386 #endif /* OPENSSL_VERSION_NUMBER<0x10100004L */
387 
locking_init(void)388 void locking_init(void) {
389     size_t i;
390 #if defined(USE_OS_THREADS) && OPENSSL_VERSION_NUMBER<0x10100004L || defined(LIBRESSL_VERSION_NUMBER)
391     size_t num;
392 
393     /* initialize the OpenSSL static locking */
394     num=(size_t)CRYPTO_num_locks();
395     lock_cs=str_alloc_detached(num*sizeof(struct CRYPTO_dynlock_value));
396     for(i=0; i<num; i++)
397         s_lock_init_debug(lock_cs+i, __FILE__, __LINE__);
398 
399     /* initialize the OpenSSL static locking callbacks */
400     CRYPTO_set_locking_callback(s_locking_cb);
401     CRYPTO_set_add_lock_callback(s_add_lock_cb);
402 
403     /* initialize the OpenSSL dynamic locking callbacks */
404     CRYPTO_set_dynlock_create_callback(s_dynlock_create_cb);
405     CRYPTO_set_dynlock_lock_callback(s_dynlock_lock_cb);
406     CRYPTO_set_dynlock_destroy_callback(s_dynlock_destroy_cb);
407 #endif /* defined(USE_OS_THREADS) && OPENSSL_VERSION_NUMBER<0x10100004L */
408 
409     /* initialize stunnel critical sections */
410     for(i=0; i<STUNNEL_LOCKS; i++) /* all the mutexes */
411         stunnel_locks[i]=CRYPTO_THREAD_lock_new();
412 }
413 
414 /**************************************** creating a client */
415 
416 #if defined(USE_UCONTEXT) || defined(USE_FORK)
417 /* no need for critical sections */
418 
419 #endif /* USE_UCONTEXT || USE_FORK */
420 
421 #ifdef USE_UCONTEXT
422 
423 #if defined(CPU_SPARC) && ( \
424         defined(OS_SOLARIS2_0) || \
425         defined(OS_SOLARIS2_1) || \
426         defined(OS_SOLARIS2_2) || \
427         defined(OS_SOLARIS2_3) || \
428         defined(OS_SOLARIS2_4) || \
429         defined(OS_SOLARIS2_5) || \
430         defined(OS_SOLARIS2_6) || \
431         defined(OS_SOLARIS2_7) || \
432         defined(OS_SOLARIS2_8))
433 #define ARGC 2
434 #else
435 #define ARGC 1
436 #endif
437 
438 /* first context on the ready list is the active context */
439 CONTEXT *ready_head=NULL, *ready_tail=NULL;         /* ready to execute */
440 CONTEXT *waiting_head=NULL, *waiting_tail=NULL;     /* waiting on poll() */
441 
new_context(void)442 NOEXPORT CONTEXT *new_context(void) {
443     static unsigned long next_id=1;
444     CONTEXT *context;
445 
446     /* allocate and fill the CONTEXT structure */
447     context=str_alloc_detached(sizeof(CONTEXT));
448     context->id=next_id++;
449     context->fds=NULL;
450     context->ready=0;
451 
452     /* append to the tail of the ready queue */
453     context->next=NULL;
454     if(ready_tail)
455         ready_tail->next=context;
456     ready_tail=context;
457     if(!ready_head)
458         ready_head=context;
459 
460     return context;
461 }
462 
sthreads_init(void)463 int sthreads_init(void) {
464     thread_id_init();
465     locking_init();
466     /* create the first (listening) context and put it in the running queue */
467     if(!new_context()) {
468         s_log(LOG_ERR, "Cannot create the listening context");
469         return 1;
470     }
471     /* update tls for newly allocated ready_head */
472     ui_tls=tls_alloc(NULL, ui_tls, "ui");
473     /* no need to initialize ucontext_t structure here
474        it will be initialied with swapcontext() call */
475     return 0;
476 }
477 
create_client(SOCKET ls,SOCKET s,CLI * arg)478 int create_client(SOCKET ls, SOCKET s, CLI *arg) {
479     CONTEXT *context;
480 
481     (void)ls; /* this parameter is only used with USE_FORK */
482 
483     s_log(LOG_DEBUG, "Creating a new context");
484     context=new_context();
485     if(!context) {
486         str_free(arg);
487         if(s>=0)
488             closesocket(s);
489         return -1;
490     }
491 
492     /* initialize context_t structure */
493     if(getcontext(&context->context)<0) {
494         str_free(context);
495         str_free(arg);
496         if(s>=0)
497             closesocket(s);
498         ioerror("getcontext");
499         return -1;
500     }
501     context->context.uc_link=NULL; /* stunnel does not use uc_link */
502 
503     /* create stack */
504     context->stack=str_alloc_detached(arg->opt->stack_size);
505 #if defined(__sgi) || ARGC==2 /* obsolete ss_sp semantics */
506     context->context.uc_stack.ss_sp=context->stack+arg->opt->stack_size-8;
507 #else
508     context->context.uc_stack.ss_sp=context->stack;
509 #endif
510     context->context.uc_stack.ss_size=arg->opt->stack_size;
511     context->context.uc_stack.ss_flags=0;
512 
513     makecontext(&context->context, (void(*)(void))client_thread, ARGC, arg);
514     thread_list_add(arg);
515     s_log(LOG_DEBUG, "New context created");
516     return 0;
517 }
518 
519 #endif /* USE_UCONTEXT */
520 
521 #ifdef USE_FORK
522 
sthreads_init(void)523 int sthreads_init(void) {
524     thread_id_init();
525     locking_init();
526     return 0;
527 }
528 
null_handler(int sig)529 NOEXPORT void null_handler(int sig) {
530     (void)sig; /* squash the unused parameter warning */
531     signal(SIGCHLD, null_handler);
532 }
533 
create_client(SOCKET ls,SOCKET s,CLI * arg)534 int create_client(SOCKET ls, SOCKET s, CLI *arg) {
535     switch(fork()) {
536     case -1:    /* error */
537         str_free(arg);
538         if(s>=0)
539             closesocket(s);
540         return -1;
541     case  0:    /* child */
542         if(ls>=0)
543             closesocket(ls);
544         signal(SIGCHLD, null_handler);
545         client_thread(arg);
546         _exit(0);
547     default:    /* parent */
548         str_free(arg);
549         if(s>=0)
550             closesocket(s);
551     }
552     return 0;
553 }
554 
555 #endif /* USE_FORK */
556 
557 #ifdef USE_PTHREAD
558 
dummy_thread(void * arg)559 NOEXPORT void *dummy_thread(void *arg) {
560     pthread_exit(arg);
561     return arg;
562 }
563 
sthreads_init(void)564 int sthreads_init(void) {
565     pthread_t thread_id;
566 
567     /* this is a workaround for NPTL threads failing to invoke
568      * pthread_exit() or pthread_cancel() from a chroot jail */
569     if(!pthread_create(&thread_id, NULL, dummy_thread, NULL))
570         pthread_join(thread_id, NULL);
571 
572     thread_id_init();
573     locking_init();
574     return 0;
575 }
576 
create_client(SOCKET ls,SOCKET s,CLI * arg)577 int create_client(SOCKET ls, SOCKET s, CLI *arg) {
578     pthread_attr_t pth_attr;
579     int error;
580 #if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__)
581     /* disabled on OS X due to strange problems on Mac OS X 10.5
582        it seems to restore signal mask somewhere (I couldn't find where)
583        effectively blocking signals after first accepted connection */
584     sigset_t new_set, old_set;
585 #endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
586 
587     (void)ls; /* this parameter is only used with USE_FORK */
588 
589 #if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__)
590     /* the idea is that only the main thread handles all the signals with
591      * posix threads;  signals are blocked for any other thread */
592     sigfillset(&new_set);
593     pthread_sigmask(SIG_SETMASK, &new_set, &old_set); /* block signals */
594 #endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
595     pthread_attr_init(&pth_attr);
596     pthread_attr_setstacksize(&pth_attr, arg->opt->stack_size);
597 
598     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_THREAD_LIST]);
599     error=pthread_create(&arg->thread_id, &pth_attr, client_thread, arg);
600     pthread_attr_destroy(&pth_attr);
601 #if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__)
602     pthread_sigmask(SIG_SETMASK, &old_set, NULL); /* unblock signals */
603 #endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
604     if(error) {
605         errno=error;
606         ioerror("pthread_create");
607         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_THREAD_LIST]);
608         str_free(arg);
609         if(s>=0)
610             closesocket(s);
611         return -1;
612     }
613     thread_list_add(arg);
614     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_THREAD_LIST]);
615     return 0;
616 }
617 
618 #endif /* USE_PTHREAD */
619 
620 #ifdef USE_WIN32
621 
622 #if !defined(_MT)
623 #error _beginthreadex requires a multithreaded C run-time library
624 #endif
625 
sthreads_init(void)626 int sthreads_init(void) {
627     thread_id_init();
628     locking_init();
629     return 0;
630 }
631 
create_client(SOCKET ls,SOCKET s,CLI * arg)632 int create_client(SOCKET ls, SOCKET s, CLI *arg) {
633     (void)ls; /* this parameter is only used with USE_FORK */
634     s_log(LOG_DEBUG, "Creating a new thread");
635     CRYPTO_THREAD_write_lock(stunnel_locks[LOCK_THREAD_LIST]);
636     arg->thread_id=(HANDLE)_beginthreadex(NULL,
637         (unsigned)arg->opt->stack_size, client_thread, arg,
638         STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
639     if(!arg->thread_id) {
640         ioerror("_beginthreadex");
641         CRYPTO_THREAD_unlock(stunnel_locks[LOCK_THREAD_LIST]);
642         str_free(arg);
643         if(s!=INVALID_SOCKET)
644             closesocket(s);
645         return -1;
646     }
647     thread_list_add(arg);
648     CRYPTO_THREAD_unlock(stunnel_locks[LOCK_THREAD_LIST]);
649     s_log(LOG_DEBUG, "New thread created");
650     return 0;
651 }
652 
653 #endif /* USE_WIN32 */
654 
655 #ifdef USE_OS2
656 
sthreads_init(void)657 int sthreads_init(void) {
658     return 0;
659 }
660 
stunnel_process_id(void)661 unsigned long stunnel_process_id(void) {
662     PTIB ptib=NULL;
663     DosGetInfoBlocks(&ptib, NULL);
664     return (unsigned long)ptib->tib_ordinal;
665 }
666 
stunnel_thread_id(void)667 unsigned long stunnel_thread_id(void) {
668     PPIB ppib=NULL;
669     DosGetInfoBlocks(NULL, &ppib);
670     return (unsigned long)ppib->pib_ulpid;
671 }
672 
create_client(SOCKET ls,SOCKET s,CLI * arg)673 int create_client(SOCKET ls, SOCKET s, CLI *arg) {
674     (void)ls; /* this parameter is only used with USE_FORK */
675     s_log(LOG_DEBUG, "Creating a new thread");
676     if((long)_beginthread(client_thread, NULL, arg->opt->stack_size, arg)==-1L) {
677         ioerror("_beginthread");
678         str_free(arg);
679         if(s>=0)
680             closesocket(s);
681         return -1;
682     }
683     s_log(LOG_DEBUG, "New thread created");
684     return 0;
685 }
686 
687 #endif /* USE_OS2 */
688 
689 #ifdef _WIN32_WCE
690 
_beginthreadex(void * security,unsigned stack_size,unsigned (__stdcall * start_address)(void *),void * arglist,unsigned initflag,unsigned * thrdaddr)691 uintptr_t _beginthreadex(void *security, unsigned stack_size,
692         unsigned ( __stdcall *start_address)(void *),
693         void *arglist, unsigned initflag, unsigned *thrdaddr) {
694     return CreateThread(NULL, stack_size,
695         (LPTHREAD_START_ROUTINE)start_address, arglist,
696         (DWORD)initflag, (LPDWORD)thrdaddr);
697 }
698 
_endthreadex(unsigned retval)699 void _endthreadex(unsigned retval) {
700     ExitThread(retval);
701 }
702 
703 #endif /* _WIN32_WCE */
704 
705 #ifdef DEBUG_STACK_SIZE
706 
707 #define STACK_RESERVE 16384
708 
709 /* some heuristic to determine the usage of client stack size */
stack_num(size_t stack_size,int init)710 NOEXPORT size_t stack_num(size_t stack_size, int init) {
711 #ifdef _WIN64
712     typedef unsigned long long TL;
713 #else
714     typedef unsigned long TL;
715 #endif
716     size_t verify_area, verify_num, i;
717     TL test_value, *table;
718 
719     if(stack_size<STACK_RESERVE)
720         return 0;
721     verify_area=(stack_size-STACK_RESERVE)&~(sizeof(TL)-1);
722     verify_num=verify_area/sizeof(TL);
723     test_value=(TL)0x1337deadbeef1337;
724     table=alloca(verify_area);
725 
726     if(init) {
727         for(i=0; i<verify_num; i++)
728             table[i]=test_value;
729         ignore_value(table); /* prevent code optimization */
730         return 0;
731     } else {
732         /* the stack grows down */
733         for(i=0; i<verify_num; i++)
734             if(table[i]!=test_value)
735                 break;
736         if(i>=16)
737             return stack_size-i*sizeof(TL);
738         /* the stack grows up */
739         for(i=0; i<verify_num; i++)
740             if(table[verify_num-i-1]!=test_value)
741                 break;
742         if(i>=16)
743             return stack_size-(i*sizeof(TL)+STACK_RESERVE);
744         return 0; /* not enough samples for meaningful results */
745     }
746 }
747 
748 #ifdef __GNUC__
749 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
750 #pragma GCC diagnostic push
751 #endif /* __GNUC__>=4.6 */
752 #pragma GCC diagnostic ignored "-Wformat"
753 #endif /* __GNUC__ */
stack_info(size_t stack_size,int init)754 void stack_info(size_t stack_size, int init) { /* 1-initialize, 0-display */
755     static size_t max_num=0;
756     size_t num;
757 
758 #ifdef USE_WIN32
759     SYSTEM_INFO si;
760     GetSystemInfo(&si);
761     stack_size&=~((size_t)si.dwPageSize-1);
762 #elif defined(_SC_PAGESIZE)
763     stack_size&=~((size_t)sysconf(_SC_PAGESIZE)-1);
764 #elif defined(_SC_PAGE_SIZE)
765     stack_size&=~((size_t)sysconf(_SC_PAGE_SIZE)-1);
766 #else
767     stack_size&=~(4096-1); /* just a guess */
768 #endif
769     num=stack_num(stack_size, init);
770     if(init)
771         return;
772     if(!num) {
773         s_log(LOG_NOTICE, "STACK_RESERVE is too high");
774         return;
775     }
776     if(num>max_num)
777         max_num=num;
778     s_log(LOG_NOTICE,
779 #ifdef USE_WIN32
780         "stack_info: size=%Iu, current=%Iu (%Iu%%), maximum=%Iu (%Iu%%)",
781 #else
782         "stack_info: size=%zu, current=%zu (%zu%%), maximum=%zu (%zu%%)",
783 #endif
784         stack_size,
785         num, num*100/stack_size,
786         max_num, max_num*100/stack_size);
787 }
788 #ifdef __GNUC__
789 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
790 #pragma GCC diagnostic pop
791 #endif /* __GNUC__>=4.6 */
792 #endif /* __GNUC__ */
793 
794 #endif /* DEBUG_STACK_SIZE */
795 
796 #ifndef USE_FORK
thread_list_add(CLI * c)797 NOEXPORT void thread_list_add(CLI *c) {
798     c->thread_next=thread_head;
799     c->thread_prev=NULL;
800     if(thread_head)
801         thread_head->thread_prev=c;
802     thread_head=c;
803 }
804 #endif /* !USE_FORK */
805 
806 /* end of sthreads.c */
807