442 lines
15 KiB
Diff
442 lines
15 KiB
Diff
|
--- src/network.c 2011-05-01 23:28:59.000000000 +0200
|
||
|
+++ src/network.c 2011-05-08 09:45:04.000000000 +0200
|
||
|
@@ -119,10 +119,10 @@
|
||
|
int retval, retry;
|
||
|
CONTEXT *context, *prev;
|
||
|
int min_timeout;
|
||
|
- int nfds, i;
|
||
|
+ unsigned int nfds, i;
|
||
|
time_t now;
|
||
|
short *signal_revents;
|
||
|
- static int max_nfds=0;
|
||
|
+ static unsigned int max_nfds=0;
|
||
|
static struct pollfd *ufds=NULL;
|
||
|
|
||
|
time(&now);
|
||
|
@@ -216,51 +216,56 @@
|
||
|
}
|
||
|
|
||
|
int s_poll_wait(s_poll_set *fds, int sec, int msec) {
|
||
|
- /* FIXME: msec parameter is currently ignored with UCONTEXT threads */
|
||
|
CONTEXT *context; /* current context */
|
||
|
static CONTEXT *to_free=NULL; /* delayed memory deallocation */
|
||
|
|
||
|
+ /* FIXME: msec parameter is currently ignored with UCONTEXT threads */
|
||
|
+ (void)msec; /* skip warning about unused parameter */
|
||
|
+
|
||
|
/* remove the current context from ready queue */
|
||
|
context=ready_head;
|
||
|
ready_head=ready_head->next;
|
||
|
if(!ready_head) /* the queue is empty */
|
||
|
ready_tail=NULL;
|
||
|
+ /* it it safe to s_log() after new ready_head is set */
|
||
|
+
|
||
|
+ /* it's illegal to deallocate the stack of the current context */
|
||
|
+ if(to_free) { /* a delayed deallocation is scheduled */
|
||
|
+ s_log(LOG_DEBUG, "Releasing context %ld", to_free->id);
|
||
|
+ free(to_free->stack);
|
||
|
+ free(to_free);
|
||
|
+ to_free=NULL;
|
||
|
+ }
|
||
|
|
||
|
+ /* manage the current thread */
|
||
|
if(fds) { /* something to wait for -> swap the context */
|
||
|
context->fds=fds; /* set file descriptors to wait for */
|
||
|
context->finish=sec<0 ? -1 : time(NULL)+sec;
|
||
|
- /* move (append) the current context to the waiting queue */
|
||
|
+
|
||
|
+ /* append the current context to the waiting queue */
|
||
|
context->next=NULL;
|
||
|
if(waiting_tail)
|
||
|
waiting_tail->next=context;
|
||
|
waiting_tail=context;
|
||
|
if(!waiting_head)
|
||
|
waiting_head=context;
|
||
|
- while(!ready_head) /* no context ready */
|
||
|
- scan_waiting_queue();
|
||
|
+ } else { /* nothing to wait for -> drop the context */
|
||
|
+ to_free=context; /* schedule for delayed deallocation */
|
||
|
+ }
|
||
|
+
|
||
|
+ while(!ready_head) /* wait until there is a thread to switch to */
|
||
|
+ scan_waiting_queue();
|
||
|
+
|
||
|
+ /* switch threads */
|
||
|
+ if(fds) { /* swap the current context */
|
||
|
if(context->id!=ready_head->id) {
|
||
|
s_log(LOG_DEBUG, "Context swap: %ld -> %ld",
|
||
|
context->id, ready_head->id);
|
||
|
swapcontext(&context->context, &ready_head->context);
|
||
|
s_log(LOG_DEBUG, "Current context: %ld", ready_head->id);
|
||
|
- if(to_free) {
|
||
|
- s_log(LOG_DEBUG, "Releasing context %ld", to_free->id);
|
||
|
- free(to_free->stack);
|
||
|
- free(to_free);
|
||
|
- to_free=NULL;
|
||
|
- }
|
||
|
}
|
||
|
return ready_head->ready;
|
||
|
- } else { /* nothing to wait for -> drop the context */
|
||
|
- /* it's illegal to deallocate the stack of the current context */
|
||
|
- if(to_free) {
|
||
|
- s_log(LOG_DEBUG, "Releasing context %ld", to_free->id);
|
||
|
- free(to_free->stack);
|
||
|
- free(to_free);
|
||
|
- }
|
||
|
- to_free=context;
|
||
|
- while(!ready_head) /* no context ready */
|
||
|
- scan_waiting_queue();
|
||
|
+ } else { /* drop the current context */
|
||
|
s_log(LOG_DEBUG, "Context set: %ld (dropped) -> %ld",
|
||
|
context->id, ready_head->id);
|
||
|
setcontext(&ready_head->context);
|
||
|
@@ -701,6 +706,10 @@
|
||
|
longjmp(c->err, 1); /* error */
|
||
|
}
|
||
|
line=str_realloc(line, ptr+1);
|
||
|
+ if(!line) {
|
||
|
+ s_log(LOG_CRIT, "Memory allocation failed");
|
||
|
+ longjmp(c->err, 1); /* error */
|
||
|
+ }
|
||
|
switch(readsocket(fd, line+ptr, 1)) {
|
||
|
case -1: /* error */
|
||
|
sockerror("readsocket (fdgetline)");
|
||
|
--- src/sthreads.c 2011-05-02 23:58:45.000000000 +0200
|
||
|
+++ src/sthreads.c 2011-05-10 11:45:18.000000000 +0200
|
||
|
@@ -47,10 +47,12 @@
|
||
|
/* 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 */
|
||
|
}
|
||
|
|
||
|
@@ -76,7 +78,6 @@
|
||
|
/* 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() */
|
||
|
-int next_id=1;
|
||
|
|
||
|
unsigned long stunnel_process_id(void) {
|
||
|
return (unsigned long)getpid();
|
||
|
@@ -86,63 +87,48 @@
|
||
|
return ready_head ? ready_head->id : 0;
|
||
|
}
|
||
|
|
||
|
-static CONTEXT *new_context(int stack_size) {
|
||
|
+static CONTEXT *new_context(void) {
|
||
|
+ static int next_id=1;
|
||
|
CONTEXT *context;
|
||
|
|
||
|
/* allocate and fill the CONTEXT structure */
|
||
|
- context=str_alloc(sizeof(CONTEXT));
|
||
|
+ context=calloc(1, sizeof(CONTEXT));
|
||
|
if(!context) {
|
||
|
s_log(LOG_ERR, "Unable to allocate CONTEXT structure");
|
||
|
return NULL;
|
||
|
}
|
||
|
- context->stack=str_alloc(stack_size);
|
||
|
- if(!context->stack) {
|
||
|
- s_log(LOG_ERR, "Unable to allocate CONTEXT stack");
|
||
|
- return NULL;
|
||
|
- }
|
||
|
context->id=next_id++;
|
||
|
context->fds=NULL;
|
||
|
context->ready=0;
|
||
|
- /* some manuals claim that initialization of context structure is required */
|
||
|
- if(getcontext(&context->context)<0) {
|
||
|
- str_free(context->stack);
|
||
|
- str_free(context);
|
||
|
- ioerror("getcontext");
|
||
|
- return NULL;
|
||
|
- }
|
||
|
- context->context.uc_link=NULL; /* it should never happen */
|
||
|
-#if defined(__sgi) || ARGC==2 /* obsolete ss_sp semantics */
|
||
|
- context->context.uc_stack.ss_sp=context->stack+stack_size-8;
|
||
|
-#else
|
||
|
- context->context.uc_stack.ss_sp=context->stack;
|
||
|
-#endif
|
||
|
- context->context.uc_stack.ss_size=stack_size;
|
||
|
- context->context.uc_stack.ss_flags=0;
|
||
|
|
||
|
- /* attach to the tail of the ready queue */
|
||
|
+ /* 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;
|
||
|
}
|
||
|
|
||
|
void sthreads_init(void) {
|
||
|
/* create the first (listening) context and put it in the running queue */
|
||
|
- if(!new_context(DEFAULT_STACK_SIZE)) {
|
||
|
+ if(!new_context()) {
|
||
|
s_log(LOG_ERR, "Unable create the listening context");
|
||
|
die(1);
|
||
|
}
|
||
|
+ /* no need to initialize ucontext_t structure here
|
||
|
+ it will be initialied with swapcontext() call */
|
||
|
}
|
||
|
|
||
|
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(arg->opt->stack_size);
|
||
|
+ context=new_context();
|
||
|
if(!context) {
|
||
|
if(arg)
|
||
|
free(arg);
|
||
|
@@ -150,6 +136,38 @@
|
||
|
closesocket(s);
|
||
|
return -1;
|
||
|
}
|
||
|
+
|
||
|
+ /* initialize context_t structure */
|
||
|
+ if(getcontext(&context->context)<0) {
|
||
|
+ free(context);
|
||
|
+ if(arg)
|
||
|
+ 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=calloc(1, arg->opt->stack_size);
|
||
|
+ if(!context->stack) {
|
||
|
+ free(context);
|
||
|
+ if(arg)
|
||
|
+ free(arg);
|
||
|
+ if(s>=0)
|
||
|
+ closesocket(s);
|
||
|
+ s_log(LOG_ERR, "Unable to allocate stack");
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+#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;
|
||
|
+
|
||
|
s_log(LOG_DEBUG, "Context %ld created", context->id);
|
||
|
makecontext(&context->context, (void(*)(void))cli, ARGC, arg);
|
||
|
return 0;
|
||
|
@@ -298,7 +316,7 @@
|
||
|
/* Disabled on OS X due to strange problems on Mac OS X 10.5
|
||
|
it seems to restore signal mask somewhere (I couldn't find where)
|
||
|
effectively blocking signals after first accepted connection */
|
||
|
- sigset_t sigset;
|
||
|
+ sigset_t new_set, old_set;
|
||
|
#endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
|
||
|
|
||
|
(void)ls; /* this parameter is only used with USE_FORK */
|
||
|
@@ -306,9 +324,8 @@
|
||
|
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__)
|
||
|
/* the idea is that only the main thread handles all the signals with
|
||
|
* posix threads; signals are blocked for any other thread */
|
||
|
- sigfillset(&sigset);
|
||
|
- pthread_sigmask(SIG_SETMASK, &sigset, NULL); /* block signals */
|
||
|
- sigemptyset(&sigset); /* prepare for unblocking */
|
||
|
+ sigfillset(&new_set);
|
||
|
+ pthread_sigmask(SIG_SETMASK, &new_set, &old_set); /* block signals */
|
||
|
#endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
|
||
|
pthread_attr_init(&pth_attr);
|
||
|
pthread_attr_setdetachstate(&pth_attr, PTHREAD_CREATE_DETACHED);
|
||
|
@@ -316,7 +333,7 @@
|
||
|
error=pthread_create(&thread, &pth_attr, cli, arg);
|
||
|
pthread_attr_destroy(&pth_attr);
|
||
|
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(__APPLE__)
|
||
|
- pthread_sigmask(SIG_SETMASK, &sigset, NULL); /* unblock signals */
|
||
|
+ pthread_sigmask(SIG_SETMASK, &old_set, NULL); /* unblock signals */
|
||
|
#endif /* HAVE_PTHREAD_SIGMASK && !__APPLE__*/
|
||
|
|
||
|
if(error) {
|
||
|
--- src/client.c 2011-05-02 18:12:53.000000000 +0200
|
||
|
+++ src/client.c 2011-06-17 15:19:44.000000000 +0200
|
||
|
@@ -106,10 +106,8 @@
|
||
|
if(!c->opt->option.retry)
|
||
|
break;
|
||
|
sleep(1); /* FIXME: not a good idea in ucontext threading */
|
||
|
-#ifndef USE_UCONTEXT
|
||
|
str_stats();
|
||
|
str_cleanup();
|
||
|
-#endif
|
||
|
}
|
||
|
} else
|
||
|
run_client(c);
|
||
|
@@ -119,18 +117,17 @@
|
||
|
#ifdef DEBUG_STACK_SIZE
|
||
|
stack_info(0); /* display computed value */
|
||
|
#endif
|
||
|
+#ifdef USE_UCONTEXT
|
||
|
+ s_log(LOG_DEBUG, "Context %ld closed", ready_head->id);
|
||
|
+#endif
|
||
|
str_stats();
|
||
|
-#if defined(USE_WIN32) && !defined(_WIN32_WCE)
|
||
|
str_cleanup();
|
||
|
+ /* s_log() is not allowed after str_cleanup() */
|
||
|
+#if defined(USE_WIN32) && !defined(_WIN32_WCE)
|
||
|
_endthread();
|
||
|
#endif
|
||
|
#ifdef USE_UCONTEXT
|
||
|
- s_log(LOG_DEBUG, "Context %ld closed", ready_head->id);
|
||
|
- /* no str_cleanup() here, as all contexts share the same CPU thread */
|
||
|
s_poll_wait(NULL, 0, 0); /* wait on poll() */
|
||
|
- s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context");
|
||
|
-#else
|
||
|
- str_cleanup();
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
@@ -283,6 +280,15 @@
|
||
|
SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx,
|
||
|
strlen(sid_ctx));
|
||
|
if(c->opt->option.client) {
|
||
|
+#ifndef OPENSSL_NO_TLSEXT
|
||
|
+ if(c->opt->host_name) {
|
||
|
+ s_log(LOG_DEBUG, "SNI host name: %s", c->opt->host_name);
|
||
|
+ if(!SSL_set_tlsext_host_name(c->ssl, c->opt->host_name)) {
|
||
|
+ sslerror("SSL_set_tlsext_host_name");
|
||
|
+ longjmp(c->err, 1);
|
||
|
+ }
|
||
|
+ }
|
||
|
+#endif
|
||
|
if(c->opt->session) {
|
||
|
enter_critical_section(CRIT_SESSION);
|
||
|
SSL_set_session(c->ssl, c->opt->session);
|
||
|
@@ -314,9 +320,9 @@
|
||
|
|
||
|
while(1) {
|
||
|
#if OPENSSL_VERSION_NUMBER<0x1000002f
|
||
|
- /* this critical section is a crude workaround for CVE-2010-3864 */
|
||
|
- /* see http://www.securityfocus.com/bid/44884 for details */
|
||
|
- /* NOTE: this critical section also covers callbacks (e.g. OCSP) */
|
||
|
+ /* this critical section is a crude workaround for CVE-2010-3864 *
|
||
|
+ * see http://www.securityfocus.com/bid/44884 for details *
|
||
|
+ * NOTE: this critical section also covers callbacks (e.g. OCSP) */
|
||
|
enter_critical_section(CRIT_SSL);
|
||
|
#endif /* OpenSSL version < 1.0.0b */
|
||
|
if(c->opt->option.client)
|
||
|
@@ -821,22 +827,26 @@
|
||
|
type=strchr(line, ':');
|
||
|
if(!type) {
|
||
|
s_log(LOG_ERR, "Malformed IDENT response");
|
||
|
+ str_free(line);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
*type++='\0';
|
||
|
system=strchr(type, ':');
|
||
|
if(!system) {
|
||
|
s_log(LOG_ERR, "Malformed IDENT response");
|
||
|
+ str_free(line);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
*system++='\0';
|
||
|
if(strcmp(type, " USERID ")) {
|
||
|
s_log(LOG_ERR, "Incorrect INETD response type");
|
||
|
+ str_free(line);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
user=strchr(system, ':');
|
||
|
if(!user) {
|
||
|
s_log(LOG_ERR, "Malformed IDENT response");
|
||
|
+ str_free(line);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
*user++='\0';
|
||
|
@@ -846,9 +856,11 @@
|
||
|
safestring(user);
|
||
|
s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)",
|
||
|
c->accepted_address, user);
|
||
|
+ str_free(line);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
s_log(LOG_INFO, "IDENT authentication passed");
|
||
|
+ str_free(line);
|
||
|
}
|
||
|
|
||
|
#if defined(_WIN32_WCE) || defined(__vms)
|
||
|
@@ -878,8 +890,8 @@
|
||
|
execname_l=str2tstr(c->opt->execname);
|
||
|
execargs_l=str2tstr(c->opt->execargs);
|
||
|
CreateProcess(execname_l, execargs_l, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
||
|
- free(execname_l);
|
||
|
- free(execargs_l);
|
||
|
+ str_free(execname_l);
|
||
|
+ str_free(execargs_l);
|
||
|
|
||
|
closesocket(fd[1]);
|
||
|
CloseHandle(pi.hProcess);
|
||
|
@@ -916,6 +928,7 @@
|
||
|
longjmp(c->err, 1);
|
||
|
case 0: /* child */
|
||
|
closesocket(fd[0]);
|
||
|
+ set_nonblock(fd[1], 0); /* switch back to blocking mode */
|
||
|
/* dup2() does not copy FD_CLOEXEC flag */
|
||
|
dup2(fd[1], 0);
|
||
|
dup2(fd[1], 1);
|
||
|
@@ -969,9 +982,10 @@
|
||
|
s=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#1");
|
||
|
if(s<0)
|
||
|
longjmp(c->err, 1);
|
||
|
- fd[1]=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#2");
|
||
|
- if(fd[1]<0)
|
||
|
+ c->fd=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#2");
|
||
|
+ if(c->fd<0)
|
||
|
longjmp(c->err, 1);
|
||
|
+
|
||
|
addrlen=sizeof addr;
|
||
|
memset(&addr, 0, addrlen);
|
||
|
addr.in.sin_family=AF_INET;
|
||
|
@@ -979,22 +993,30 @@
|
||
|
addr.in.sin_port=htons(0); /* dynamic port allocation */
|
||
|
if(bind(s, &addr.sa, addrlen))
|
||
|
log_error(LOG_DEBUG, get_last_socket_error(), "bind#1");
|
||
|
- if(bind(fd[1], &addr.sa, addrlen))
|
||
|
+ if(bind(c->fd, &addr.sa, addrlen))
|
||
|
log_error(LOG_DEBUG, get_last_socket_error(), "bind#2");
|
||
|
+
|
||
|
if(listen(s, 1)) {
|
||
|
+ closesocket(s);
|
||
|
sockerror("listen");
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
if(getsockname(s, &addr.sa, &addrlen)) {
|
||
|
+ closesocket(s);
|
||
|
sockerror("getsockname");
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
- if(connect(fd[1], &addr.sa, addrlen)) {
|
||
|
- sockerror("connect");
|
||
|
+ if(connect_blocking(c, &addr, addr_len(addr))) {
|
||
|
+ closesocket(s);
|
||
|
longjmp(c->err, 1);
|
||
|
}
|
||
|
- if((fd[0]=s_accept(s, &addr.sa, &addrlen, 1, "accept"))<0)
|
||
|
+ fd[0]=s_accept(s, &addr.sa, &addrlen, 1, "accept");
|
||
|
+ if(fd[0]<0) {
|
||
|
+ closesocket(s);
|
||
|
longjmp(c->err, 1);
|
||
|
+ }
|
||
|
+ fd[1]=c->fd;
|
||
|
+ c->fd=-1;
|
||
|
closesocket(s); /* don't care about the result */
|
||
|
#else
|
||
|
if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, fd, 1, "socketpair"))
|