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