[Ttssh2-commit] [6846] Dynamic Forwarding (SOCKS Proxy) に対応。

svnno****@sourc***** svnno****@sourc*****
2017年 7月 5日 (水) 00:03:01 JST


Revision: 6846
          http://sourceforge.jp/projects/ttssh2/scm/svn/commits/6846
Author:   doda
Date:     2017-07-05 00:03:01 +0900 (Wed, 05 Jul 2017)
Log Message:
-----------
Dynamic Forwarding (SOCKS Proxy) に対応。

/ssh-D1080 でポート 1080 で SOCKS の待ち受けを行う

ToDo:
・SSH 転送ダイアログでの設定への対応 (誰かやって……)
・IE で利用すると SSH チャネルが足りなくなったりする……

Modified Paths:
--------------
    trunk/ttssh2/ttxssh/fwd.c
    trunk/ttssh2/ttxssh/fwd.h
    trunk/ttssh2/ttxssh/fwdui.c
    trunk/ttssh2/ttxssh/ttxssh.c
    trunk/ttssh2/ttxssh/ttxssh.vcproj

Added Paths:
-----------
    trunk/ttssh2/ttxssh/fwd-socks.c
    trunk/ttssh2/ttxssh/fwd-socks.h

-------------- next part --------------
Added: trunk/ttssh2/ttxssh/fwd-socks.c
===================================================================
--- trunk/ttssh2/ttxssh/fwd-socks.c	                        (rev 0)
+++ trunk/ttssh2/ttxssh/fwd-socks.c	2017-07-04 15:03:01 UTC (rev 6846)
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2017 TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ttxssh.h"
+#include "fwd.h"
+#include "ttcommon.h"
+
+// 4 + 1 + 255 + 2
+#define SOCKS_REQUEST_MAXLEN 262
+
+typedef enum {
+	SOCKS_STATE_INIT,
+	SOCKS_STATE_INITREPLY,
+	SOCKS_STATE_AUTHREPLY,
+	SOCKS_STATE_REQUESTSENT
+} DynamicFwdState;
+
+#define SOCKS4_COMMAND_CONNECT   0x01
+#define SOCKS4_COMMAND_BIND      0x02
+
+#define SOCKS4_RESULT_OK         0x5a
+#define SOCKS4_RESULT_NG         0x5b
+#define SOCKS4_RESULT_NOIDENTD   0x5c
+#define SOCKS4_RESULT_IDENTDERR  0x5d
+
+#define SOCKS5_COMMAND_CONNECT   0x01
+#define SOCKS5_COMMAND_BIND      0x02
+#define SOCKS5_COMMAND_UDP       0x03
+
+#define SOCKS5_AUTH_NONE         0x00
+#define SOCKS5_AUTH_GSSAPI       0x00
+#define SOCKS5_AUTH_USERPASS     0x00
+#define SOCKS5_AUTH_NOACCEPTABLE 0xff
+
+#define SOCKS5_ADDRTYPE_IPV4     0x01
+#define SOCKS5_ADDRTYPE_DOMAIN   0x03
+#define SOCKS5_ADDRTYPE_IPV6     0x04
+
+#define SOCKS5_OPEN_CONFIRM      128
+#define SOCKS5_ERROR_COMMAND     129
+#define SOCKS5_ERROR_ADDRTYPE    130
+
+typedef struct {
+	PTInstVar pvar;
+
+	int channel_num;
+
+	char *peer_name;
+	int peer_port;
+
+	DynamicFwdState status;
+	unsigned int socks_ver;
+	unsigned char request_buf[SOCKS_REQUEST_MAXLEN];
+	unsigned int buflen;
+} FWDDynamicFilterClosure;
+
+void *SOCKS_init_filter(PTInstVar pvar, int channel_num, char *peer_name, int port)
+{
+	FWDDynamicFilterClosure *closure = malloc(sizeof(FWDDynamicFilterClosure));
+
+	if (closure == NULL) {
+		logputs(LOG_LEVEL_ERROR, __FUNCTION__ ": Can't allocate memory for closure.");
+		return NULL;
+	}
+
+	closure->pvar = pvar;
+	closure->channel_num = channel_num;
+	closure->peer_name = strdup(peer_name);
+	closure->peer_port = port;
+
+	closure->status = SOCKS_STATE_INIT;
+	closure->socks_ver = 0;
+	closure->request_buf[0] = 0;
+	closure->buflen = 0;
+
+	return closure;
+}
+
+void ClearRemoteConnectFlag(FWDDynamicFilterClosure *closure)
+{
+	PTInstVar pvar = closure->pvar;
+	FWDChannel *c = pvar->fwd_state.channels + closure->channel_num;
+
+	c->status &= ~FWD_REMOTE_CONNECTED;
+}
+
+// \x83_\x83~\x81[\x82̃u\x83\x8D\x83b\x83L\x83\x93\x83O\x8F\x91\x82\xAB\x8D\x9E\x82ݗp\x8A֐\x94
+// \x92ʏ\xED\x82͔񓯊\x{23D10AB}\x8D\x9E\x82݂ŏ\x88\x97\x9D\x82ł\xAB\x82\xE9\x82͂\xB8\x82Ȃ̂ŁA\x83u\x83\x8D\x83b\x83L\x83\x93\x83O\x8F\x91\x82\xAB\x8D\x9E\x82݂ɗ\x8E\x82\xBF\x82\xBD\x8E\x9E\x93_\x82ŃG\x83\x89\x81[\x82Ƃ\xB7\x82\xE9
+static BOOL dummy_blocking_write(PTInstVar pvar, SOCKET s, const char *data, int length)
+{
+        return FALSE;
+}
+
+static int send_socks_reply(FWDDynamicFilterClosure *closure, const char *data, int len)
+{
+	PTInstVar pvar = closure->pvar;
+	FWDChannel *c = pvar->fwd_state.channels + closure->channel_num;
+
+	logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": sending %d bytes.", len);
+
+	return UTIL_sock_buffered_write(pvar, &c->writebuf, dummy_blocking_write, c->local_socket, data, len);
+}
+
+send_socks4_reply(FWDDynamicFilterClosure *closure, int code) {
+	unsigned char buff[] = {
+		0, // NUL
+		SOCKS4_RESULT_NG, // status
+		0, 0, // field 3
+		0, 0, 0, 0 // field 4
+	};
+
+	if (code >= SOCKS4_RESULT_OK) {
+		buff[1] = SOCKS4_RESULT_OK;
+	}
+
+	send_socks_reply(closure, buff, sizeof(buff));
+}
+
+static void send_socks5_open_success(FWDDynamicFilterClosure *closure)
+{
+	unsigned char buff[] = {
+		5, // version
+		0, // status -- success
+		0, // reserved
+		1, // addr-type (for dummy) -- IPv4
+		0, 0, 0, 0, // dummy address -- \x83T\x81[\x83o\x82\xAA BIND Address \x82\xF0\x92ʒm\x82\xB5\x82Ă\xAD\x82\xEA\x82Ȃ\xA2\x82̂\xC5 orz
+		0, 0 // dummy port number
+	};
+
+	send_socks_reply(closure, buff, sizeof(buff));
+}
+
+static void send_socks5_open_failure(FWDDynamicFilterClosure *closure, int reason)
+{
+	unsigned char buff[] = {
+		5, // version
+		1, // status -- general failure
+		0, // reserved
+		1, // addr-type (for dummy) -- IPv4
+		0, 0, 0, 0, // dummy address -- \x90ڑ\xB1\x8Fo\x97\x88\x82Ă\xA2\x82Ȃ\xA2\x82񂾂\xA9\x82\xE7 BIND Address \x82\xE0\x89\xBD\x82\xE0\x96\xB3\x82\xA2\x82\xBE\x82\xEB
+		0, 0 // dummy port number
+	};
+
+	switch (reason) {
+	case 1: // SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
+		buff[1] = 0x02; // connection not allowed
+		break;
+	case 2: // SSH_OPEN_CONNECT_FAILED
+		buff[1] = 0x04; // Host unreachable -- \x90ڑ\xB1\x8Fo\x97\x88\x82Ȃ\xA2\x97\x9D\x97R\x82͐F\x81X\x82\xA0\x82邯\x82\xEA\x82ǁA\x82Ƃ肠\x82\xA6\x82\xB8\x82\xB1\x82\xEA\x82\xC5
+		break;
+	case 3: // SSH_OPEN_UNKNOWN_CHANNEL_TYPE
+		buff[1] = 0x01; // general failure
+		break;
+	case 4: // SSH_OPEN_RESOURCE_SHORTAGE
+		buff[1] = 0x01; // general failure
+		break;
+	case SOCKS5_ERROR_COMMAND:
+		buff[1] = 0x07; // command not supported
+		break;
+	case SOCKS5_ERROR_ADDRTYPE:
+		buff[1] = 0x08; // address not supported
+		break;
+	}
+
+	send_socks_reply(closure, buff, sizeof(buff));
+}
+
+struct socks4_header {
+	unsigned char proto;
+	unsigned char command;
+	unsigned char port[2];
+	unsigned char addr[4];
+};
+
+int parse_socks4_request(FWDDynamicFilterClosure *closure, unsigned char *buff, unsigned int bufflen)
+{
+	struct socks4_header s4hdr;
+	unsigned char addrbuff[NI_MAXHOST];
+	unsigned char pname[NI_MAXSERV];
+	unsigned char *user, *addr;
+	int port;
+
+	if (bufflen < 8) {
+		return 0;
+	}
+
+	memcpy_s(&s4hdr, sizeof(s4hdr), buff, 8);
+
+	// CONNECT \x82̂ݑΉ\x9E
+	if (s4hdr.proto != 4 || s4hdr.command != SOCKS4_COMMAND_CONNECT) {
+		send_socks4_reply(closure, SOCKS4_RESULT_NG);
+		return -1;
+	}
+
+	// skip socks header
+	buff += 8;
+	bufflen -= 8;
+
+	user = buff;
+
+	while (bufflen > 0 && *buff != 0) {
+		bufflen--; buff++;
+	}
+
+	if (bufflen == 0) {
+		// NUL terminate \x82\xB3\x82\xEA\x82ĂȂ\xA2 -> \x83\x8A\x83N\x83G\x83X\x83g\x82\xAA\x82܂\xBE\x93r\x92\x86
+		return 0;
+	}
+
+	// skip NUL
+	buff++;
+	bufflen--;
+
+	port = s4hdr.port[0] * 256 + s4hdr.port[1];
+
+	if (s4hdr.addr[0] == 0 && s4hdr.addr[1] == 0 && s4hdr.addr[2] == 0 && s4hdr.addr[3] != 0) {
+		// SOCKS4a
+		addr = buff;
+		while (bufflen > 0 && *buff != 0) {
+			bufflen--; buff++;
+		}
+		if (bufflen == 0) {
+			// NUL terminate \x82\xB3\x82\xEA\x82ĂȂ\xA2 -> \x83\x8A\x83N\x83G\x83X\x83g\x82\xAA\x82܂\xBE\x93r\x92\x86
+			return 0;
+		}
+	}
+	else {  // SOCKS4
+		struct sockaddr_in saddr4;
+
+		memset(&saddr4, 0, sizeof(saddr4));
+		saddr4.sin_family = AF_INET;
+		memcpy_s(&(saddr4.sin_addr), sizeof(saddr4.sin_addr), s4hdr.addr, 4);
+		getnameinfo((struct sockaddr *)&saddr4, sizeof(saddr4), addrbuff, sizeof(addrbuff),
+			pname, sizeof(pname), NI_NUMERICHOST | NI_NUMERICSERV);
+
+		addr = addrbuff;
+	}
+
+	// \x83T\x81[\x83o\x82ɗv\x8B\x81\x82𑗂\xE9\x91O\x82ɁA\x83t\x83\x89\x83O\x82\xF0\x96{\x97\x88\x82̏\xF3\x91Ԃɖ߂\xB5\x82Ă\xA8\x82\xAD
+	ClearRemoteConnectFlag(closure);
+
+	closure->socks_ver = 4;
+
+	SSH_open_channel(closure->pvar, closure->channel_num, addr, port, closure->peer_name, closure->peer_port);
+
+	closure->status = SOCKS_STATE_REQUESTSENT;
+	return 1;
+}
+
+int parse_socks5_init_request(FWDDynamicFilterClosure *closure, unsigned char *buff, unsigned int bufflen)
+{
+	unsigned int authmethod_count;
+	unsigned int i;
+	unsigned char reply_buff[2] = { 5, SOCKS5_AUTH_NOACCEPTABLE };
+	PTInstVar pvar = closure->pvar;
+	FWDChannel *channel = pvar->fwd_state.channels + closure->channel_num;
+
+	if (bufflen < 2) {
+		return 0;
+	}
+
+	if (buff[0] != 5) {
+		// protocol version missmatch
+		return -1;
+	}
+
+	authmethod_count = buff[1];
+
+	if (bufflen < authmethod_count + 2) {
+		return 0;
+	}
+
+	for (i=0; i<authmethod_count; i++) {
+		if (buff[i+2] == SOCKS5_AUTH_NONE) {
+			// \x8C\xBB\x8F\xF3\x82ł͔F\x8F؂Ȃ\xB5\x82̂݃T\x83|\x81[\x83g
+			closure->socks_ver = 5;
+			reply_buff[1] = SOCKS5_AUTH_NONE;
+			send_socks_reply(closure, reply_buff, 2);
+
+			closure->status = SOCKS_STATE_AUTHREPLY;
+			closure->buflen = 0;
+			return 1;
+		}
+	}
+	send_socks_reply(closure, reply_buff, 2);
+	return -1;
+}
+
+struct socks5_header {
+	unsigned char proto;
+	unsigned char command;
+	unsigned char reserved;
+	unsigned char addr_type;
+	unsigned char addr_len;
+};
+
+int parse_socks5_connect_request(FWDDynamicFilterClosure *closure, unsigned char *buff, unsigned int bufflen)
+{
+	struct socks5_header s5hdr;
+	unsigned char addr[NI_MAXHOST];
+	unsigned char pname[NI_MAXSERV];
+	int port;
+	unsigned int reqlen, addrlen;
+
+	if (bufflen < 5) {
+		return 0;
+	}
+	memcpy_s(&s5hdr, sizeof(s5hdr), buff, 5);
+
+	switch (s5hdr.addr_type) {
+		case SOCKS5_ADDRTYPE_IPV4:
+			addrlen = 4;
+			break;
+		case SOCKS5_ADDRTYPE_DOMAIN:
+			addrlen = s5hdr.addr_len + 1;
+			break;
+		case SOCKS5_ADDRTYPE_IPV6:
+			addrlen = 16;
+			break;
+		default: // Invalid address type
+			send_socks5_open_failure(closure, SOCKS5_ERROR_ADDRTYPE);
+			return -1;
+	}
+
+	reqlen = 4 + addrlen + 2;
+
+	if (bufflen < reqlen) {
+		return 0;
+	}
+
+	if (s5hdr.proto != 5 || s5hdr.reserved != 0) {
+		return -1;
+	}
+
+	if (s5hdr.command != SOCKS5_COMMAND_CONNECT) {
+		// CONNECT \x88ȊO\x82͖\xA2\x91Ή\x9E
+		send_socks5_open_failure(closure, SOCKS5_ERROR_COMMAND);
+		return -1;
+	}
+
+	switch (s5hdr.addr_type) {
+		case SOCKS5_ADDRTYPE_IPV4: {
+			struct sockaddr_in saddr4;
+
+			memset(&saddr4, 0, sizeof(saddr4));
+			saddr4.sin_family = AF_INET;
+			memcpy_s(&(saddr4.sin_addr), sizeof(saddr4.sin_addr), &buff[4], addrlen);
+			getnameinfo((struct sockaddr *)&saddr4, sizeof(saddr4), addr, sizeof(addr),
+				pname, sizeof(pname), NI_NUMERICHOST | NI_NUMERICSERV);
+			break;
+		}
+
+		case SOCKS5_ADDRTYPE_DOMAIN:
+			if (s5hdr.addr_len > sizeof(addr) - 1 ) {
+				return -1;
+			}
+			memcpy_s(addr, sizeof(addr), &buff[5], s5hdr.addr_len);
+			addr[s5hdr.addr_len] = 0;
+			break;
+
+		case SOCKS5_ADDRTYPE_IPV6: {
+			struct sockaddr_in6 saddr6;
+
+			memset(&saddr6, 0, sizeof(saddr6));
+			saddr6.sin6_family = AF_INET6;
+			memcpy_s(&(saddr6.sin6_addr), sizeof(saddr6.sin6_addr), &buff[4], addrlen);
+			getnameinfo((struct sockaddr *)&saddr6, sizeof(saddr6), addr, sizeof(addr),
+				pname, sizeof(pname), NI_NUMERICHOST | NI_NUMERICSERV);
+			break;
+		}
+	}
+
+	port = buff[4 + addrlen] * 256 + buff[4 + addrlen + 1];
+
+	// \x83T\x81[\x83o\x82ɗv\x8B\x81\x82𑗂\xE9\x91O\x82ɁA\x83t\x83\x89\x83O\x82\xF0\x96{\x97\x88\x82̏\xF3\x91Ԃɖ߂\xB5\x82Ă\xA8\x82\xAD
+	ClearRemoteConnectFlag(closure);
+
+	SSH_open_channel(closure->pvar, closure->channel_num, addr, port, closure->peer_name, closure->peer_port);
+	closure->status = SOCKS_STATE_REQUESTSENT;
+
+	return 1;
+}
+
+FwdFilterResult parse_client_request(FWDDynamicFilterClosure *closure, int *len, unsigned char **buf)
+{
+	unsigned char *request = closure->request_buf;
+	unsigned int reqlen, newlen;
+	int result = 0;
+
+	newlen = closure->buflen + *len;
+
+	if (newlen > SOCKS_REQUEST_MAXLEN || *len < 0) {
+		// \x83\x8A\x83N\x83G\x83X\x83g\x82\xAA\x91傫\x82\xB7\x82\xAC\x82\xE9\x8Fꍇ\x82͐ؒf\x82\xB7\x82\xE9
+		logprintf(LOG_LEVEL_ERROR, __FUNCTION__
+			": request too large: state=%d, buflen=%d, reqlen=%d",
+			closure->status, closure->buflen, *len);
+		return FWD_FILTER_CLOSECHANNEL;
+	}
+
+	memcpy_s(closure->request_buf + closure->buflen,
+		sizeof(closure->request_buf) - closure->buflen,
+		*buf, *len);
+	closure->buflen = newlen;
+	request = closure->request_buf;
+	reqlen = closure->buflen;
+
+	// \x83e\x83X\x83g\x82Ȃ̂ŁA\x8C㑱\x82̃f\x81[\x83^\x82\xAA\x82\xA0\x82\xC1\x82Ă\xE0\x96\xB3\x8E\x8B\x82\xB5\x82đS\x82ď\xC1\x82\xB7
+	**buf = 0; *len = 0;
+
+	switch (closure->status) {
+	case SOCKS_STATE_INIT:
+		if (request[0] == 4) {
+			result = parse_socks4_request(closure, request, reqlen);
+		}
+		else if (request[0] == 5) {
+			result = parse_socks5_init_request(closure, request, reqlen);
+		}
+		else {
+			// Invalid request
+			logprintf(LOG_LEVEL_ERROR, __FUNCTION__ ": Invalid request. protocol-version=%d", buf[0]);
+			result = -1;
+		}
+		break;
+	case SOCKS_STATE_AUTHREPLY:
+		if (request[0] == 5) {
+			result = parse_socks5_connect_request(closure, request, reqlen);
+		}
+		else {
+			// Invalid request
+			logprintf(LOG_LEVEL_ERROR, __FUNCTION__ ": Invalid request. protocol-version=%d", buf[0]);
+			result = -1;
+		}
+		break;
+	default:
+		// NOT REACHED
+		break;
+	}
+
+
+	if (result < 0) {
+		// \x83t\x83\x89\x83O\x82\xF0\x96{\x97\x88\x82̏\xF3\x91\xD4(\x83\x8A\x83\x82\x81[\x83g\x96\xA2\x90ڑ\xB1)\x82ɖ߂\xB7
+		// \x82\xB1\x82\xEA\x82\xF0\x82\xE2\x82\xC1\x82Ă\xA8\x82\xA9\x82Ȃ\xA2\x82ƁA\x96\xA2\x90ڑ\xB1\x82̃`\x83\x83\x83l\x83\x8B\x82\xF0\x95‚\xB6\x82悤\x82Ƃ\xB5\x82Ă\xA8\x82\xA9\x82\xB5\x82\xAD\x82Ȃ\xE9
+		ClearRemoteConnectFlag(closure);
+
+		return FWD_FILTER_CLOSECHANNEL;
+	}
+
+	return FWD_FILTER_RETAIN;
+}
+
+FwdFilterResult SOCKS_filter(void *void_closure, FwdFilterEvent event, int *len, unsigned char **buf)
+{
+	FWDDynamicFilterClosure *closure = (FWDDynamicFilterClosure *)void_closure;
+
+	if (closure == NULL) {
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": closure does not available. event=%d", event);
+		return FWD_FILTER_REMOVE;
+	}
+
+	switch (event) {
+	case FWD_FILTER_CLEANUP:
+		// FWD_FILTER_REMOVE \x82\xF0\x95Ԃ\xB7\x82ƁA\x83\x8A\x83\\x81[\x83X\x8AJ\x95\xFA\x82ׂ̈ɂ\xB1\x82\xEA\x82ōēx\x8CĂ΂\xEA\x82\xE9
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": closure cleanup. channel=%d", closure->channel_num);
+		free(closure->peer_name);
+		free(closure);
+		return FWD_FILTER_REMOVE;
+
+	case FWD_FILTER_OPENCONFIRM:
+		// SSH_open_channel() \x82\xAA\x90\xAC\x8C\xF7
+		logputs(LOG_LEVEL_VERBOSE, __FUNCTION__ ": OpenConfirmation received");
+		if (closure->socks_ver == 4) {
+			send_socks4_reply(closure, SOCKS4_RESULT_OK);
+		}
+		else if (closure->socks_ver == 5) {
+			send_socks5_open_success(closure);
+		}
+		else {
+			logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": protocol version missmatch. version=%d", closure->socks_ver);
+		}
+		return FWD_FILTER_REMOVE;
+
+	case FWD_FILTER_OPENFAILURE:
+		// SSH_open_channel() \x82\xAA\x8E\xB8\x94s
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": Open Failure. reason=%d", *len);
+		if (closure->socks_ver == 4) {
+			send_socks4_reply(closure, SOCKS4_RESULT_NG);
+		}
+		else if (closure->socks_ver == 5) {
+			send_socks5_open_failure(closure, *len);
+		}
+		else {
+			logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": protocol version missmatch. version=%d", closure->socks_ver);
+		}
+		return FWD_FILTER_CLOSECHANNEL;
+
+	case FWD_FILTER_FROM_SERVER:
+		// \x82\xB1\x82̃t\x83B\x83\x8B\x83^\x82\xAA\x97L\x8C\xF8\x82Ȏ\x9E\x93_\x82ł̓T\x81[\x83o\x82ւ̃`\x83\x83\x83l\x83\x8B\x82͊J\x82\xA2\x82Ă\xA2\x82Ȃ\xA2\x82̂\xC5
+		// \x82\xB1\x82\xB1\x82ɂ͂\xB1\x82Ȃ\xA2\x82͂\xB8
+		logputs(LOG_LEVEL_VERBOSE, __FUNCTION__ ": data received from server. (bug?)");
+		return FWD_FILTER_RETAIN;
+
+	case FWD_FILTER_FROM_CLIENT:
+		// \x83N\x83\x89\x83C\x83A\x83\x93\x83g\x82\xA9\x82\xE7\x82̗v\x8B\x81\x82\xF0\x8F\x88\x97\x9D\x82\xB7\x82\xE9
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": data received from client. size=%d", *len);
+		return parse_client_request(closure, len, buf);
+	}
+
+	// NOT REACHED
+	return FWD_FILTER_RETAIN;
+}

