diff -urN --exclude=.flxpkg stunnel-4.20-old/doc/stunnel.8 stunnel-4.20-patched/doc/stunnel.8 --- stunnel-4.20-old/doc/stunnel.8 2006-11-15 20:51:32 +0100 +++ stunnel-4.20-patched/doc/stunnel.8 2007-08-08 15:59:50 +0200 @@ -520,6 +520,10 @@ .IP "\fBtransparent\fR = yes | no (Unix only)" 4 .IX Item "transparent = yes | no (Unix only)" transparent proxy mode +.IP "\fBxforwardedfor\fR = yes | no" 4 +.IX Item "xforwardedfor = yes | no" +append an 'X-Forwarded-For:' HTTP request header providing the +client's IP address to the server. .Sp Re-write address to appear as if wrapped daemon is connecting from the \s-1SSL\s0 client machine instead of the machine running \fBstunnel\fR. diff -urN --exclude=.flxpkg stunnel-4.20-old/doc/stunnel.fr.8 stunnel-4.20-patched/doc/stunnel.fr.8 --- stunnel-4.20-old/doc/stunnel.fr.8 2005-01-15 10:15:12 +0100 +++ stunnel-4.20-patched/doc/stunnel.fr.8 2007-08-08 15:52:39 +0200 @@ -445,6 +445,10 @@ Négocie avec \s-1SSL\s0 selon le protocole indiqué .Sp Actuellement gérés\ : cifs, nntp, pop3, smtp +.IP "\fBxforwardedfor\fR = yes | no" 4 +.IX Item "xforwardedfor = yes | no" +Ajoute un en-tête 'X-Forwarded-For:' dans la requête HTTP fournissant +au serveur l'adresse IP du client. .IP "\fBpty\fR = yes | no (Unix seulement)" 4 .IX Item "pty = yes | no (Unix seulement)" Alloue un pseudo-terminal pour l'option «\ exec\ » diff -urN --exclude=.flxpkg stunnel-4.20-old/src/client.c stunnel-4.20-patched/src/client.c --- stunnel-4.20-old/src/client.c 2007-08-08 15:51:24 +0200 +++ stunnel-4.20-patched/src/client.c 2007-08-08 16:10:10 +0200 @@ -91,6 +91,12 @@ return NULL; } c->opt=opt; + /* some options need space to add some information */ + if (c->opt->option.xforwardedfor) + c->buffsize = BUFFSIZE - BUFF_RESERVED; + else + c->buffsize = BUFFSIZE; + c->crlf_seen=0; c->local_rfd.fd=rfd; c->local_wfd.fd=wfd; return c; @@ -380,6 +386,28 @@ } } +/* Moves all data from the buffer between positions and + * to insert of length . and are updated to their + * new respective values, and the number of characters inserted is returned. + * If is too long, nothing is done and -1 is returned. + * Note that neither nor can be NULL. + */ +static int buffer_insert_with_len(char *buffer, int *start, int *stop, int limit, char *string, int len) { + if (len > limit - *stop) + return -1; + if (*start > *stop) + return -1; + memmove(buffer + *start + len, buffer + *start, *stop - *start); + memcpy(buffer + *start, string, len); + *start += len; + *stop += len; + return len; +} + +static int buffer_insert(char *buffer, int *start, int *stop, int limit, char *string) { + return buffer_insert_with_len(buffer, start, stop, limit, string, strlen(string)); +} + /****************************** some defines for transfer() */ /* is socket/SSL open for read/write? */ #define sock_rd (c->sock_rfd->rd) @@ -412,13 +440,13 @@ check_SSL_pending=0; SSL_read_wants_read= - ssl_rd && c->ssl_ptrssl_ptrbuffsize && !SSL_read_wants_write; SSL_write_wants_write= ssl_wr && c->sock_ptr && !SSL_write_wants_read; /****************************** setup c->fds structure */ s_poll_zero(&c->fds); /* initialize the structure */ - if(sock_rd && c->sock_ptrsock_ptrbuffsize) s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0); if(SSL_read_wants_read || SSL_write_wants_read || @@ -499,7 +527,7 @@ break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); - if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ + if(c->ssl_ptr>=c->buffsize) /* buffer was previously full */ check_SSL_pending=1; /* check for data buffered by SSL */ c->ssl_ptr-=num; c->sock_bytes+=num; @@ -559,7 +587,7 @@ /****************************** read from socket */ if(sock_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, - c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); + c->sock_buff+c->sock_ptr, c->buffsize-c->sock_ptr); switch(num) { case -1: parse_socket_error(c, "readsocket"); @@ -579,10 +607,70 @@ (SSL_read_wants_write && ssl_can_wr) || (check_SSL_pending && SSL_pending(c->ssl))) { SSL_read_wants_write=0; - num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); + num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, c->buffsize-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: - c->ssl_ptr+=num; + if (c->buffsize != BUFFSIZE && c->opt->option.xforwardedfor) { /* some work left to do */ + int last = c->ssl_ptr; + c->ssl_ptr += num; + + /* Look for end of HTTP headers between last and ssl_ptr. + * To achieve this reliably, we have to count the number of + * successive [CR]LF and to memorize it in case it's spread + * over multiple segments. --WT. + */ + while (last < c->ssl_ptr) { + if (c->ssl_buff[last] == '\n') { + if (++c->crlf_seen == 2) + break; + } else if (last < c->ssl_ptr - 1 && + c->ssl_buff[last] == '\r' && + c->ssl_buff[last+1] == '\n') { + if (++c->crlf_seen == 2) + break; + last++; + } else if (c->ssl_buff[last] != '\r') + /* don't refuse '\r' because we may get a '\n' on next read */ + c->crlf_seen = 0; + last++; + } + if (c->crlf_seen >= 2) { + /* We have all the HTTP headers now. We don't need to + * reserve any space anymore. points to the + * first byte of unread data, and points to the + * exact location where we want to insert our headers, + * which is right before the empty line. + */ + c->buffsize = BUFFSIZE; + + if (c->opt->option.xforwardedfor) { + /* X-Forwarded-For: xxxx \r\n\0 */ + char xforw[17 + IPLEN + 3]; + + /* We will insert our X-Forwarded-For: header here. + * We need to write the IP address, but if we use + * sprintf, it will pad with the terminating 0. + * So we will pass via a temporary buffer allocated + * on the stack. + */ + memcpy(xforw, "X-Forwarded-For: ", 17); + if (getnameinfo(&c->peer_addr.addr[0].sa, + addr_len(c->peer_addr.addr[0]), + xforw + 17, IPLEN, NULL, 0, + NI_NUMERICHOST) == 0) { + strcat(xforw + 17, "\r\n"); + buffer_insert(c->ssl_buff, &last, &c->ssl_ptr, + c->buffsize, xforw); + } + /* last still points to the \r\n and ssl_ptr to the + * end of the buffer, so we may add as many headers + * as wee need to. + */ + } + } + } + else + c->ssl_ptr+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: diff -urN --exclude=.flxpkg stunnel-4.20-old/src/common.h stunnel-4.20-patched/src/common.h --- stunnel-4.20-old/src/common.h 2006-11-17 10:03:18 +0100 +++ stunnel-4.20-patched/src/common.h 2007-08-08 15:52:39 +0200 @@ -46,6 +46,9 @@ #endif /* OPTIMIZE_SCALABILITY */ +/* maximum space reserved for header insertion in BUFFSIZE */ +#define BUFF_RESERVED 1024 + /* Length of strings (including the terminating '\0' character) */ /* It can't be lower than 256 bytes or NTLM authentication will break */ #define STRLEN 256 diff -urN --exclude=.flxpkg stunnel-4.20-old/src/options.c stunnel-4.20-patched/src/options.c --- stunnel-4.20-old/src/options.c 2007-08-08 15:51:24 +0200 +++ stunnel-4.20-patched/src/options.c 2007-08-08 15:52:39 +0200 @@ -707,6 +707,29 @@ } #endif + /* xforwardedfor */ + switch(cmd) { + case CMD_INIT: + section->option.xforwardedfor=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "xforwardedfor")) + break; + if(!strcasecmp(arg, "yes")) + section->option.xforwardedfor=1; + else if(!strcasecmp(arg, "no")) + section->option.xforwardedfor=0; + else + return "argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + log_raw("%-15s = yes|no append an HTTP X-Forwarded-For header", + "xforwardedfor"); + break; + } + /* exec */ #ifndef USE_WIN32 switch(cmd) { diff -urN --exclude=.flxpkg stunnel-4.20-old/src/prototypes.h stunnel-4.20-patched/src/prototypes.h --- stunnel-4.20-old/src/prototypes.h 2007-08-08 15:51:24 +0200 +++ stunnel-4.20-patched/src/prototypes.h 2007-08-08 15:58:25 +0200 @@ -207,6 +207,7 @@ unsigned int accept:1; unsigned int remote:1; unsigned int retry:1; /* loop remote+program */ + unsigned int xforwardedfor:1; #ifndef USE_WIN32 unsigned int program:1; unsigned int pty:1; @@ -310,6 +311,8 @@ FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ s_poll_set fds; /* File descriptors */ + int buffsize; /* current buffer size, may be lower than BUFFSIZE */ + int crlf_seen; /* the number of successive CRLF seen */ } CLI; extern int max_clients;