Home » FreeBSD » stunnel 5.08 X-Forwarded-For patch (Tag: )

X-Forwarded-For patch (stunnel-4.35-xforwarded-for.diff) を適用した stunnel 4.35 を 5.08 にアップデートした際に、stunnel 5.02 X-Forwarded-For patch を一部参考にしつつ 5.08 向け patch を作成しました。

ソースファイルを展開したディレクトリを /usr/appli/ とし、文末に添付した patch を同ディレクトリに stunnel-5.08-xforwarded-for.diff として保存した場合、

cd /usr/appli/stunnel-5.08/
patch -p1 < stunnel-5.08-xforwarded-for.diff

上記を実行することで適用されます。

diff -ru stunnel-5.08.0/doc/stunnel.8 stunnel-5.08/doc/stunnel.8
--- stunnel-5.08.0/doc/stunnel.8    2014-11-16 00:25:48.000000000 +0900
+++ stunnel-5.08/doc/stunnel.8      2015-01-29 16:56:47.000000000 +0900
@@ -751,6 +751,10 @@
 .IP "\fBTIMEOUTidle\fR = \s-1SECONDS\s0" 4
 .IX Item "TIMEOUTidle = SECONDS"
 time to keep an idle connection
+.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 "\fBtransparent\fR = none | source | destination | both (Unix only)" 4
 .IX Item "transparent = none | source | destination | both (Unix only)"
 enable transparent proxy support on selected platforms
diff -ru stunnel-5.08.0/doc/stunnel.fr.8 stunnel-5.08/doc/stunnel.fr.8
--- stunnel-5.08.0/doc/stunnel.fr.8    2014-09-22 18:47:00.000000000 +0900
+++ stunnel-5.08/doc/stunnel.fr.8      2015-01-29 16:55:36.000000000 +0900
@@ -399,6 +399,10 @@
 .IP "\fBTIMEOUTidle\fR = secondes" 4
 .IX Item "TIMEOUTidle = secondes"
 Durée d'attente sur une connexion inactive
+.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 "\fBtransparent\fR = yes | no (Unix seulement)" 4
 .IX Item "transparent = yes | no (Unix seulement)"
 Mode mandataire transparent
diff -ru stunnel-5.08.0/src/client.c stunnel-5.08/src/client.c
--- stunnel-5.08.0/src/client.c    2014-11-20 16:52:45.000000000 +0900
+++ stunnel-5.08/src/client.c      2015-01-30 07:36:36.000000000 +0900
@@ -73,6 +73,14 @@
     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;
@@ -504,6 +512,28 @@
     s_log(LOG_DEBUG, "Peer certificate was cached (%d bytes)", len);
 }

+/* 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 */
 NOEXPORT void transfer(CLI *c) {
     int watchdog=0; /* a counter to detect an infinite loop */
@@ -527,7 +557,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;

@@ -536,7 +566,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 */
@@ -680,7 +710,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"))
@@ -700,7 +730,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;

@@ -767,12 +797,73 @@
                 (read_wants_write && ssl_can_wr)) {
             read_wants_read=0;
             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 -ru stunnel-5.08.0/src/common.h stunnel-5.08/src/common.h
--- stunnel-5.08.0/src/common.h    2014-10-28 22:10:39.000000000 +0900
+++ stunnel-5.08/src/common.h      2015-01-29 18:33:24.000000000 +0900
@@ -52,6 +52,12 @@
 /* I/O buffer size: 18432 (0x4800) 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 -ru stunnel-5.08.0/src/options.c stunnel-5.08/src/options.c
--- stunnel-5.08.0/src/options.c    2014-11-20 16:52:45.000000000 +0900
+++ stunnel-5.08/src/options.c      2015-01-29 18:50:04.000000000 +0900
@@ -1439,6 +1439,33 @@

 #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 -ru stunnel-5.08.0/src/prototypes.h stunnel-5.08/src/prototypes.h
--- stunnel-5.08.0/src/prototypes.h    2014-11-20 16:52:45.000000000 +0900
+++ stunnel-5.08/src/prototypes.h      2015-01-29 17:20:28.000000000 +0900
@@ -229,6 +229,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
@@ -467,6 +468,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);

Random Select

古瀬戸ブレンド
沖縄から東京に訪れた友達を カリーライス専門店 エチオピア に連れて行った後、まだ時間に余裕がある様でしたので、3 件程となりの 古瀬戸珈琲店 (コセトコーヒーテン) を訪れました。このお店はかねてか
2014/10/10 払い戻し (2)
09/19 – 09/24 はプライベートでは長期滞在に入る沖縄旅行でした。その回とは別に、去年 10 月に伊江島を訪れて 沖縄本島 (No. 6 – 打ち上げ) したメンバー
ハンバーグ
沖縄出張 (No. 11 ? 波布食堂) 程の量ではないけど、もう少し手前 (浦添寄り) であれば 軽食の店 ルビー より きょんたろう食堂 ですよ。という話を前回の出張 2013/01 で聞いていま
My Select (2013/05/20)
今日から 4 ヶ月振りの沖縄出張で 2 週間滞在します。05/01 から沖縄支店 (? てぃーだスクエア) で始まった てぃーだ社員食堂 に興味津々です。開始日からしばらくはブログや facebook
神田カレーグランプリ 2013 (2)
欧風カレー ボンディ (神保町本店) を後にし、東京名物神田古本まつり 開催の一部でもある神田すずらん通り商店街をゆったり散歩しつつ 神田カレーグランプリ の開催場所まで移動しました。まるで神田カレー
タコライス 材料 (OLD EL PASO タコ・シーズニング)
自宅 de タコライス (OLD EL PASO タコ・シーズニング Part 2) の続きです。沖縄本島 (No. 42 – 払い戻し) で年内最後の沖縄旅行が中止となったので、久しぶり
カレーライス (ハウス ザ・カリー) + あきたこまち (白米)
自宅 de カレーライス (ハウス ザ・カリー <辛口> Part 1) の続きです。出来上がりの猛烈に食欲をそそる香りの誘惑に負けず、1 日寝かしました。今回は白米のあきたこまちが残って
与那国空港 (5)
与那国島 (No. 14 – ビヤガーデン国境) の続きです。明けて 10/08 です。生まれて初めてのひとり旅も、後は帰るだけとなりました。小さな島とは言え、何回か来ないと分からないこと
FreeBSD Logo
FreeBSD kernel compile でも紹介した通り、常に kernel 再構築によりカスタムカーネルで稼動させています。私はカスタムカーネルの名称を `hostname -s` にする習慣
ビーフカレー
御茶ノ水・神田神保町界隈は、スポーツ用品店や楽器店にオフィスや大学が混在する不思議な場所ですが、実はカレー店も老舗が集まっています。昔はこの界隈に "バルチックカレー" という店舗
Valid HTML5 Valid CSS3 Another HTML Lint