Added: trunk/ttssh2/ttxssh/fwd-socks.h
===================================================================
--- trunk/ttssh2/ttxssh/fwd-socks.h	                        (rev 0)
+++ trunk/ttssh2/ttxssh/fwd-socks.h	2017-07-04 15:03:01 UTC (rev 6846)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ttxssh.h"
+#include "ttcommon.h"
+
+void *SOCKS_init_filter(PTInstVar pvar, int channel_num, char *peer_name, int port);
+FwdFilterResult SOCKS_filter(void *closure, FwdFilterEvent event, int *len, unsigned char **buf);

Modified: trunk/ttssh2/ttxssh/fwd.c
===================================================================
--- trunk/ttssh2/ttxssh/fwd.c	2017-07-04 15:02:57 UTC (rev 6845)
+++ trunk/ttssh2/ttxssh/fwd.c	2017-07-04 15:03:01 UTC (rev 6846)
@@ -33,11 +33,9 @@
 */
 
 #include "ttxssh.h"
-
 #include "x11util.h"
-
 #include "fwd.h"
-
+#include "fwd-socks.h"
 #include "ttcommon.h"
 
 #include <assert.h>
@@ -590,9 +588,20 @@
 	channel_opening_error(pvar, channel_num, WSAGetLastError());
 }
 
