Created
October 10, 2012 18:27
-
-
Save rraptorr/3867499 to your computer and use it in GitHub Desktop.
X-Forwarded-For stunnel 4.54 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 b0204d6..7f46be0 100644 | |
--- a/doc/stunnel.8 | |
+++ b/doc/stunnel.8 | |
@@ -739,6 +739,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 5fa5d15..a34c74a 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 win_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) | |
- 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) | |
s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr); | |
/* for SSL assume that sockets are open if there any pending requests */ | |
@@ -655,7 +685,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")) | |
@@ -692,7 +722,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; | |
@@ -702,12 +732,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 4f85e2f..f9f1335 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 3073e3a..5b217c5 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 3181323..2a3cbdc 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