786 lines
22 KiB
Diff
786 lines
22 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"))
|
|
--- src/str.c 2011-05-02 19:14:53.000000000 +0200
|
|
+++ src/str.c 2011-05-08 08:37:57.000000000 +0200
|
|
@@ -38,17 +38,128 @@
|
|
#include "common.h"
|
|
#include "prototypes.h"
|
|
|
|
-typedef struct str_struct {
|
|
- struct str_struct *prev, *next;
|
|
+#ifndef va_copy
|
|
+#ifdef __va_copy
|
|
+#define va_copy(dst, src) __va_copy((dst), (src))
|
|
+#else /* __va_copy */
|
|
+#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list))
|
|
+#endif /* __va_copy */
|
|
+#endif /* va_copy */
|
|
+
|
|
+typedef struct alloc_list {
|
|
+ struct alloc_list *prev, *next;
|
|
size_t size;
|
|
unsigned int magic;
|
|
-} STR;
|
|
-static void str_set(STR *);
|
|
-static STR *str_get();
|
|
+} ALLOC_LIST;
|
|
+
|
|
+static void set_alloc_head(ALLOC_LIST *);
|
|
+static ALLOC_LIST *get_alloc_head();
|
|
+
|
|
+char *str_dup(const char *str) {
|
|
+ char *retval;
|
|
+
|
|
+ retval=str_alloc(strlen(str)+1);
|
|
+ if(retval)
|
|
+ strcpy(retval, str);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+char *str_printf(const char *format, ...) {
|
|
+ char *txt;
|
|
+ va_list arglist;
|
|
+
|
|
+ va_start(arglist, format);
|
|
+ txt=str_vprintf(format, arglist);
|
|
+ va_end(arglist);
|
|
+ return txt;
|
|
+}
|
|
+
|
|
+char *str_vprintf(const char *format, va_list start_ap) {
|
|
+ int n, size=64;
|
|
+ char *p, *np;
|
|
+ va_list ap;
|
|
+
|
|
+ p=str_alloc(size);
|
|
+ if(!p)
|
|
+ return NULL;
|
|
+ for(;;) {
|
|
+ va_copy(ap, start_ap);
|
|
+ n=vsnprintf(p, size, format, ap);
|
|
+ if(n>-1 && n<size)
|
|
+ return p;
|
|
+ if(n>-1) /* glibc 2.1 */
|
|
+ size=n+1; /* precisely what is needed */
|
|
+ else /* glibc 2.0, WIN32, etc. */
|
|
+ size*=2; /* twice the old size */
|
|
+ np=str_realloc(p, size);
|
|
+ if(!np) {
|
|
+ str_free(p);
|
|
+ return NULL;
|
|
+ }
|
|
+ p=np; /* LOL */
|
|
+ }
|
|
+}
|
|
+
|
|
+#ifdef USE_UCONTEXT
|
|
+
|
|
+static ALLOC_LIST *alloc_tls=NULL;
|
|
+
|
|
+void str_init() {
|
|
+}
|
|
+
|
|
+static void set_alloc_head(ALLOC_LIST *alloc_head) {
|
|
+ if(ready_head)
|
|
+ ready_head->tls=alloc_head;
|
|
+ else /* ucontext threads not initialized */
|
|
+ alloc_tls=alloc_head;
|
|
+}
|
|
+
|
|
+static ALLOC_LIST *get_alloc_head() {
|
|
+ if(ready_head)
|
|
+ return ready_head->tls;
|
|
+ else /* ucontext threads not initialized */
|
|
+ return alloc_tls;
|
|
+}
|
|
+
|
|
+#endif /* USE_UCONTEXT */
|
|
+
|
|
+#ifdef USE_FORK
|
|
+
|
|
+static ALLOC_LIST *alloc_tls=NULL;
|
|
+
|
|
+void str_init() {
|
|
+}
|
|
+
|
|
+static void set_alloc_head(ALLOC_LIST *alloc_head) {
|
|
+ alloc_tls=alloc_head;
|
|
+}
|
|
+
|
|
+static ALLOC_LIST *get_alloc_head() {
|
|
+ return alloc_tls;
|
|
+}
|
|
+
|
|
+#endif /* USE_FORK */
|
|
+
|
|
+#ifdef USE_PTHREAD
|
|
+
|
|
+static pthread_key_t pthread_key;
|
|
+
|
|
+void str_init() {
|
|
+ pthread_key_create(&pthread_key, NULL);
|
|
+}
|
|
+
|
|
+static void set_alloc_head(ALLOC_LIST *alloc_head) {
|
|
+ pthread_setspecific(pthread_key, alloc_head);
|
|
+}
|
|
+
|
|
+static ALLOC_LIST *get_alloc_head() {
|
|
+ return pthread_getspecific(pthread_key);
|
|
+}
|
|
+
|
|
+#endif /* USE_PTHREAD */
|
|
|
|
#ifdef USE_WIN32
|
|
|
|
-/* __thread does not work in mingw32 due to a bug in GCC */
|
|
static DWORD tls_index;
|
|
|
|
void str_init() {
|
|
@@ -59,97 +170,81 @@
|
|
}
|
|
}
|
|
|
|
-static void str_set(STR *str) {
|
|
- if(!TlsSetValue(tls_index, str)) {
|
|
+static void set_alloc_head(ALLOC_LIST *alloc_head) {
|
|
+ if(!TlsSetValue(tls_index, alloc_head)) {
|
|
s_log(LOG_ERR, "TlsSetValue failed");
|
|
die(1);
|
|
}
|
|
}
|
|
|
|
-static STR *str_get() {
|
|
- STR *str;
|
|
+static ALLOC_LIST *get_alloc_head() {
|
|
+ ALLOC_LIST *alloc_head;
|
|
|
|
- str=TlsGetValue(tls_index);
|
|
- if(!str && GetLastError()!=ERROR_SUCCESS) {
|
|
+ alloc_head=TlsGetValue(tls_index);
|
|
+ if(!alloc_head && GetLastError()!=ERROR_SUCCESS) {
|
|
s_log(LOG_ERR, "TlsGetValue failed");
|
|
die(1);
|
|
}
|
|
- return str;
|
|
-}
|
|
-
|
|
-#else
|
|
-
|
|
-/* gcc Thread-Local Storage */
|
|
-static __thread STR *root_str=NULL;
|
|
-
|
|
-void str_init() {
|
|
- if(root_str)
|
|
- s_log(LOG_WARNING, "str_init: Non-empty allocation list");
|
|
+ return alloc_head;
|
|
}
|
|
|
|
-static void str_set(STR *str) {
|
|
- root_str=str;
|
|
-}
|
|
-
|
|
-static STR *str_get() {
|
|
- return root_str;
|
|
-}
|
|
-
|
|
-#endif
|
|
+#endif /* USE_WIN32 */
|
|
|
|
void str_cleanup() {
|
|
- STR *str, *tmp;
|
|
+ ALLOC_LIST *alloc_head, *tmp;
|
|
|
|
- str=str_get();
|
|
- while(str) {
|
|
- tmp=str;
|
|
- str=tmp->next;
|
|
+ alloc_head=get_alloc_head();
|
|
+ while(alloc_head) {
|
|
+ tmp=alloc_head;
|
|
+ alloc_head=tmp->next;
|
|
free(tmp);
|
|
}
|
|
- str_set(NULL);
|
|
+ set_alloc_head(NULL);
|
|
}
|
|
|
|
void str_stats() {
|
|
- STR *tmp;
|
|
+ ALLOC_LIST *tmp;
|
|
int blocks=0, bytes=0;
|
|
|
|
- for(tmp=str_get(); tmp; tmp=tmp->next) {
|
|
+ for(tmp=get_alloc_head(); tmp; tmp=tmp->next) {
|
|
++blocks;
|
|
bytes+=tmp->size;
|
|
}
|
|
- s_log(LOG_DEBUG, "str_stats: %d blocks, %d bytes", blocks, bytes);
|
|
+ s_log(LOG_DEBUG, "str_stats: %d block(s), %d byte(s)", blocks, bytes);
|
|
}
|
|
|
|
void *str_alloc(size_t size) {
|
|
- STR *str, *tmp;
|
|
+ ALLOC_LIST *alloc_head, *tmp;
|
|
|
|
if(size>=1024*1024) /* huge allocations are not allowed */
|
|
return NULL;
|
|
- tmp=calloc(1, sizeof(STR)+size);
|
|
+ tmp=calloc(1, sizeof(ALLOC_LIST)+size);
|
|
if(!tmp)
|
|
return NULL;
|
|
- str=str_get();
|
|
+ alloc_head=get_alloc_head();
|
|
tmp->prev=NULL;
|
|
- tmp->next=str;
|
|
+ tmp->next=alloc_head;
|
|
tmp->size=size;
|
|
tmp->magic=0xdeadbeef;
|
|
- if(str)
|
|
- str->prev=tmp;
|
|
- str_set(tmp);
|
|
+ if(alloc_head)
|
|
+ alloc_head->prev=tmp;
|
|
+ set_alloc_head(tmp);
|
|
return tmp+1;
|
|
}
|
|
|
|
void *str_realloc(void *ptr, size_t size) {
|
|
- STR *oldtmp, *tmp;
|
|
+ ALLOC_LIST *old_tmp, *tmp;
|
|
|
|
if(!ptr)
|
|
return str_alloc(size);
|
|
- oldtmp=(STR *)ptr-1;
|
|
- if(oldtmp->magic!=0xdeadbeef) { /* not allocated by str_alloc() */
|
|
+ old_tmp=(ALLOC_LIST *)ptr-1;
|
|
+ if(old_tmp->magic!=0xdeadbeef) { /* not allocated by str_alloc() */
|
|
s_log(LOG_CRIT, "INTERNAL ERROR: str_realloc: Bad magic");
|
|
die(1);
|
|
}
|
|
- tmp=realloc(oldtmp, sizeof(STR)+size);
|
|
+ if(size>=1024*1024) /* huge allocations are not allowed */
|
|
+ return NULL;
|
|
+ tmp=realloc(old_tmp, sizeof(ALLOC_LIST)+size);
|
|
if(!tmp)
|
|
return NULL;
|
|
/* refresh all possibly invalidated pointers */
|
|
@@ -158,17 +253,17 @@
|
|
if(tmp->prev)
|
|
tmp->prev->next=tmp;
|
|
tmp->size=size;
|
|
- if(str_get()==oldtmp)
|
|
- str_set(tmp);
|
|
+ if(get_alloc_head()==old_tmp)
|
|
+ set_alloc_head(tmp);
|
|
return tmp+1;
|
|
}
|
|
|
|
void str_free(void *ptr) {
|
|
- STR *tmp;
|
|
+ ALLOC_LIST *tmp;
|
|
|
|
if(!ptr) /* do not attempt to free null pointers */
|
|
return;
|
|
- tmp=(STR *)ptr-1;
|
|
+ tmp=(ALLOC_LIST *)ptr-1;
|
|
if(tmp->magic!=0xdeadbeef) { /* not allocated by str_alloc() */
|
|
s_log(LOG_CRIT, "INTERNAL ERROR: str_free: Bad magic");
|
|
die(1);
|
|
@@ -178,54 +273,9 @@
|
|
tmp->next->prev=tmp->prev;
|
|
if(tmp->prev)
|
|
tmp->prev->next=tmp->next;
|
|
- if(str_get()==tmp)
|
|
- str_set(tmp->next);
|
|
+ if(get_alloc_head()==tmp)
|
|
+ set_alloc_head(tmp->next);
|
|
free(tmp);
|
|
}
|
|
|
|
-char *str_dup(const char *str) {
|
|
- char *retval;
|
|
-
|
|
- retval=str_alloc(strlen(str)+1);
|
|
- if(retval)
|
|
- strcpy(retval, str);
|
|
- return retval;
|
|
-}
|
|
-
|
|
-char *str_vprintf(const char *format, va_list start_ap) {
|
|
- int n, size=64;
|
|
- char *p, *np;
|
|
- va_list ap;
|
|
-
|
|
- p=str_alloc(size);
|
|
- if(!p)
|
|
- return NULL;
|
|
- for(;;) {
|
|
- va_copy(ap, start_ap);
|
|
- n=vsnprintf(p, size, format, ap);
|
|
- if(n>-1 && n<size)
|
|
- return p;
|
|
- if(n>-1) /* glibc 2.1 */
|
|
- size=n+1; /* precisely what is needed */
|
|
- else /* glibc 2.0 */
|
|
- size*=2; /* twice the old size */
|
|
- np=str_realloc(p, size);
|
|
- if(!np) {
|
|
- str_free(p);
|
|
- return NULL;
|
|
- }
|
|
- p=np; /* LOL */
|
|
- }
|
|
-}
|
|
-
|
|
-char *str_printf(const char *format, ...) {
|
|
- char *txt;
|
|
- va_list arglist;
|
|
-
|
|
- va_start(arglist, format);
|
|
- txt=str_vprintf(format, arglist);
|
|
- va_end(arglist);
|
|
- return txt;
|
|
-}
|
|
-
|
|
/* end of str.c */
|