summaryrefslogtreecommitdiff
blob: 135260bdb9f6cf56d4e707cb8029fd5a9a3beb85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
diff -Naur stunnel-4.56.orig/src/client.c stunnel-4.56/src/client.c
--- stunnel-4.56.orig/src/client.c	2013-03-14 18:54:24.000000000 -0400
+++ stunnel-4.56/src/client.c	2013-06-16 11:24:39.000000000 -0400
@@ -75,6 +75,12 @@
     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 +507,28 @@
 }
 #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 +547,7 @@
     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 +556,7 @@
         /* 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 +711,7 @@
         /****************************** 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 +748,7 @@
         /****************************** 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 +758,73 @@
                  * 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) {
+                        /* 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;
+
+                        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.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 -Naur stunnel-4.56.orig/src/common.h stunnel-4.56/src/common.h
--- stunnel-4.56.orig/src/common.h	2013-03-13 09:41:57.000000000 -0400
+++ stunnel-4.56/src/common.h	2013-06-16 11:23:12.000000000 -0400
@@ -52,6 +52,12 @@
 /* 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
+
+/* IP address and TCP port textual representation length */
+#define IPLEN 128
+
 /* 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 -Naur stunnel-4.56.orig/src/options.c stunnel-4.56/src/options.c
--- stunnel-4.56.orig/src/options.c	2013-06-16 11:17:49.000000000 -0400
+++ stunnel-4.56/src/options.c	2013-06-16 11:23:12.000000000 -0400
@@ -1032,6 +1032,29 @@
     }
 #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_DEFAULT:
+        break;
+    case CMD_HELP:
+        s_log(LOG_NOTICE, "%-15s = yes|no append an HTTP X-Forwarded-For header",
+            "xforwardedfor");
+        break;
+    }
+
     /* exec */
     switch(cmd) {
     case CMD_BEGIN:
diff -Naur stunnel-4.56.orig/src/prototypes.h stunnel-4.56/src/prototypes.h
--- stunnel-4.56.orig/src/prototypes.h	2013-06-16 11:17:49.000000000 -0400
+++ stunnel-4.56/src/prototypes.h	2013-06-16 11:23:12.000000000 -0400
@@ -205,6 +205,7 @@
         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
@@ -434,6 +435,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;
 
 CLI *alloc_client_session(SERVICE_OPTIONS *, int, int);