Created
May 17, 2013 14:51
-
-
Save rraptorr/5599557 to your computer and use it in GitHub Desktop.
X-Forwarded-For stunnel 4.56 patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/doc/stunnel.8 b/doc/stunnel.8 | |
index 7624a27..3593f24 100644 | |
--- a/doc/stunnel.8 | |
+++ b/doc/stunnel.8 | |
@@ -753,6 +753,10 @@ This options has been renamed to \fInone\fR. | |
.RE | |
.RS 4 | |
.RE | |
+.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. | |
.IP "\fBverify\fR = level" 4 | |
.IX Item "verify = level" | |
verify peer certificate | |
diff --git a/src/client.c b/src/client.c | |
index ad92e2e..7f618e0 100644 | |
--- a/src/client.c | |
+++ b/src/client.c | |
@@ -48,6 +48,8 @@ | |
#define SHUT_RDWR 2 | |
#endif | |
+#define IPLEN 40 | |
+ | |
static void client_try(CLI *); | |
static void client_run(CLI *); | |
static void init_local(CLI *); | |
@@ -75,6 +77,12 @@ CLI *alloc_client_session(SERVICE_OPTIONS *opt, int rfd, int wfd) { | |
c=str_alloc(sizeof(CLI)); | |
str_detach(c); | |
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; | |
@@ -501,6 +509,28 @@ static void new_chain(CLI *c) { | |
} | |
#endif | |
+/* Moves all data from the buffer <buffer> between positions <start> and <stop> | |
+ * to insert <string> of length <len>. <start> and <stop> are updated to their | |
+ * new respective values, and the number of characters inserted is returned. | |
+ * If <len> is too long, nothing is done and -1 is returned. | |
+ * Note that neither <string> nor <buffer> 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)); | |
+} | |
+ | |
/****************************** transfer data */ | |
static void transfer(CLI *c) { | |
int watchdog=0; /* a counter to detect an infinite loop */ | |
@@ -519,7 +549,7 @@ static void transfer(CLI *c) { | |
do { /* main loop of client data transfer */ | |
/****************************** initialize *_wants_* */ | |
read_wants_read=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN) | |
- && c->ssl_ptr<BUFFSIZE && !read_wants_write; | |
+ && c->ssl_ptr<c->buffsize && !read_wants_write; | |
write_wants_write=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) | |
&& c->sock_ptr && !write_wants_read; | |
@@ -528,7 +558,7 @@ static void transfer(CLI *c) { | |
/* for plain socket open data strem = open file descriptor */ | |
/* make sure to add each open socket to receive exceptions! */ | |
if(sock_open_rd) /* only poll if the read file descriptor is open */ | |
- s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0); | |
+ s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<c->buffsize, 0); | |
if(sock_open_wr) /* only poll if the write file descriptor is open */ | |
s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr); | |
/* poll SSL file descriptors unless SSL shutdown was completed */ | |
@@ -683,7 +713,7 @@ static void transfer(CLI *c) { | |
/****************************** read from socket */ | |
if(sock_open_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: | |
if(parse_socket_error(c, "readsocket")) | |
@@ -720,7 +750,7 @@ static void transfer(CLI *c) { | |
/****************************** update *_wants_* based on new *_ptr */ | |
/* this update is also required for SSL_pending() to be used */ | |
read_wants_read=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN) | |
- && c->ssl_ptr<BUFFSIZE && !read_wants_write; | |
+ && c->ssl_ptr<c->buffsize && !read_wants_write; | |
write_wants_write=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) | |
&& c->sock_ptr && !write_wants_read; | |
@@ -730,12 +760,70 @@ static void transfer(CLI *c) { | |
* writesocket() above made some room in c->ssl_buff */ | |
(read_wants_write && ssl_can_wr)) { | |
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: | |
if(num==0) | |
s_log(LOG_DEBUG, "SSL_read returned 0"); | |
- 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) { | |
+ /* X-Forwarded-For: xxxx \r\n\0 */ | |
+ char xforw[17 + IPLEN + 3]; | |
+ | |
+ /* We have all the HTTP headers now. We don't need to | |
+ * reserve any space anymore. <ssl_ptr> points to the | |
+ * first byte of unread data, and <last> points to the | |
+ * exact location where we want to insert our headers, | |
+ * which is right before the empty line. | |
+ */ | |
+ c->buffsize = BUFFSIZE; | |
+ | |
+ /* 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.sa, | |
+ c->peer_addr_len, | |
+ 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 --git a/src/common.h b/src/common.h | |
index 0efdd7c..8c58f47 100644 | |
--- a/src/common.h | |
+++ b/src/common.h | |
@@ -52,6 +52,9 @@ | |
/* I/O buffer size - 18432 is the maximum size of SSL record payload */ | |
#define BUFFSIZE 18432 | |
+/* maximum space reserved for header insertion in BUFFSIZE */ | |
+#define BUFF_RESERVED 1024 | |
+ | |
/* how many bytes of random input to read from files for PRNG */ | |
/* OpenSSL likes at least 128 bits, so 64 bytes seems plenty. */ | |
#define RANDOM_BYTES 64 | |
diff --git a/src/options.c b/src/options.c | |
index 0a0e909..f3dff6d 100644 | |
--- a/src/options.c | |
+++ b/src/options.c | |
@@ -1032,6 +1032,33 @@ static char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section, | |
} | |
#endif | |
+ /* xforwardedfor */ | |
+ switch(cmd) { | |
+ case CMD_BEGIN: | |
+ 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_END: | |
+ break; | |
+ case CMD_FREE: | |
+ break; | |
+ case CMD_DEFAULT: | |
+ break; | |
+ case CMD_HELP: | |
+ s_log(LOG_NOTICE, "%-22s = yes|no append an HTTP X-Forwarded-For header", | |
+ "xforwardedfor"); | |
+ break; | |
+ } | |
+ | |
/* exec */ | |
switch(cmd) { | |
case CMD_BEGIN: | |
diff --git a/src/prototypes.h b/src/prototypes.h | |
index 0b02b54..393c3f7 100644 | |
--- a/src/prototypes.h | |
+++ b/src/prototypes.h | |
@@ -204,6 +204,7 @@ typedef struct service_options_struct { | |
unsigned int accept:1; /* endpoint: accept */ | |
unsigned int client:1; | |
unsigned int delayed_lookup:1; | |
+ unsigned int xforwardedfor:1; | |
#ifdef USE_LIBWRAP | |
unsigned int libwrap:1; | |
#endif | |
@@ -433,6 +434,8 @@ typedef struct { | |
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; | |
CLI *alloc_client_session(SERVICE_OPTIONS *, int, int); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
How do I change the "X-Forwarded-For" default name?