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>