corkscrewをSSLなプロキシに対応させた

Corkscrew は HTTP プロキシを使ったトンネリングで SSH を使うためのプログラムです。

あるとき HTTPS なウェブサーバの背後にあるプロキシサーバへセッションを張る必要があって、Corkscrew を SSL 対応にするためのパッチを作成しました。

通常の corkscrew の使い方に加えて、第一引数に `-ssl` を指定することで、プロキシサーバへSSLで接続します。

```
usage: corkscrew [-ssl] <proxyhost> <proxyport> <desthost> <destport> [authfile]
```

コードのパッチだけなので、ビルドのときには、

```
make LDADD="-lcrypto -lssl"
```

のように追加で SSL ライブラリを与える必要があります。

```diff
% diff -u corkscrew-2.0/corkscrew.c corkscrew-2.0/corkscrew.c 
--- corkscrew-2.0/corkscrew.c	2001-08-24 03:27:32.000000000 +0900
+++ corkscrew-2.0/corkscrew.c	2014-11-11 19:35:08.000000000 +0900
@@ -12,6 +12,12 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#define CHK_NULL(x) if ((x)==NULL) { fprintf(stderr, "NULL pointer\n"); exit(-1); }
+
 #if HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 #endif
@@ -27,7 +33,7 @@
 
 char *base64_encodei PARAMS((char *in));
 void usage PARAMS((void));
-int sock_connect PARAMS((const char *hname, int port));
+int sock_connect PARAMS((int fd, const char *hname, int port));
 int main PARAMS((int argc, char *argv[]));
 
 #define BUFSIZE 4096
@@ -132,21 +138,17 @@
 }
 
 #ifdef ANSI_FUNC
-int sock_connect (const char *hname, int port)
+int sock_connect (int fd, const char *hname, int port)
 #else
-int sock_connect (hname, port)
+int sock_connect (fd, hname, port)
+int fd;
 const char *hname;
 int port;
 #endif
 {
-	int fd;
 	struct sockaddr_in addr;
 	struct hostent *hent;
 
-	fd = socket(AF_INET, SOCK_STREAM, 0);
-	if (fd == -1)
-		return -1;
-
 	hent = gethostbyname(hname);
 	if (hent == NULL)
 		addr.sin_addr.s_addr = inet_addr(hname);
@@ -181,32 +183,52 @@
 	struct timeval tv;
 	ssize_t len;
 	FILE *fp;
+	int use_ssl = 0;
+	char **av;
+	int ac;
+	int n;
+	char *p;
+	SSL_METHOD *ssl_meth;
+	SSL_CTX* ctx;
+	SSL* ssl;
 
 	port = 80;
 
-	if ((argc == 5) || (argc == 6)) {
-		if (argc == 5) {
-			host = argv[1];
-			port = atoi(argv[2]);
-			desthost = argv[3];
-			destport = argv[4];
+	ac = argc - 1;
+	av = argv + 1;
+	if (ac >= 1 && strcmp(av[0], "-ssl") == 0) {
+		use_ssl = 1;
+		av++;
+		ac--;
+		port = 443;
+	}
+	if (ac >= 4) {
+		host = av[0];
+		port = atoi(av[1]);
+		desthost = av[2];
+		destport = av[3];
+		av += 4;
+		ac -= 4;
+		if (ac > 1) {
+			usage();
+			exit(-1);
 		}
-		if ((argc == 6)) {
-			host = argv[1];
-			port = atoi(argv[2]);
-			desthost = argv[3];
-			destport = argv[4];
-			fp = fopen(argv[5], "r");
+		if (ac == 1) {
+			fp = fopen(av[0], "r");
 			if (fp == NULL) {
-				fprintf(stderr, "Error opening %s: %s\n", argv[5], strerror(errno));
+				fprintf(stderr, "Error opening %s: %s\n", av[0], strerror(errno));
 				exit(-1);
-			} else {
-				char line[4096];
-				fscanf(fp, "%s", line);
-				up = malloc(sizeof(line));
-				up = line;
-				fclose(fp);
 			}
+			if (fgets(buffer, sizeof buffer - 1, fp)) {
+				len = strlen(buffer);
+				if (buffer[len - 1] == '\n')
+					buffer[len - 1] = '\0';
+				up = malloc(len + 1);
+				strcpy(up, buffer);
+			}
+			fclose(fp);
+			av++;
+			ac--;
 		}
 	} else {
 		usage();
@@ -218,18 +240,40 @@
 	strncat(uri, ":", sizeof(uri) - strlen(uri) - 1);
 	strncat(uri, destport, sizeof(uri) - strlen(uri) - 1);
 	strncat(uri, " HTTP/1.0", sizeof(uri) - strlen(uri) - 1);
-	if ((argc == 6) || (argc == 7)) {
-		strncat(uri, "\nProxy-Authorization: Basic ", sizeof(uri) - strlen(uri) - 1);
+	if (up && *up) {
+		strncat(uri, "\r\nProxy-Authorization: Basic ", sizeof(uri) - strlen(uri) - 1);
 		strncat(uri, base64_encode(up), sizeof(uri) - strlen(uri) - 1);
 	}
 	strncat(uri, linefeed, sizeof(uri) - strlen(uri) - 1);
 
-	csock = sock_connect(host, port);
-	if(csock == -1) {
+	csock = socket(AF_INET, SOCK_STREAM, 0);
+	if (csock == -1) {
+		fprintf(stderr, "Couldn't create socket: %s\n", strerror(errno));
+		exit(-1);
+	}
+	if (sock_connect(csock, host, port) < 0 &&
+	    errno && errno != EINPROGRESS) {
 		fprintf(stderr, "Couldn't establish connection to proxy: %s\n", strerror(errno));
 		exit(-1);
 	}
 
+	if (use_ssl) {
+		SSLeay_add_ssl_algorithms();
+		SSL_load_error_strings();
+		SSL_library_init();
+		ssl_meth = SSLv23_client_method();
+		ctx = SSL_CTX_new(ssl_meth);
+		CHK_NULL(ctx);
+		SSL_CTX_set_options(ctx,SSL_OP_ALL);
+		ssl = SSL_new(ctx);
+		CHK_NULL(ssl);
+		SSL_set_fd(ssl, csock);
+		if (SSL_connect(ssl) < 0) {
+			fprintf(stderr, "Couldn't establish connection to proxy: %s\n", strerror(errno));
+			exit(-1);
+		}
+	}
+
 	sent = 0;
 	setup = 0;
 	for(;;) {
@@ -249,7 +293,11 @@
 		/* there's probably a better way to do this */
 		if (setup == 0) {
 			if (FD_ISSET(csock, &rfd)) {
-				len = read(csock, buffer, sizeof(buffer));
+				if (use_ssl) {
+					len = SSL_read(ssl, buffer, sizeof(buffer));
+				} else {
+					len = read(csock, buffer, sizeof(buffer));
+				}
 				if (len<=0)
 					break;
 				else {
@@ -265,7 +313,11 @@
 				}
 			}
 			if (FD_ISSET(csock, &sfd) && (sent == 0)) {
-				len = write(csock, uri, strlen(uri));
+				if (use_ssl) {
+					len = SSL_write(ssl, uri, strlen(uri));
+				} else {
+					len = write(csock, uri, strlen(uri));
+				}
 				if (len<=0)
 					break;
 				else
@@ -273,7 +325,11 @@
 			}
 		} else {
 			if (FD_ISSET(csock, &rfd)) {
-				len = read(csock, buffer, sizeof(buffer));
+				if (use_ssl) {
+					len = SSL_read(ssl, buffer, sizeof(buffer));
+				} else {
+					len = read(csock, buffer, sizeof(buffer));
+				}
 				if (len<=0) break;
 				len = write(1, buffer, len);
 				if (len<=0) break;
@@ -282,10 +338,23 @@
 			if (FD_ISSET(0, &rfd)) {
 				len = read(0, buffer, sizeof(buffer));
 				if (len<=0) break;
-				len = write(csock, buffer, len);
+				if (use_ssl) {
+					len = SSL_write(ssl, buffer, len);
+				} else {
+					len = write(csock, buffer, len);
+				}
 				if (len<=0) break;
 			}
 		}
 	}
+	if (use_ssl) {
+		SSL_shutdown(ssl);
+		close(csock);
+		SSL_free(ssl);
+		SSL_CTX_free(ctx);
+	} else {
+		close(csock);
+	}
+
 	exit(0);
 }
```