-static void accept_local_connection(PTInstVar pvar, int request_num,
-                                    int listening_socket_num)
+static char *dump_fwdchannel(FWDChannel *c)
 {
+	static char buff[1024];
+
+	_snprintf_s(buff, sizeof(buff), _TRUNCATE,
+		"status=%d, channel: local=%d, remote=%d, socket: %s, filter: %s",
+		c->status, c->request_num, c->remote_num, (c->local_socket != INVALID_SOCKET) ? "ok" : "invalid",
+		(c->filter != NULL) ? "on" : "off");
+
+	return buff;
+}
+
+static void accept_local_connection(PTInstVar pvar, int request_num, int listening_socket_num)
+{
 	int channel_num;
 	SOCKET s;
 	struct sockaddr_storage addr;
@@ -604,34 +613,46 @@
 	FWDRequest *request = &pvar->fwd_state.requests[request_num];
 	BOOL is_localhost = FALSE;
 
-	s = accept(request->listening_sockets[listening_socket_num],
-	           (struct sockaddr *) &addr, &addrlen);
+	s = accept(request->listening_sockets[listening_socket_num], (struct sockaddr *) &addr, &addrlen);
 	if (s == INVALID_SOCKET)
 		return;
 
 	// SSH2 port-forwarding\x82ɐڑ\xB1\x8C\xB3\x82̃\x8A\x83\x82\x81[\x83g\x83|\x81[\x83g\x82\xAA\x95K\x97v\x81B(2005.2.27 yutaka)
-	if (getnameinfo
-	    ((struct sockaddr *) &addr, addrlen, hname, sizeof(hname),
-	     strport, sizeof(strport), NI_NUMERICHOST | NI_NUMERICSERV)) {
+	if (getnameinfo((struct sockaddr *) &addr, addrlen, hname, sizeof(hname),
+	                strport, sizeof(strport), NI_NUMERICHOST | NI_NUMERICSERV)) {
 		/* NOT REACHED */
 	}
 	port = atoi(strport);
 
-	logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__
-	          ": Host %s(%d) connecting to port %d; forwarding to %s:%d",
-	          hname, port, request->spec.from_port, request->spec.to_host,
-	          request->spec.to_port);
-
 	channel_num = alloc_channel(pvar, FWD_LOCAL_CONNECTED, request_num);
 	channel = pvar->fwd_state.channels + channel_num;
 
 	channel->local_socket = s;
-	channel->filter_closure = NULL;
-	channel->filter = NULL;
 
-	// add originator-port (2005.2.27 yutaka)
-	SSH_open_channel(pvar, channel_num, request->spec.to_host,
-	                 request->spec.to_port, hname, port);
+	if (request->spec.type == FWD_LOCAL_TO_REMOTE) {
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__
+		          ": Host %s(%d) connecting to port %d; forwarding to %s:%d; type=LtoR",
+		          hname, port, request->spec.from_port, request->spec.to_host, request->spec.to_port);
+
+		channel->filter_closure = NULL;
+		channel->filter = NULL;
+		SSH_open_channel(pvar, channel_num, request->spec.to_host,
+		                 request->spec.to_port, hname, port);
+	}
+	else { // FWD_LOCAL_DYNAMIC
+		logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__
+		          ": Host %s(%d) connecting to port %d; type=dynamic",
+		          hname, port, request->spec.from_port);
+
+		// SOCKS \x82̃\x8A\x83N\x83G\x83X\x83g\x82\xF0\x8F\x88\x97\x9D\x82\xB7\x82\xE9\x88ׂ\xCC filter \x82\xF0\x93o\x98^
+		channel->filter_closure = SOCKS_init_filter(pvar, channel_num, hname, port);
+		channel->filter = SOCKS_filter;
+
+		// \x83\x8A\x83\x82\x81[\x83g\x91\xA4\x82͂܂\xBE\x8Cq\x82\xAA\x82\xC1\x82Ă\xA2\x82Ȃ\xA2\x82\xAA\x81Aread_local_connection() \x93\x99\x82ŏ\x88\x97\x9D\x82\xAA\x8Ds\x82\xED\x82\xEA\x82\xE9\x82悤\x82Ƀt\x83\x89\x83O\x82𗧂Ă\xE9\x81B
+		channel->status |= FWD_BOTH_CONNECTED;
+
+	}
+	logprintf(150, __FUNCTION__ ": channel info: %s", dump_fwdchannel(channel));
 }
 
 static void write_local_connection_buffer(PTInstVar pvar, int channel_num)
@@ -650,6 +671,8 @@
 {
 	FWDChannel *channel = pvar->fwd_state.channels + channel_num;
 
+	logprintf(LOG_LEVEL_VERBOSE, __FUNCTION__ ": channel=%d", channel_num);
+
 	if ((channel->status & FWD_BOTH_CONNECTED) != FWD_BOTH_CONNECTED) {
 		return;
 	}
@@ -909,6 +932,7 @@
 
 	switch (spec->type) {
 	case FWD_LOCAL_TO_REMOTE:
+	case FWD_LOCAL_DYNAMIC:
 		return TRUE;
 	default:
 		listener =
@@ -997,7 +1021,7 @@
 {
 	FWDRequest *request = pvar->fwd_state.requests + request_num;
 
-	if (request->spec.type == FWD_LOCAL_TO_REMOTE) {
+	if (request->spec.type == FWD_LOCAL_TO_REMOTE || request->spec.type == FWD_LOCAL_DYNAMIC) {
 		struct addrinfo hints;
 		struct addrinfo *res;
 		struct addrinfo *res0;
@@ -1107,6 +1131,7 @@
 	  case FWD_LOCAL_TO_REMOTE: ftype = "LtoR"; break;
 	  case FWD_REMOTE_TO_LOCAL: ftype = "RtoL"; break;
 	  case FWD_REMOTE_X11_TO_LOCAL: ftype = "X11"; bind_addr = ftype; break;
+	  case FWD_LOCAL_DYNAMIC: ftype="dynamic"; break;
 	  default: ftype = "Unknown"; break;
 	}
 

Modified: trunk/ttssh2/ttxssh/fwd.h
===================================================================
--- trunk/ttssh2/ttxssh/fwd.h	2017-07-04 15:02:57 UTC (rev 6845)
+++ trunk/ttssh2/ttxssh/fwd.h	2017-07-04 15:03:01 UTC (rev 6846)
@@ -78,7 +78,7 @@
 
 /* Request types */
 typedef enum {
-	FWD_NONE, FWD_LOCAL_TO_REMOTE, FWD_REMOTE_TO_LOCAL, FWD_REMOTE_X11_TO_LOCAL
+	FWD_NONE, FWD_LOCAL_TO_REMOTE, FWD_REMOTE_TO_LOCAL, FWD_REMOTE_X11_TO_LOCAL, FWD_LOCAL_DYNAMIC
 } FWDType;
 
 /* If 'type' is FWD_REMOTE_X11_TO_LOCAL, then from_port must be

Modified: trunk/ttssh2/ttxssh/fwdui.c
===================================================================
--- trunk/ttssh2/ttxssh/fwdui.c	2017-07-04 15:02:57 UTC (rev 6845)
+++ trunk/ttssh2/ttxssh/fwdui.c	2017-07-04 15:03:01 UTC (rev 6846)
@@ -469,6 +469,8 @@
 		request->type = FWD_LOCAL_TO_REMOTE;
 	} else if (*tmp == 'R' || *tmp == 'r') {
 		request->type = FWD_REMOTE_TO_LOCAL;
+	} else if (*tmp == 'D' || *tmp == 'd') {
+		request->type = FWD_LOCAL_DYNAMIC;
 	} else if (*tmp == 'X' || *tmp == 'x') {
 		make_X_forwarding_spec(request, pvar);
 		return TRUE;
@@ -496,14 +498,12 @@
 		}
 	}
 
-	strncpy_s(request->bind_address, sizeof(request->bind_address),
-	          "localhost", _TRUNCATE);
+	strncpy_s(request->bind_address, sizeof(request->bind_address), "localhost", _TRUNCATE);
 	i=0;
 	switch (argc) {
 		case 4:
 			if (*argv[i] == '\0' || strcmp(argv[i], "*") == 0) {
-				strncpy_s(request->bind_address, sizeof(request->bind_address),
-				          "0.0.0.0", _TRUNCATE);
+				strncpy_s(request->bind_address, sizeof(request->bind_address), "0.0.0.0", _TRUNCATE);
 			}
 			else {
 				// IPv6 \x83A\x83h\x83\x8C\x83X\x82\xCC "[", "]" \x82\xAA\x82\xA0\x82\xEA\x82΍폜
@@ -552,6 +552,46 @@
 
 			break;
 
+		case 2:
+			if (request->type != FWD_LOCAL_DYNAMIC) {
+				return FALSE;
+			}
+			if (*argv[i] == '\0' || strcmp(argv[i], "*") == 0) {
+				strncpy_s(request->bind_address, sizeof(request->bind_address),
+				          "0.0.0.0", _TRUNCATE);
+			}
+			else {
+				// IPv6 \x83A\x83h\x83\x8C\x83X\x82\xCC "[", "]" \x82\xAA\x82\xA0\x82\xEA\x82΍폜
+				start = 0;
+				strncpy_s(hostname, sizeof(hostname), argv[i], _TRUNCATE);
+				if (strlen(hostname) > 0 &&
+				    hostname[strlen(hostname)-1] == ']') {
+					hostname[strlen(hostname)-1] = '\0';
+				}
+				if (hostname[0] == '[') {
+					start = 1;
+				}
+				strncpy_s(request->bind_address, sizeof(request->bind_address),
+				          hostname + start, _TRUNCATE);
+			}
+			i++;
+			// FALLTHROUGH
+		case 1:
+			if (request->type != FWD_LOCAL_DYNAMIC) {
+				return FALSE;
+			}
+			request->from_port = parse_port(argv[i], request->from_port_name,
+			                                sizeof(request->from_port_name));
+			if (request->from_port < 0) {
+				return FALSE;
+			}
+			i++;
+
+			request->to_host[0] = '\0';
+			request->to_port = parse_port("0", request->to_port_name,
+			                              sizeof(request->to_port_name));
+			break;
+
 		default:
 			return FALSE;
 	}
@@ -659,6 +699,8 @@
 			case FWD_REMOTE_X11_TO_LOCAL:
 				_snprintf_s(str, str_remaining, _TRUNCATE, "X");
 				break;
+			case FWD_LOCAL_DYNAMIC:
+				_snprintf_s(str, str_remaining, _TRUNCATE, "D%s", spec->from_port_name);
 			}
 
 			chars = strlen(str);
@@ -773,6 +815,9 @@
 		                  "Remote X applications to local X server");
 		strncpy_s(buf, bufsize, pvar->ts->UIMsg, _TRUNCATE);
 		return;
+	case FWD_LOCAL_DYNAMIC:
+		UTIL_get_lang_msg("MSG_FWD_DYNAMIC", pvar, "Local port %s to remote dynamic");
+		_snprintf_s(buf, bufsize, _TRUNCATE, pvar->ts->UIMsg, verbose_from_port);
 	}
 }
 
@@ -894,8 +939,7 @@
 	BOOL X_enabled = IsDlgButtonChecked(dlg, IDC_SSHFWDX11);
 	int num_specs = X_enabled ? 1 : 0;
 	FWDRequestSpec *specs =
-		(FWDRequestSpec *) malloc(sizeof(FWDRequestSpec) *
-		                              (num_specs + num_items));
+		(FWDRequestSpec *) malloc(sizeof(FWDRequestSpec) * (num_specs + num_items));
 	int i;
 	int num_unspecified_forwardings = 0;
 
@@ -917,8 +961,7 @@
 
 	buf[0] = '\0';
 	for (i = 0; i < num_specs; i++) {
-		if (i < num_specs - 1
-			&& FWD_compare_specs(specs + i, specs + i + 1) == 0) {
+		if (i < num_specs - 1 && FWD_compare_specs(specs + i, specs + i + 1) == 0) {
 			switch (specs[i].type) {
 			case FWD_REMOTE_TO_LOCAL:
 				UTIL_get_lang_msg("MSG_SAME_SERVERPORT_ERROR", pvar,
@@ -927,6 +970,7 @@
 				            pvar->ts->UIMsg, specs[i].from_port);
 				break;
 			case FWD_LOCAL_TO_REMOTE:
+			case FWD_LOCAL_DYNAMIC:
 				UTIL_get_lang_msg("MSG_SAME_LOCALPORT_ERROR", pvar,
 				                  "You cannot have two forwarding from the same local port (%d).");
 				_snprintf_s(buf, sizeof(buf), _TRUNCATE,

Modified: trunk/ttssh2/ttxssh/ttxssh.c
===================================================================
--- trunk/ttssh2/ttxssh/ttxssh.c	2017-07-04 15:02:57 UTC (rev 6845)
+++ trunk/ttssh2/ttxssh/ttxssh.c	2017-07-04 15:03:01 UTC (rev 6846)
@@ -1788,7 +1788,8 @@
 				if (option[4] == 0) {
 					pvar->settings.Enabled = 1;
 				} else if (MATCH_STR(option + 4, "-L") == 0 ||
-				           MATCH_STR(option + 4, "-R") == 0) {
+				           MATCH_STR(option + 4, "-R") == 0 ||
+				           MATCH_STR(option + 4, "-D") == 0) {
 					char *p = option + 5;
 					option2[0] = *p;
 					i = 1;

Modified: trunk/ttssh2/ttxssh/ttxssh.vcproj
===================================================================
--- trunk/ttssh2/ttxssh/ttxssh.vcproj	2017-07-04 15:02:57 UTC (rev 6845)
+++ trunk/ttssh2/ttxssh/ttxssh.vcproj	2017-07-04 15:03:01 UTC (rev 6846)
@@ -272,6 +272,10 @@
 				>
 			</File>
 			<File
+				RelativePath="fwd-socks.h"
+				>
+			</File>
+			<File
 				RelativePath="fwdui.h"
 				>
 			</File>
@@ -392,6 +396,10 @@
 				>
 			</File>
 			<File
+				RelativePath="fwd-socks.c"
+				>
+			</File>
+			<File
 				RelativePath="fwdui.c"
 				>
 			</File>



Ttssh2-commit メーリングリストの案内