1 /***********************************************************************
2 * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
4 * Copyright (c) 2000-2004 Shun-ichi Goto
5 * Copyright (c) 2002, J. Grant (English Corrections)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * ---------------------------------------------------------
22 * PROJECT: My Test Program
23 * AUTHOR: Shun-ichi GOTO <gotoh@taiyo.co.jp>
24 * CREATE: Wed Jun 21, 2000
25 * REVISION: $Revision$
26 * ---------------------------------------------------------
31 * Recent version of 'connect.c' is available from
32 * http://www.taiyo.co.jp/~gotoh/ssh/connect.c
34 * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
36 * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz
39 * http://www.taiyo.co.jp/~gotoh/ssh/connect.html
44 * On UNIX environment:
45 * $ gcc connect.c -o connect
48 * $ gcc -o connect -lresolv -lsocket -lnsl connect.c
50 * on Win32 environment:
51 * $ cl connect.c wsock32.lib advapi32.lib
53 * $ bcc32 connect.c wsock32.lib advapi32.lib
55 * $ gcc connect.c -o connect
57 * on Mac OS X environment:
58 * $ gcc connect.c -o connect -lresolv
60 * $ gcc connect.c -o connect -DBIND_8_COMPAT=1
65 * You can specify proxy method in an environment variable or in a
66 * command line option.
68 * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
69 * [-H [user@]proxy-server[:port]]
70 * [-S [user@]socks-server[:port]]
71 * [-T proxy-server[:port]]
72 * [-c telnet proxy command]
75 * "host" and "port" is for the target hostname and port-number to
78 * The -H option specifys a hostname and port number of the http proxy
79 * server to relay. If port is omitted, 80 is used. You can specify this
80 * value in the environment variable HTTP_PROXY and pass the -h option
83 * The -S option specifys the hostname and port number of the SOCKS
84 * server to relay. Like -H, port number can be omitted and the default
85 * is 1080. You can also specify this value pair in the environment
86 * variable SOCKS5_SERVER and give the -s option to use it.
88 * The '-4' and the '-5' options are for specifying SOCKS relaying and
89 * indicates protocol version to use. It is valid only when used with
90 * '-s' or '-S'. Default is '-5' (protocol version 5)
92 * The '-R' option is for specifying method to resolve the
93 * hostname. Three keywords ("local", "remote", "both") or dot-notation
94 * IP address are acceptable. The keyword "both" means, "Try local
95 * first, then remote". If a dot-notation IP address is specified, use
96 * this host as nameserver. The default is "remote" for SOCKS5 or
97 * "local" for others. On SOCKS4 protocol, remote resolving method
98 * ("remote" and "both") requires protocol 4a supported server.
100 * The '-p' option will forward a local TCP port instead of using the
101 * standard input and output.
103 * The '-P' option is same to '-p' except keep remote session. The
104 * program repeats waiting the port with holding remote session without
105 * disconnecting. To disconnect the remote session, send EOF to stdin or
108 * The '-w' option specifys timeout seconds for making connection with
111 * The '-d' option is used for debug. If you fail to connect, use this
112 * and check request to and response from server.
114 * You can omit the "port" argument when program name is special format
115 * containing port number itself. For example,
116 * $ ln -s connect connect-25
117 * means this connect-25 command is spcifying port number 25 already
118 * so you need not 2nd argument (and ignored if specified).
120 * To use proxy, this example is for SOCKS5 connection to connect to
121 * 'host' at port 25 via SOCKS5 server on 'firewall' host.
122 * $ connect -S firewall host 25
124 * $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER
125 * $ connect -s host 25
127 * For a HTTP-PROXY connection:
128 * $ connect -H proxy-server:8080 host 25
130 * $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY
131 * $ connect -h host 25
132 * To forward a local port, for example to use ssh:
133 * $ connect -p 5550 -H proxy-server:8080 host 22
134 * ($ ssh -l user -p 5550 localhost )
139 * Connect.c doesn't have any configuration to specify the SOCKS server.
140 * If you are a mobile user, this limitation might bother you. However,
141 * You can compile connect.c and link with other standard SOCKS library
142 * like the NEC SOCKS5 library or Dante. This means connect.c is
143 * socksified and uses a configration file like to other SOCKSified
144 * network commands and you can switch configuration file any time
145 * (ex. when ppp startup) that brings you switching of SOCKS server for
146 * connect.c in same way with other commands. For this case, you can
147 * write ~/.ssh/config like this:
149 * ProxyCommand connect -n %h %p
151 * SOCKS5 authentication
152 * =====================
154 * Only USER/PASS authentication is supported.
156 * Proxy authentication
157 * ====================
159 * Only BASIC scheme is supported.
161 * Authentication informations
162 * ===========================
164 * User name for authentication is specifed by an environment variable
165 * or system login name. And password is specified from environment
166 * variable or external program (specified in $SSH_ASKPASS) or tty.
168 * Following environment variable is used for specifying user name.
169 * SOCKS: $SOCKS5_USER, $LOGNAME, $USER
170 * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER
172 * ssh-askpass support
173 * ===================
175 * You can use ssh-askpass (came from OpenSSH or else) to specify
176 * password on graphical environment (X-Window or MS Windows). To use
177 * this, set program name to environment variable SSH_ASKPASS. On UNIX,
178 * X-Window must be required, so $DISPLAY environment variable is also
179 * needed. On Win32 environment, $DISPLAY is not mentioned.
181 * Related Informations
182 * ====================
184 * SOCKS5 -- RFC 1928, RFC 1929, RFC 1961
185 * NEC SOCKS Reference Implementation is available from:
186 * http://www.socks.nec.com
187 * DeleGate version 5 or earlier can be SOCKS4 server,
188 * and version 6 can be SOCKS5 and SOCKS4 server.
189 * and version 7.7.0 or later can be SOCKS5 and SOCKS4a server.
190 * http://www.delegate.org/delegate/
193 * Many http proxy servers supports this, but https should
194 * be allowed as configuration on your host.
195 * For example on DeleGate, you should add "https" to the
196 * "REMITTABLE" parameter to allow HTTP-Proxy like this:
197 * delegated -Pxxxx ...... REMITTABLE="+,https" ...
199 * Hypertext Transfer Protocol -- HTTP/1.1 -- RFC 2616
200 * HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617
201 * For proxy authentication, refer these documents.
203 ***********************************************************************/
212 #include <sys/types.h>
224 #include <sys/stat.h>
231 #include <sys/time.h>
233 #include <sys/select.h>
235 #include <sys/socket.h>
236 #include <netinet/in.h>
237 #include <arpa/inet.h>
239 #if !defined(_WIN32) && !defined(__CYGWIN32__)
240 #define WITH_RESOLVER 1
241 #include <arpa/nameser.h>
243 #else /* not ( not _WIN32 && not __CYGWIN32__) */
245 #endif /* not ( not _WIN32 && not __CYGWIN32__) */
249 #define ECONNRESET WSAECONNRESET
255 static char *vcid = "$Id$";
258 /* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */
260 #define snprintf _snprintf
261 #define vsnprintf _vsnprintf
264 /* consider Borland C */
267 #define _setmode setmode
271 Win32 environment does not support -R option (vc and cygwin)
272 Win32 native compilers does not support -w option, yet (vc)
274 static char *usage = "usage: %s [-dnhst45] [-p local-port]"
277 "[-w timeout] \n" /* cygwin cannot -R */
278 #else /* not __CYGWIN32__ */
279 " \n" /* VC cannot -w nor -R */
280 #endif /* not __CYGWIN32__ */
281 #else /* not _WIN32 */
282 /* help message for UNIX */
283 "[-R resolve] [-w timeout] \n"
284 #endif /* not _WIN32 */
285 " [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
286 " [-T proxy-server[:port]]\n"
287 " [-c telnet-proxy-command]\n"
290 /* name of this program */
291 char *progname = NULL;
292 char *progdesc = "connect --- simple relaying command via proxy.";
293 char *rcs_revstr = "$Revision$";
296 /* set of character for strspn() */
297 const char *digits = "0123456789";
298 const char *dotdigits = "0123456789.";
303 /* report flag to hide secure information */
306 int connect_timeout = 0;
308 /* local input type */
309 #define LOCAL_STDIO 0
310 #define LOCAL_SOCKET 1
311 char *local_type_names[] = { "stdio", "socket" };
312 int local_type = LOCAL_STDIO;
313 u_short local_port = 0; /* option 'p' */
314 int f_hold_session = 0; /* option 'P' */
316 char *telnet_command = "telnet %h %p";
318 /* utiity types, pair holder of number and string */
324 /* relay method, server and port */
325 #define METHOD_UNDECIDED 0
326 #define METHOD_DIRECT 1
327 #define METHOD_SOCKS 2
328 #define METHOD_HTTP 3
329 #define METHOD_TELNET 4
330 char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
332 int relay_method = METHOD_UNDECIDED; /* relaying method */
333 char *relay_host = NULL; /* hostname of relay server */
334 u_short relay_port = 0; /* port of relay server */
335 char *relay_user = NULL; /* user name for auth */
337 /* destination target host and port */
338 char *dest_host = NULL;
339 struct sockaddr_in dest_addr;
340 u_short dest_port = 0;
342 /* informations for SOCKS */
343 #define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
344 #define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
345 #define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
346 #define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
347 #define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
348 #define SOCKS5_REP_REFUSED 0x05 /* connection refused */
349 #define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
350 #define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
351 #define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
352 #define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
354 LOOKUP_ITEM socks5_rep_names[] = {
355 { SOCKS5_REP_SUCCEEDED, "succeeded"},
356 { SOCKS5_REP_FAIL, "general SOCKS server failure"},
357 { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"},
358 { SOCKS5_REP_NUNREACH, "Network unreachable"},
359 { SOCKS5_REP_HUNREACH, "Host unreachable"},
360 { SOCKS5_REP_REFUSED, "connection refused"},
361 { SOCKS5_REP_EXPIRED, "TTL expired"},
362 { SOCKS5_REP_CNOTSUP, "Command not supported"},
363 { SOCKS5_REP_ANOTSUP, "Address not supported"},
364 { SOCKS5_REP_INVADDR, "Invalid address"},
368 /* SOCKS5 authentication methods */
369 #define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
370 #define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
371 #define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
372 #define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
373 #define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
374 #define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
375 #define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
377 #define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */
378 #define SOCKS4_REP_REJECTED 91 /* request rejected or failed */
379 #define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */
380 #define SOCKS4_REP_USERID 93 /* user id not matched */
382 LOOKUP_ITEM socks4_rep_names[] = {
383 { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"},
384 { SOCKS4_REP_REJECTED, "request rejected or failed"},
385 { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
386 { SOCKS4_REP_USERID, "user id not matched"},
390 #define RESOLVE_UNKNOWN 0
391 #define RESOLVE_LOCAL 1
392 #define RESOLVE_REMOTE 2
393 #define RESOLVE_BOTH 3
394 char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
396 int socks_version = 5; /* SOCKS protocol version */
397 int socks_resolve = RESOLVE_UNKNOWN;
398 struct sockaddr_in socks_ns;
399 char *socks5_auth = NULL;
401 /* Environment variable names */
402 #define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
403 #define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
404 #define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
406 #define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
407 #define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
408 #define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
410 #define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
411 #define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */
412 #define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */
413 #define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */
414 #define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */
416 #define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
417 #define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
418 #define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
420 #define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
422 #define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */
423 #define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
425 #define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
426 #define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT"
427 #define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT"
428 #define ENV_HTTP_DIRECT "HTTP_DIRECT"
429 #define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
431 #define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
432 #define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
434 /* Prefix string of HTTP_PROXY */
435 #define HTTP_PROXY_PREFIX "http://"
436 #define PROXY_AUTH_NONE 0
437 #define PROXY_AUTH_BASIC 1
438 #define PROXY_AUTH_DIGEST 2
439 int proxy_auth_type = PROXY_AUTH_NONE;
441 /* reason of end repeating */
442 #define REASON_UNK -2
443 #define REASON_ERROR -1
444 #define REASON_CLOSED_BY_LOCAL 0
445 #define REASON_CLOSED_BY_REMOTE 1
447 /* return value of relay start function. */
448 #define START_ERROR -1
450 #define START_RETRY 1
452 /* socket related definitions */
457 #define SOCKET_ERROR -1
461 #define socket_errno() WSAGetLastError()
463 #define closesocket close
464 #define socket_errno() (errno)
471 /* packet operation macro */
472 #define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
474 /* debug message output */
476 debug( const char *fmt, ... )
480 va_start( args, fmt );
481 fprintf(stderr, "DEBUG: ");
482 vfprintf( stderr, fmt, args );
488 debug_( const char *fmt, ... ) /* without prefix */
492 va_start( args, fmt );
493 vfprintf( stderr, fmt, args );
498 /* error message output */
500 error( const char *fmt, ... )
503 va_start( args, fmt );
504 fprintf(stderr, "ERROR: ");
505 vfprintf( stderr, fmt, args );
510 fatal( const char *fmt, ... )
513 va_start( args, fmt );
514 fprintf(stderr, "FATAL: ");
515 vfprintf( stderr, fmt, args );
522 xmalloc (size_t size)
524 void *ret = malloc(size);
526 fatal("Cannot allocate memory: %d bytes.\n", size);
531 downcase( char *buf )
541 expand_host_and_port (const char *fmt, const char *host, int port)
544 char *buf, *dst, *ptr;
545 size_t len = strlen(fmt) + strlen(host) + 20;
558 snprintf (dst, len, "%d", port);
565 dst = buf + strlen (buf);
566 } else if (*src == '\\') {
590 assert (strlen(buf) < len);
596 lookup_resolve( const char *str )
598 char *buf = strdup( str );
602 if ( strcmp( buf, "both" ) == 0 )
604 else if ( strcmp( buf, "remote" ) == 0 )
605 ret = RESOLVE_REMOTE;
606 else if ( strcmp( buf, "local" ) == 0 )
608 else if ( strspn(buf, dotdigits) == strlen(buf) ) {
609 #ifndef WITH_RESOLVER
610 fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
611 #endif /* not WITH_RESOLVER */
612 ret = RESOLVE_LOCAL; /* this case is also 'local' */
613 socks_ns.sin_addr.s_addr = inet_addr(buf);
614 socks_ns.sin_family = AF_INET;
617 ret = RESOLVE_UNKNOWN;
626 static char buf[1024];
627 DWORD size = sizeof(buf);
629 GetUserName( buf, &size);
631 #else /* not _WIN32 */
632 struct passwd *pw = getpwuid(getuid());
634 fatal("getpwuid() failed for uid: %d\n", getuid());
636 #endif /* not _WIN32 */
640 check STR is begin with substr with case-ignored comparison.
641 Return 1 if matched, otherwise 0.
644 expect( char *str, char *substr)
646 int len = strlen(substr);
647 while ( 0 < len-- ) {
648 if ( toupper(*str) != toupper(*substr) )
649 return 0; /* not matched */
652 return 1; /* good, matched */
656 /** PARAMETER operation **/
657 #define PARAMETER_FILE "/etc/connectrc"
658 #define PARAMETER_DOTFILE ".connectrc"
663 PARAMETER_ITEM parameter_table[] = {
664 { ENV_SOCKS_SERVER, NULL },
665 { ENV_SOCKS5_SERVER, NULL },
666 { ENV_SOCKS4_SERVER, NULL },
667 { ENV_SOCKS_RESOLVE, NULL },
668 { ENV_SOCKS5_RESOLVE, NULL },
669 { ENV_SOCKS4_RESOLVE, NULL },
670 { ENV_SOCKS5_USER, NULL },
671 { ENV_SOCKS5_PASSWD, NULL },
672 { ENV_SOCKS5_PASSWORD, NULL },
673 { ENV_HTTP_PROXY, NULL },
674 { ENV_HTTP_PROXY_USER, NULL },
675 { ENV_HTTP_PROXY_PASSWORD, NULL },
676 { ENV_CONNECT_USER, NULL },
677 { ENV_CONNECT_PASSWORD, NULL },
678 { ENV_SSH_ASKPASS, NULL },
679 { ENV_SOCKS5_DIRECT, NULL },
680 { ENV_SOCKS4_DIRECT, NULL },
681 { ENV_SOCKS_DIRECT, NULL },
682 { ENV_HTTP_DIRECT, NULL },
683 { ENV_CONNECT_DIRECT, NULL },
684 { ENV_SOCKS5_AUTH, NULL },
689 find_parameter_item(const char* name)
692 for( i = 0; parameter_table[i].name != NULL; i++ ){
693 if ( strcmp(name, parameter_table[i].name) == 0 )
694 return ¶meter_table[i];
700 read_parameter_file_1(const char* name)
705 f = fopen(name, "r");
707 debug("Reading parameter file(%s)\n", name);
708 for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
709 char *p, *q, *param, *value;
710 p = strchr(lbuf, '\n');
712 fatal("%s:%d: buffer overflow\n", name, line);
714 p = strchr(lbuf, '#');
717 for ( p = lbuf; *p; p++ )
718 if( *p != ' ' && *p != '\t' ) break;
719 if ( *p == '\0' ) continue;
723 error("%s:%d: missing equal sign\n", name, line);
726 for ( q = p - 1; q >= lbuf; q-- )
727 if ( *q != ' ' && *q != '\t' ) break;
730 if ( *p != ' ' && *p != '\t' ) break;
733 for ( p--; p >= lbuf; p-- )
734 if ( *p != ' ' && *p != '\t' ) break;
736 if ( param && value ) {
737 PARAMETER_ITEM *item;
738 item = find_parameter_item(param);
739 if ( item == NULL ) {
740 error("%s:%d: unknown parameter `%s'\n", name, line, param);
743 item->value = strdup(value);
744 debug("Parameter `%s' is set to `%s'\n", param, value);
751 read_parameter_file(void)
753 #if !defined(_WIN32) || defined(cygwin)
758 read_parameter_file_1(PARAMETER_FILE);
759 #if !defined(_WIN32) || defined(cygwin)
760 pw = getpwuid(getuid());
762 fatal("getpwuid() failed for uid: %d\n", getuid());
763 name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
764 strcpy(name, pw->pw_dir);
765 strcat(name, "/" PARAMETER_DOTFILE);
766 read_parameter_file_1(name);
772 getparam(const char* name)
774 char *value = getenv(name);
775 if ( value == NULL ){
776 PARAMETER_ITEM *item = find_parameter_item(name);
784 /** DIRECT connection **/
785 #define MAX_DIRECT_ADDR_LIST 256
793 struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
794 int n_direct_addr_list = 0;
797 mask_addr (void *addr, void *mask, int addrlen)
802 while ( 0 < addrlen-- )
807 add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
809 struct in_addr iaddr;
811 if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
812 error("direct address table is full!\n");
816 mask_addr(&iaddr, mask, sizeof(iaddr));
817 s = strdup(inet_ntoa(iaddr));
818 debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
820 memcpy( &direct_addr_list[n_direct_addr_list].addr,
821 &iaddr, sizeof(iaddr));
822 memcpy( &direct_addr_list[n_direct_addr_list].mask,
823 mask, sizeof(*mask));
824 direct_addr_list[n_direct_addr_list].negative = negative;
825 n_direct_addr_list++;
830 parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
833 /* Assume already be splitted by separator
834 and formatted as folowing:
835 1) 12.34.56.789/255.255.255.0
838 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
844 assert( str != NULL );
845 debug("parsing address pair: '%s'\n", str);
849 dsta = (u_char*)&addr->s_addr;
850 dstm = (u_char*)&mask->s_addr;
851 for (i=0; i<4; i++ ) {
853 break; /* case of format #3 */
854 if ( !isdigit(*ptr) )
855 return -1; /* format error: */
856 *dsta++ = atoi( ptr );
857 *dstm++ = 255; /* automatic mask for format #3 */
858 while ( isdigit(*ptr) ) /* skip digits */
865 /* At this point, *ptr points '/' or EOS ('\0') */
867 return 0; /* complete as format #3 */
869 return -1; /* format error */
870 /* Now parse mask for format #1 or #2 */
872 mask->s_addr = 0; /* clear automatic mask */
874 if ( strchr( ptr, '.') ) {
875 /* case of format #1 */
876 dstm = (u_char*)&mask->s_addr;
877 for (i=0; i<4; i++) {
878 if ( !isdigit(*ptr) )
879 return -1; /* format error: */
881 while ( isdigit(*ptr) ) /* skip digits */
886 break; /* from for loop */
888 /* complete as format #1 */
890 /* case of format #2 */
891 if ( !isdigit(*ptr) )
892 return -1; /* format error: */
895 return -1; /* format error */
896 mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
897 /* complete as format #1 */
903 initialize_direct_addr (void)
907 char *env = NULL, *beg, *next, *envkey = NULL;
908 struct in_addr addr, mask;
910 if ( relay_method == METHOD_SOCKS ){
911 if ( socks_version == 5 )
912 envkey = ENV_SOCKS5_DIRECT;
914 envkey = ENV_SOCKS4_DIRECT;
915 env = getparam(envkey);
917 env = getparam(ENV_SOCKS_DIRECT);
918 } else if ( relay_method == METHOD_HTTP ){
919 env = getparam(ENV_HTTP_DIRECT);
923 env = getparam(ENV_CONNECT_DIRECT);
926 return; /* no entry */
927 debug("making direct addr list from: '%s'\n", env);
928 env = strdup( env ); /* reallocate to modify */
932 if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
933 error("too many entries in %s", envkey);
934 break; /* from do loop */
936 next = strchr( beg, ',');
946 if ( !parse_addr_pair( beg, &addr, &mask ) ) {
947 add_direct_addr( &addr, &mask, negative );
949 error("invalid addr format in %s: %s\n", envkey, beg);
953 } while ( next != NULL );
960 cmp_addr (void *addr1, void *addr2, int addrlen)
962 return memcmp( addr1, addr2, addrlen );
966 is_direct_address (const struct sockaddr_in *addr)
969 struct in_addr saddr, iaddr;
971 saddr = addr->sin_addr;
973 /* Note: assume IPV4 address !! */
974 for (i=0; i<n_direct_addr_list; i++ ) {
976 mask_addr( &iaddr, &direct_addr_list[i].mask,
977 sizeof(struct in_addr));
978 if (cmp_addr(&iaddr, &direct_addr_list[i].addr,
979 sizeof(struct in_addr)) == 0) {
980 if (direct_addr_list[i].negative) {
981 debug("negative match, addr to be SOCKSify: %s\n",
983 return 0; /* not direct */
985 if (!direct_addr_list[i].negative) {
986 debug("positive match, addr to be direct: %s\n",
988 return 1; /* direct*/
992 debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
993 return 0; /* not direct */
997 /** TTY operation **/
1003 intr_handler(int sig)
1009 tty_change_echo(int fd, int enable)
1011 static struct termios ntio, otio; /* new/old termios */
1012 static sigset_t nset, oset; /* new/old sigset */
1013 static struct sigaction nsa, osa; /* new/old sigaction */
1014 static int disabled = 0;
1016 if ( disabled && enable ) {
1018 tcsetattr(fd, TCSANOW, &otio);
1020 /* resotore sigaction */
1021 sigprocmask(SIG_SETMASK, &oset, NULL);
1022 sigaction(SIGINT, &osa, NULL);
1023 if ( intr_flag != 0 ) {
1024 /* re-generate signal */
1025 kill(getpid(), SIGINT);
1030 } else if (!disabled && !enable) {
1031 /* set SIGINTR handler and break syscall on singal */
1033 sigaddset(&nset, SIGTSTP);
1034 sigprocmask(SIG_BLOCK, &nset, &oset);
1036 memset(&nsa, 0, sizeof(nsa));
1037 nsa.sa_handler = intr_handler;
1038 sigaction(SIGINT, &nsa, &osa);
1040 if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
1043 ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
1044 (void) tcsetattr(fd, TCSANOW, &ntio);
1051 #define TTY_NAME "/dev/tty"
1053 tty_readpass( const char *prompt, char *buf, size_t size )
1057 tty = open(TTY_NAME, O_RDWR);
1059 error("Unable to open %s\n", TTY_NAME);
1060 return -1; /* can't open tty */
1063 return -1; /* no room */
1064 write(tty, prompt, strlen(prompt));
1066 tty_change_echo(tty, 0); /* disable echo */
1067 ret = read(tty,buf, size-1);
1068 tty_change_echo(tty, 1); /* restore */
1069 write(tty, "\n", 1); /* new line */
1071 if ( strchr(buf,'\n') == NULL )
1081 w32_intr_handler(DWORD dwCtrlType)
1083 if ( dwCtrlType == CTRL_C_EVENT ) {
1091 #define tty_readpass w32_tty_readpass
1093 w32_tty_readpass( const char *prompt, char *buf, size_t size )
1095 HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1096 0, NULL, OPEN_EXISTING, 0, NULL);
1097 HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
1098 0, NULL, OPEN_EXISTING, 0, NULL);
1102 if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
1103 fatal("Cannot open console. (errno=%d)", GetLastError());
1105 WriteFile(out, prompt, strlen(prompt), &bytes, 0);
1106 SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
1107 GetConsoleMode(in, &mode);
1108 SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
1109 ret = ReadFile(in, buf, size, &bytes, 0);
1110 SetConsoleMode(in, mode); /* enable echo */
1111 SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
1113 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
1114 WriteFile(out,"\n", 1, &bytes, 0);
1122 /*** User / Password ***/
1124 /* SOCKS5 and HTTP Proxy authentication may requires username and
1125 password. We ll give it via environment variable or tty.
1126 Username and password for authentication are decided by
1129 Username is taken from
1130 1) server location spec (i.e. user@host:port)
1131 2) environment variables (see tables.1)
1132 3) system account name currently logged in.
1134 Table.1 Order of environment variables for username
1136 | SOCKS v5 | SOCKS v4 | HTTP proxy |
1137 --+-------------+-------------+-----------------+
1138 1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
1139 --+-------------+-------------+ |
1141 --+---------------------------+-----------------+
1143 --+---------------------------------------------+
1145 Password is taken from
1146 1) by environment variables (see table.2)
1147 2) by entering from tty.
1149 Table.2 Order of environment variables for password
1151 | SOCKS v5 | HTTP proxy |
1152 --+-----------------+---------------------+
1153 1 | SOCKS5_PASSWD | |
1154 --+-----------------+ HTTP_PROXY_PASSWORD |
1155 2 | SOCKS5_PASSWORD | |
1156 --+-----------------+---------------------+
1157 3 | CONNECT_PASSWORD |
1158 --+---------------------------------------+
1160 Note: SOCKS5_PASSWD which is added in rev. 1.79
1161 to share value with NEC SOCKS implementation.
1165 determine_relay_user ()
1168 /* get username from environment variable, or system. */
1169 if (relay_method == METHOD_SOCKS) {
1170 if (user == NULL && socks_version == 5)
1171 user = getparam (ENV_SOCKS5_USER);
1172 if (user == NULL && socks_version == 4)
1173 user = getparam (ENV_SOCKS4_USER);
1175 user = getparam (ENV_SOCKS_USER);
1176 } else if (relay_method == METHOD_HTTP) {
1178 user = getparam (ENV_HTTP_PROXY_USER);
1181 user = getparam (ENV_CONNECT_USER);
1182 /* determine relay user by system call if not yet. */
1184 user = getusername();
1189 determine_relay_password ()
1192 if (pass == NULL && relay_method == METHOD_HTTP)
1193 pass = getparam(ENV_HTTP_PROXY_PASSWORD);
1194 if (pass == NULL && relay_method == METHOD_SOCKS)
1195 pass = getparam(ENV_SOCKS5_PASSWD);
1196 if (pass == NULL && relay_method == METHOD_SOCKS)
1197 pass = getparam(ENV_SOCKS5_PASSWORD);
1199 pass = getparam(ENV_CONNECT_PASSWORD);
1204 /*** network operations ***/
1208 Determine relay informations:
1209 method, host, port, and username.
1210 1st arg, METHOD should be METHOD_xxx.
1211 2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
1212 hostname is domain name or dot notation.
1213 If port is omitted, use 80 for METHOD_HTTP method,
1214 use 1080 for METHOD_SOCKS method.
1215 Username is also able to given by 3rd. format.
1216 2nd argument SPEC can be NULL. if NULL, use environment variable.
1219 set_relay( int method, char *spec )
1221 char *buf, *sep, *resolve;
1223 relay_method = method;
1225 read_parameter_file();
1226 initialize_direct_addr();
1227 if (n_direct_addr_list == 0) {
1228 debug ("No direct address are specified.\n");
1231 for ( i=0; i<n_direct_addr_list; i++ ) {
1233 s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
1234 s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
1235 debug(" #%d: %c%s/%s\n", i,
1236 (direct_addr_list[i].negative)? '!': ' ',
1245 return -1; /* nothing to do */
1248 if ( spec == NULL ) {
1249 switch ( socks_version ) {
1251 spec = getparam(ENV_SOCKS5_SERVER);
1254 spec = getparam(ENV_SOCKS4_SERVER);
1259 spec = getparam(ENV_SOCKS_SERVER);
1262 fatal("Failed to determine SOCKS server.\n");
1263 relay_port = 1080; /* set default first */
1265 /* determine resolve method */
1266 if ( socks_resolve == RESOLVE_UNKNOWN ) {
1267 if ( ((socks_version == 5) &&
1268 ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
1269 ((socks_version == 4) &&
1270 ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
1271 ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
1272 socks_resolve = lookup_resolve( resolve );
1273 if ( socks_resolve == RESOLVE_UNKNOWN )
1274 fatal("Invalid resolve method: %s\n", resolve);
1277 if ( socks_version == 5 )
1278 socks_resolve = RESOLVE_REMOTE;
1280 socks_resolve = RESOLVE_LOCAL;
1287 spec = getparam(ENV_HTTP_PROXY);
1289 fatal("You must specify http proxy server\n");
1290 relay_port = 80; /* set default first */
1294 spec = getparam(ENV_TELNET_PROXY);
1296 fatal("You must specify telnet proxy server\n");
1297 relay_port = 23; /* set default first */
1300 if (expect( spec, HTTP_PROXY_PREFIX)) {
1301 /* URL format like: "http://server:port/" */
1302 /* extract server:port part */
1303 buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
1304 buf[strcspn(buf, "/")] = '\0';
1306 /* assume spec is aready "server:port" format */
1307 buf = strdup( spec );
1311 /* check username in spec */
1312 sep = strchr( spec, '@' );
1313 if ( sep != NULL ) {
1315 relay_user = strdup( spec );
1318 if (relay_user == NULL)
1319 relay_user = determine_relay_user();
1321 /* split out hostname and port number from spec */
1322 sep = strchr(spec,':');
1323 if ( sep == NULL ) {
1324 /* hostname only, port is already set as default */
1325 relay_host = strdup( spec );
1327 /* hostname and port */
1328 relay_port = atoi(sep+1);
1330 relay_host = strdup( spec );
1338 resolve_port( const char *service )
1341 if ( service[strspn (service, digits)] == '\0' ) {
1342 /* all digits, port number */
1343 port = atoi(service);
1345 /* treat as service name */
1346 struct servent *ent;
1347 ent = getservbyname( service, NULL );
1348 if ( ent == NULL ) {
1349 debug("Unknown service, '%s'\n", service);
1352 port = ntohs(ent->s_port);
1353 debug("service: %s => %d\n", service, port);
1356 return (u_short)port;
1364 ptr = strstr(rcs_revstr, ": ");
1366 revstr = strdup("unknown");
1370 len = strspn(ptr, dotdigits);
1372 revstr = xmalloc(len+1);
1373 memcpy(revstr, ptr, len);
1379 getarg( int argc, char **argv )
1382 char *ptr, *server = (char*)NULL;
1383 int method = METHOD_DIRECT;
1389 while ( (0 < argc) && (**argv == '-') ) {
1393 case 's': /* use SOCKS */
1394 method = METHOD_SOCKS;
1397 case 'n': /* no proxy */
1398 method = METHOD_DIRECT;
1401 case 'h': /* use http-proxy */
1402 method = METHOD_HTTP;
1405 method = METHOD_TELNET;
1408 case 'S': /* specify SOCKS server */
1411 method = METHOD_SOCKS;
1414 error("option '-%c' needs argument.\n", *ptr);
1419 case 'H': /* specify http-proxy server */
1422 method = METHOD_HTTP;
1425 error("option '-%c' needs argument.\n", *ptr);
1429 case 'T': /* specify telnet proxy server */
1432 method = METHOD_TELNET;
1435 error("option '-%c' needs argument.\n", *ptr);
1443 telnet_command = *argv;
1445 error("option '%c' needs argument.\n", *ptr);
1453 case 'p': /* specify port to forward */
1456 local_type = LOCAL_SOCKET;
1457 local_port = resolve_port(*argv);
1459 error("option '-%c' needs argument.\n", *ptr);
1468 connect_timeout = atoi(*argv);
1470 error("option '-%c' needs argument.\n", *ptr);
1474 #endif /* not _WIN32 */
1487 socks5_auth = *argv;
1489 error("option '-%c' needs argument.\n", *ptr);
1494 case 'R': /* specify resolve method */
1497 socks_resolve = lookup_resolve( *argv );
1499 error("option '-%c' needs argument.\n", *ptr);
1504 case 'V': /* print version */
1505 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1508 case 'd': /* debug mode */
1513 error("unknown option '-%c'\n", *ptr);
1525 set_relay( method, server );
1527 /* check destination HOST (MUST) */
1529 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1530 fprintf(stderr, usage, progname);
1533 dest_host = argv[0];
1534 /* decide port or service name from programname or argument */
1535 if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
1536 ((ptr=strchr( progname, '\\')) != NULL) )
1540 if ( dest_port == 0 ) {
1541 /* accept only if -P is not specified. */
1543 /* get port number from argument (prior to progname) */
1544 /* NOTE: This way is for cvs ext method. */
1545 dest_port = resolve_port(argv[1]);
1546 } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
1547 /* decide port number from program name */
1548 char *str = strdup( ptr+8 );
1549 str[strcspn( str, "." )] = '\0';
1550 dest_port = resolve_port(str);
1554 /* check port number */
1555 if ( dest_port <= 0 ) {
1556 error( "You must specify the destination port correctly.\n");
1560 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
1561 error("Invalid relay port: %d\n", dest_port);
1567 /* report for debugging */
1568 debug("relay_method = %s (%d)\n",
1569 method_names[relay_method], relay_method);
1570 if ( relay_method != METHOD_DIRECT ) {
1571 debug("relay_host=%s\n", relay_host);
1572 debug("relay_port=%d\n", relay_port);
1573 debug("relay_user=%s\n", relay_user);
1575 if ( relay_method == METHOD_SOCKS ) {
1576 debug("socks_version=%d\n", socks_version);
1577 debug("socks_resolve=%s (%d)\n",
1578 resolve_names[socks_resolve], socks_resolve);
1580 debug("local_type=%s\n", local_type_names[local_type]);
1581 if ( local_type == LOCAL_SOCKET ) {
1582 debug("local_port=%d\n", local_port);
1584 debug (" with holding remote session.\n");
1586 debug("dest_host=%s\n", dest_host);
1587 debug("dest_port=%d\n", dest_port);
1589 fprintf(stderr, usage, progname);
1596 /* Time-out feature is not allowed for Win32 native compilers. */
1597 /* MSVC and Borland C cannot but Cygwin and UNIXes can. */
1599 /* timeout signal hander */
1603 debug( "timed out\n" );
1604 signal( SIGALRM, SIG_IGN );
1608 /* set timeout param = seconds, 0 clears */
1610 set_timeout(int timeout)
1612 /* This feature is allowed for UNIX or cygwin environments, currently */
1613 if ( timeout == 0 ) {
1614 debug( "clearing timeout\n" );
1615 signal( SIGALRM, SIG_IGN );
1618 debug( "setting timeout: %d seconds\n", timeout );
1619 signal(SIGALRM, (void *)sig_timeout);
1625 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1627 switch_ns (struct sockaddr_in *ns)
1630 memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
1632 debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
1634 #endif /* !_WIN32 && !__CYGWIN32__ */
1637 TODO: fallback if askpass execution failed.
1641 local_resolve (const char *host, struct sockaddr_in *addr)
1643 struct hostent *ent;
1644 if ( strspn(host, dotdigits) == strlen(host) ) {
1645 /* given by IPv4 address */
1646 addr->sin_family = AF_INET;
1647 addr->sin_addr.s_addr = inet_addr(host);
1649 debug("resolving host by name: %s\n", host);
1650 ent = gethostbyname (host);
1652 memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
1653 addr->sin_family = ent->h_addrtype;
1654 debug("resolved: %s (%s)\n",
1655 host, inet_ntoa(addr->sin_addr));
1657 debug("failed to resolve locally.\n");
1658 return -1; /* failed */
1661 return 0; /* good */
1665 open_connection( const char *host, u_short port )
1668 struct sockaddr_in saddr;
1670 if ( relay_method == METHOD_DIRECT ) {
1673 } else if ((local_resolve (dest_host, &saddr) >= 0)&&
1674 (is_direct_address(&saddr))) {
1675 debug("%s is connected directly\n", dest_host);
1676 relay_method = METHOD_DIRECT;
1684 if (local_resolve (host, &saddr) < 0) {
1685 error("can't resolve hostname: %s\n", host);
1686 return SOCKET_ERROR;
1688 saddr.sin_port = htons(port);
1690 debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
1691 s = socket( AF_INET, SOCK_STREAM, 0 );
1692 if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
1694 debug( "connect() failed.\n");
1695 return SOCKET_ERROR;
1701 report_text( char *prefix, char *buf )
1703 static char work[1024];
1709 return; /* don't report */
1710 debug("%s \"", prefix);
1712 memset( work, 0, sizeof(work));
1714 while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) {
1716 case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
1717 case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
1718 case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
1719 case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
1721 if ( isprint(*buf) ) {
1724 int consumed = tmp - work;
1725 snprintf( tmp, sizeof(work)-consumed,
1726 "\\x%02X", (unsigned char)*buf);
1741 report_bytes( char *prefix, char *buf, int len )
1745 debug( "%s", prefix );
1747 fprintf( stderr, " %02x", *(unsigned char *)buf);
1751 fprintf(stderr, "\n");
1756 atomic_out( SOCKET s, char *buf, int size )
1760 assert( buf != NULL );
1764 while ( 0 < size ) {
1765 len = send( s, buf+ret, size, 0 );
1767 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1772 debug("atomic_out() [some bytes]\n");
1773 debug(">>> xx xx xx xx ...\n");
1775 debug("atomic_out() [%d bytes]\n", ret);
1776 report_bytes(">>>", buf, ret);
1782 atomic_in( SOCKET s, char *buf, int size )
1786 assert( buf != NULL );
1791 while ( 0 < size ) {
1792 len = recv( s, buf+ret, size, 0 );
1794 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1795 } else if ( len == 0 ) {
1796 fatal( "Connection closed by peer.\n");
1802 debug("atomic_in() [some bytes]\n");
1803 debug("<<< xx xx xx xx ...\n");
1805 debug("atomic_in() [%d bytes]\n", ret);
1806 report_bytes("<<<", buf, ret);
1812 line_input( SOCKET s, char *buf, int size )
1816 return 0; /* no error */
1818 while ( 0 < size ) {
1819 switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */
1821 error("recv() error\n");
1822 return -1; /* error */
1824 size = 0; /* end of stream */
1827 /* continue reading until last 1 char is EOL? */
1828 if ( *dst == '\n' ) {
1839 report_text( "<<<", buf);
1844 Span token in given string STR until char in DELIM is appeared.
1845 Then replace contiguous DELIMS with '\0' for string termination
1846 and returns next pointer.
1847 If no next token, return NULL.
1850 cut_token( char *str, char *delim)
1852 char *ptr = str + strcspn(str, delim);
1853 char *end = ptr + strspn(ptr, delim);
1862 lookup(int num, LOOKUP_ITEM *items)
1865 while (0 <= items[i].num) {
1866 if (items[i].num == num)
1867 return items[i].str;
1874 password input routine
1875 Use ssh-askpass (same mechanism to OpenSSH)
1878 readpass( const char* prompt, ...)
1880 static char buf[1000]; /* XXX, don't be fix length */
1882 va_start(args, prompt);
1883 vsnprintf(buf, sizeof(buf), prompt, args);
1886 if ( getparam(ENV_SSH_ASKPASS)
1887 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1888 && getenv("DISPLAY")
1889 #endif /* not _WIN32 && not __CYGWIN32__ */
1891 /* use ssh-askpass to get password */
1893 char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
1894 int cmd_size = strlen(askpass) +1 +1 +strlen(buf) +1 +1;
1895 cmd = xmalloc(cmd_size);
1896 snprintf(cmd, cmd_size, "%s \"%s\"", askpass, buf);
1897 fp = popen(cmd, "r");
1900 return NULL; /* fail */
1902 if (fgets(buf, sizeof(buf), fp) == NULL)
1903 return NULL; /* fail */
1906 tty_readpass( buf, buf, sizeof(buf));
1908 buf[strcspn(buf, "\r\n")] = '\0';
1913 socks5_do_auth_userpass( int s )
1915 unsigned char buf[1024], *ptr;
1919 /* do User/Password authentication. */
1920 /* This feature requires username and password from
1921 command line argument or environment variable,
1923 if (relay_user == NULL)
1924 fatal("cannot determine user name.\n");
1926 /* get password from environment variable if exists. */
1927 if ((pass=determine_relay_password()) == NULL &&
1928 (pass=readpass("Enter SOCKS5 password for %s@%s: ",
1929 relay_user, relay_host)) == NULL)
1930 fatal("Cannot get password for user: %s\n", relay_user);
1932 /* make authentication packet */
1934 PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */
1935 len = strlen( relay_user ); /* ULEN and UNAME */
1936 PUT_BYTE( ptr++, len );
1937 strcpy( ptr, relay_user );
1939 len = strlen( pass ); /* PLEN and PASSWD */
1940 PUT_BYTE( ptr++, strlen(pass));
1941 strcpy( ptr, pass );
1943 memset (pass, 0, strlen(pass)); /* erase password */
1945 /* send it and get answer */
1947 atomic_out( s, buf, ptr-buf );
1949 atomic_in( s, buf, 2 );
1953 return 0; /* success */
1955 return -1; /* fail */
1959 socks5_getauthname( int auth )
1962 case SOCKS5_AUTH_REJECT: return "REJECTED";
1963 case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
1964 case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
1965 case SOCKS5_AUTH_USERPASS: return "USERPASS";
1966 case SOCKS5_AUTH_CHAP: return "CHAP";
1967 case SOCKS5_AUTH_EAP: return "EAP";
1968 case SOCKS5_AUTH_MAF: return "MAF";
1969 default: return "(unknown)";
1978 AUTH_METHOD_ITEM socks5_auth_table[] = {
1979 { "none", SOCKS5_AUTH_NOAUTH },
1980 { "gssapi", SOCKS5_AUTH_GSSAPI },
1981 { "userpass", SOCKS5_AUTH_USERPASS },
1982 { "chap", SOCKS5_AUTH_CHAP },
1987 socks5_auth_parse_1(char *start, char *end){
1989 for ( ; *start; start++ )
1990 if ( *start != ' ' && *start != '\t') break;
1991 for ( end--; end >= start; end-- ) {
1992 if ( *end != ' ' && *end != '\t'){
1998 for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
1999 if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
2000 return socks5_auth_table[i].auth;
2003 fatal("Unknown auth method: %s\n", start);
2008 socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
2011 while ( i < max_auth ) {
2012 end = strchr(start, ',');
2013 if (*start && end) {
2014 auth_list[i++] = socks5_auth_parse_1(start, end);
2020 if ( *start && ( i < max_auth ) ){
2021 for( end = start; *end; end++ );
2022 auth_list[i++] = socks5_auth_parse_1(start, end);
2024 fatal("Too much auth method.\n");
2029 /* begin SOCKS5 relaying
2030 And no authentication is supported.
2033 begin_socks5_relay( SOCKET s )
2035 unsigned char buf[256], *ptr, *env = socks5_auth;
2036 unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
2037 int len, auth_result, i;
2039 debug( "begin_socks_relay()\n");
2041 /* request authentication */
2043 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2046 env = getparam(ENV_SOCKS5_AUTH);
2047 if ( env == NULL ) {
2048 /* add no-auth authentication */
2049 auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
2050 /* add user/pass authentication */
2051 auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
2053 n_auth = socks5_auth_parse(env, auth_list, 10);
2055 PUT_BYTE( ptr++, n_auth); /* num auth */
2056 for (i=0; i<n_auth; i++) {
2057 debug("available auth method[%d] = %s (0x%02x)\n",
2058 i, socks5_getauthname(auth_list[i]), auth_list[i]);
2059 PUT_BYTE( ptr++, auth_list[i]); /* authentications */
2061 atomic_out( s, buf, ptr-buf ); /* send requst */
2062 atomic_in( s, buf, 2 ); /* recv response */
2063 if ( (buf[0] != 5) || /* ver5 response */
2064 (buf[1] == 0xFF) ) { /* check auth method */
2065 error("No auth method accepted.\n");
2068 auth_method = buf[1];
2070 debug("auth method: %s\n", socks5_getauthname(auth_method));
2072 switch ( auth_method ) {
2073 case SOCKS5_AUTH_REJECT:
2074 error("No acceptable authentication method\n");
2075 return -1; /* fail */
2077 case SOCKS5_AUTH_NOAUTH:
2082 case SOCKS5_AUTH_USERPASS:
2083 auth_result = socks5_do_auth_userpass(s);
2087 error("Unsupported authentication method: %s\n",
2088 socks5_getauthname( auth_method ));
2089 return -1; /* fail */
2091 if ( auth_result != 0 ) {
2092 error("Authentication failed.\n");
2095 /* request to connect */
2097 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2098 PUT_BYTE( ptr++, 1); /* CMD: CONNECT */
2099 PUT_BYTE( ptr++, 0); /* FLG: 0 */
2100 if ( dest_addr.sin_addr.s_addr == 0 ) {
2101 /* resolved by SOCKS server */
2102 PUT_BYTE( ptr++, 3); /* ATYP: DOMAINNAME */
2103 len = strlen(dest_host);
2104 PUT_BYTE( ptr++, len); /* DST.ADDR (len) */
2105 memcpy( ptr, dest_host, len ); /* (hostname) */
2108 /* resolved localy */
2109 PUT_BYTE( ptr++, 1 ); /* ATYP: IPv4 */
2110 memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
2111 ptr += sizeof(dest_addr.sin_addr);
2113 PUT_BYTE( ptr++, dest_port>>8); /* DST.PORT */
2114 PUT_BYTE( ptr++, dest_port&0xFF);
2115 atomic_out( s, buf, ptr-buf); /* send request */
2116 atomic_in( s, buf, 4 ); /* recv response */
2117 if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */
2118 error("Got error response from SOCKS server: %d (%s).\n",
2119 buf[1], lookup(buf[1], socks5_rep_names));
2123 switch ( buf[3] ) { /* case by ATYP */
2124 case 1: /* IP v4 ADDR*/
2125 atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */
2127 case 3: /* DOMAINNAME */
2128 atomic_in( s, ptr, 1 ); /* recv name and port */
2129 atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
2131 case 4: /* IP v6 ADDR */
2132 atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */
2136 /* Conguraturation, connected via SOCKS5 server! */
2140 /* begin SOCKS protocol 4 relaying
2141 And no authentication is supported.
2143 There's SOCKS protocol version 4 and 4a. Protocol version
2144 4a has capability to resolve hostname by SOCKS server, so
2145 we don't need resolving IP address of destination host on
2148 Environment variable SOCKS_RESOLVE directs how to resolve
2149 IP addess. There's 3 keywords allowed; "local", "remote"
2150 and "both" (case insensitive). Keyword "local" means taht
2151 target host name is resolved by localhost resolver
2152 (usualy with gethostbyname()), "remote" means by remote
2153 SOCKS server, "both" means to try resolving by localhost
2156 SOCKS4 protocol and authentication of SOCKS5 protocol
2157 requires user name on connect request.
2158 User name is determined by following method.
2160 1. If server spec has user@hostname:port format then
2161 user part is used for this SOCKS server.
2163 2. Get user name from environment variable LOGNAME, USER
2168 begin_socks4_relay( SOCKET s )
2170 unsigned char buf[256], *ptr;
2172 debug( "begin_socks_relay()\n");
2174 /* make connect request packet
2176 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
2178 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
2181 PUT_BYTE( ptr++, 4); /* protocol version (4) */
2182 PUT_BYTE( ptr++, 1); /* CONNECT command */
2183 PUT_BYTE( ptr++, dest_port>>8); /* destination Port */
2184 PUT_BYTE( ptr++, dest_port&0xFF);
2185 /* destination IP */
2186 memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
2187 ptr += sizeof(dest_addr.sin_addr);
2188 if ( dest_addr.sin_addr.s_addr == 0 )
2189 *(ptr-1) = 1; /* fake, protocol 4a */
2191 if (relay_user == NULL)
2192 fatal( "Cannot determine user name.\n");
2193 strcpy( ptr, relay_user );
2194 ptr += strlen( relay_user ) +1;
2195 /* destination host name (for protocol 4a) */
2196 if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
2197 strcpy( ptr, dest_host );
2198 ptr += strlen( dest_host ) +1;
2200 /* send command and get response
2201 response is: VN:1, CD:1, PORT:2, ADDR:4 */
2202 atomic_out( s, buf, ptr-buf); /* send request */
2203 atomic_in( s, buf, 8 ); /* recv response */
2204 if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */
2205 error("Got error response: %d: '%s'.\n",
2206 buf[1], lookup(buf[1], socks4_rep_names));
2207 return -1; /* failed */
2210 /* Conguraturation, connected via SOCKS4 server! */
2215 sendf(SOCKET s, const char *fmt,...)
2217 static char buf[10240]; /* xxx, enough? */
2220 va_start( args, fmt );
2221 vsnprintf( buf, sizeof(buf), fmt, args );
2224 report_text(">>>", buf);
2225 if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
2226 debug("failed to send http request. errno=%d\n", socket_errno());
2232 const char *base64_table =
2233 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2236 make_base64_string(const char *str)
2241 int bits, data, src_len, dst_len;
2242 /* make base64 string */
2243 src_len = strlen(str);
2244 dst_len = (src_len+2)/3*4;
2245 buf = xmalloc(dst_len+1);
2247 src = (unsigned char *)str;
2248 dst = (unsigned char *)buf;
2249 while ( dst_len-- ) {
2251 data = (data << 8) | *src;
2256 *dst++ = base64_table[0x3F & (data >> (bits-6))];
2260 /* fix-up tail padding */
2261 switch ( src_len%3 ) {
2272 basic_auth (SOCKET s)
2276 const char *user = relay_user;
2280 /* Get username/password for authentication */
2282 fatal("Cannot decide username for proxy authentication.");
2283 if ((pass = determine_relay_password ()) == NULL &&
2284 (pass = readpass("Enter proxy authentication password for %s@%s: ",
2285 relay_user, relay_host)) == NULL)
2286 fatal("Cannot decide password for proxy authentication.");
2288 len = strlen(user)+strlen(pass)+1;
2289 userpass = xmalloc(len+1);
2290 snprintf(userpass, len+1, "%s:%s", user, pass);
2291 memset (pass, 0, strlen(pass));
2292 cred = make_base64_string(userpass);
2293 memset (userpass, 0, len);
2295 f_report = 0; /* don't report for security */
2296 ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
2298 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
2300 memset(cred, 0, strlen(cred));
2306 /* begin relaying via HTTP proxy
2307 Directs CONNECT method to proxy server to connect to
2308 destination host (and port). It may not be allowed on your
2312 begin_http_relay( SOCKET s )
2318 debug("begin_http_relay()\n");
2320 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
2322 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
2324 if (sendf(s,"\r\n") < 0)
2328 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
2329 debug("failed to read http response.\n");
2334 if (!strchr(buf, ' ')) {
2335 error ("Unexpected http response: '%s'.\n", buf);
2338 result = atoi(strchr(buf,' '));
2342 /* Conguraturation, connected via http proxy server! */
2343 debug("connected, start user session.\n");
2345 case 302: /* redirect */
2347 if (line_input(s, buf, sizeof(buf)))
2350 if (expect(buf, "Location: ")) {
2351 relay_host = cut_token(buf, "//");
2352 cut_token(buf, "/");
2353 relay_port = atoi(cut_token(buf, ":"));
2355 } while (strcmp(buf,"\r\n") != 0);
2358 /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
2359 * not strictly the correct response, but some proxies do send this (e.g.
2360 * Symantec's Raptor firewall) */
2361 case 401: /* WWW-Auth required */
2362 case 407: /* Proxy-Auth required */
2363 /** NOTE: As easy implementation, we support only BASIC scheme
2364 and ignore realm. */
2365 /* If proxy_auth_type is PROXY_AUTH_BASIC and get
2366 this result code, authentication was failed. */
2367 if (proxy_auth_type != PROXY_AUTH_NONE) {
2368 error("Authentication failed.\n");
2371 auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2373 if ( line_input(s, buf, sizeof(buf)) ) {
2377 if (expect(buf, auth_what)) {
2378 /* parse type and realm */
2379 char *scheme, *realm;
2380 scheme = cut_token(buf, " ");
2381 realm = cut_token(scheme, " ");
2382 if ( scheme == NULL || realm == NULL ) {
2383 debug("Invalid format of %s field.", auth_what);
2384 return START_ERROR; /* fail */
2386 /* check supported auth type */
2387 if (expect(scheme, "basic")) {
2388 proxy_auth_type = PROXY_AUTH_BASIC;
2390 debug("Unsupported authentication type: %s", scheme);
2393 } while (strcmp(buf,"\r\n") != 0);
2394 if ( proxy_auth_type == PROXY_AUTH_NONE ) {
2395 debug("Can't find %s in response header.", auth_what);
2403 debug("http proxy is not allowed.\n");
2406 /* skip to end of response header */
2408 if ( line_input(s, buf, sizeof(buf) ) ) {
2409 debug("Can't skip response headers\n");
2412 } while ( strcmp(buf,"\r\n") != 0 );
2417 /* begin relaying via TELNET proxy.
2418 Sends string specified by telnet_command (-c option) with
2419 replacing host name and port number to the socket. */
2421 begin_telnet_relay( SOCKET s )
2425 char *good_phrase = "connected to";
2426 char *bad_phrase_list[] = {
2427 " failed", " refused", " rejected", " closed"
2432 debug("begin_telnet_relay()\n");
2435 debug("good phrase: '%s'\n", good_phrase);
2436 debug("bad phrases");
2438 for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
2439 debug_("%c '%s'", sep, bad_phrase_list[i]);
2444 /* make request string with replacing %h by destination hostname
2445 and %p by port number, etc. */
2446 cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
2448 /* Sorry, we send request string now without waiting a prompt. */
2449 if (sendf(s, "%s\r\n", cmd) < 0) {
2455 /* Process answer from proxy until good or bad phrase is detected. We
2456 assume that the good phrase should be appeared only in the final
2457 line of proxy responses. Bad keywods in the line causes operation
2458 fail. First checks a good phrase, then checks bad phrases.
2459 If no match, continue reading line from proxy. */
2460 while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
2462 /* first, check good phrase */
2463 if (strstr(buf, good_phrase)) {
2464 debug("good phrase is detected: '%s'\n", good_phrase);
2467 /* then, check bad phrase */
2468 for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) {
2469 if (strstr(buf, bad_phrase_list[i]) != NULL) {
2470 debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]);
2475 debug("error reading from telnet proxy\n");
2483 Returns 1 if data is available, otherwise return 0
2491 if ( st.st_mode & _S_IFIFO ) {
2492 /* in case of PIPE */
2493 if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
2494 NULL, 0, NULL, &len, NULL) ) {
2495 if ( GetLastError() == ERROR_BROKEN_PIPE ) {
2496 /* PIPE source is closed */
2497 /* read() will detects EOF */
2500 fatal("PeekNamedPipe() failed, errno=%d\n",
2504 } else if ( st.st_mode & _S_IFREG ) {
2505 /* in case of regular file (redirected) */
2506 len = 1; /* always data ready */
2507 } else if ( _kbhit() ) {
2508 /* in case of console */
2515 /* relay byte from stdin to socket and fro socket to stdout.
2516 returns reason of termination */
2518 do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
2520 /** vars for local input data **/
2521 char lbuf[1024]; /* local input buffer */
2522 int lbuf_len; /* available data in lbuf */
2523 int f_local; /* read local input more? */
2524 /** vars for remote input data **/
2525 char rbuf[1024]; /* remote input buffer */
2526 int rbuf_len; /* available data in rbuf */
2527 int f_remote; /* read remote input more? */
2528 int close_reason = REASON_UNK; /* reason of end repeating */
2529 /** other variables **/
2532 struct timeval *tmo;
2534 struct timeval win32_tmo;
2537 /* repeater between stdin/out and socket */
2538 nfds = ((local_in<remote)? remote: local_in) +1;
2539 f_local = 1; /* yes, read from local */
2540 f_remote = 1; /* yes, read from remote */
2544 while ( f_local || f_remote ) {
2549 /** prepare for reading local input **/
2550 if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
2552 if ( local_type != LOCAL_SOCKET ) {
2553 /* select() on Winsock is not accept standard handle.
2554 So use select() with short timeout and checking data
2555 in stdin by another method. */
2556 win32_tmo.tv_sec = 0;
2557 win32_tmo.tv_usec = 10*1000; /* 10 ms */
2560 #endif /* !_WIN32 */
2561 FD_SET( local_in, &ifds );
2564 /** prepare for reading remote input **/
2565 if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
2566 FD_SET( remote, &ifds );
2569 /* FD_SET( local_out, ofds ); */
2570 /* FD_SET( remote, ofds ); */
2572 if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
2574 error( "select() failed, %d\n", socket_errno());
2575 return REASON_ERROR;
2578 /* fake ifds if local is stdio handle because
2579 select() of Winsock does not accept stdio
2581 if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
2582 FD_SET(0,&ifds); /* data ready */
2585 /* remote => local */
2586 if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
2587 len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
2589 debug("connection closed by peer\n");
2590 close_reason = REASON_CLOSED_BY_REMOTE;
2591 f_remote = 0; /* no more read from socket */
2593 } else if ( len == -1 ) {
2594 if (socket_errno() != ECONNRESET) {
2596 fatal("recv() faield, %d\n", socket_errno());
2598 debug("ECONNRESET detected\n");
2601 debug("recv %d bytes\n", len);
2602 if ( 1 < f_debug ) /* more verbose */
2603 report_bytes( "<<<", rbuf, rbuf_len);
2608 /* local => remote */
2609 if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) {
2610 if (local_type == LOCAL_SOCKET)
2611 len = recv(local_in, lbuf + lbuf_len,
2612 sizeof(lbuf)-lbuf_len, 0);
2614 len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
2617 debug("local input is EOF\n");
2618 if (!f_hold_session)
2619 shutdown(remote, 1); /* no-more writing */
2621 close_reason = REASON_CLOSED_BY_LOCAL;
2622 } else if ( len == -1 ) {
2623 /* error on reading from stdin */
2624 if (f_hold_session) {
2625 debug ("failed to read from local\n");
2627 close_reason = REASON_CLOSED_BY_LOCAL;
2629 fatal("recv() failed, errno = %d\n", errno);
2636 /* flush data in buffer to socket */
2637 if ( 0 < lbuf_len ) {
2638 len = send(remote, lbuf, lbuf_len, 0);
2639 if ( 1 < f_debug ) /* more verbose */
2640 report_bytes( ">>>", lbuf, lbuf_len);
2642 fatal("send() failed, %d\n", socket_errno());
2643 } else if ( 0 < len ) {
2644 /* move data on to top of buffer */
2645 debug("send %d bytes\n", len);
2648 memcpy( lbuf, lbuf+len, lbuf_len );
2649 assert( 0 <= lbuf_len );
2653 /* flush data in buffer to local output */
2654 if ( 0 < rbuf_len ) {
2655 if (local_type == LOCAL_SOCKET)
2656 len = send( local_out, rbuf, rbuf_len, 0);
2658 len = write( local_out, rbuf, rbuf_len);
2660 fatal("output (local) failed, errno=%d\n", errno);
2663 if ( len < rbuf_len )
2664 memcpy( rbuf, rbuf+len, rbuf_len );
2665 assert( 0 <= rbuf_len );
2667 if (f_local == 0 && f_hold_session) {
2668 debug ("closing local port without disconnecting from remote\n");
2670 shutdown (local_out, 2);
2676 return close_reason;
2680 accept_connection (u_short port)
2682 static int sock = -1;
2684 struct sockaddr_in name;
2685 struct sockaddr client;
2691 /* Create the socket. */
2692 debug("Creating source port to forward.\n");
2693 sock = socket (PF_INET, SOCK_STREAM, 0);
2695 fatal("socket() failed, errno=%d\n", socket_errno());
2697 setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
2698 (void*)&sockopt, sizeof(sockopt));
2700 /* Give the socket a name. */
2701 name.sin_family = AF_INET;
2702 name.sin_port = htons (port);
2703 name.sin_addr.s_addr = htonl (INADDR_ANY);
2704 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
2705 fatal ("bind() failed, errno=%d\n", socket_errno());
2707 if (listen( sock, 1) < 0)
2708 fatal ("listen() failed, errno=%d\n", socket_errno());
2710 /* wait for new connection with watching EOF of stdin. */
2711 debug ("waiting new connection at port %d (socket=%d)\n", port, sock);
2715 struct timeval *ptmo = NULL;
2719 tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */
2723 FD_SET ((SOCKET)sock, &ifds);
2725 FD_SET (0, &ifds); /* watch stdin */
2727 n = select (nfds, &ifds, NULL, NULL, ptmo);
2729 fatal ("select() failed, %d\n", socket_errno());
2733 if (0 < stdindatalen()) {
2734 FD_SET (0, &ifds); /* fake */
2739 if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
2741 debug ("Give-up waiting port because stdin is closed.");
2744 if (FD_ISSET(sock, &ifds))
2745 break; /* socket is stimulated */
2748 socklen = sizeof(client);
2749 connection = accept( sock, &client, &socklen);
2750 if ( connection < 0 )
2751 fatal ("accept() failed, errno=%d\n", socket_errno());
2757 /** Main of program **/
2759 main( int argc, char **argv )
2762 int remote; /* socket */
2763 int local_in; /* Local input */
2764 int local_out; /* Local output */
2768 WSAStartup( 0x101, &wsadata);
2771 /* initialization */
2773 getarg( argc, argv );
2774 debug("Program is $Revision$\n");
2776 /* Open local_in and local_out if forwarding a port */
2777 if ( local_type == LOCAL_SOCKET ) {
2778 /* Relay between local port and destination */
2779 local_in = local_out = accept_connection( local_port );
2781 /* Relay between stdin/stdout and desteination */
2785 _setmode(local_in, O_BINARY);
2786 _setmode(local_out, O_BINARY);
2792 if (0 < connect_timeout)
2793 set_timeout (connect_timeout);
2794 #endif /* not _WIN32 */
2796 /* make connection */
2797 if ( relay_method == METHOD_DIRECT ) {
2798 remote = open_connection (dest_host, dest_port);
2799 if ( remote == SOCKET_ERROR )
2800 fatal( "Unable to connect to destination host, errno=%d\n",
2803 remote = open_connection (relay_host, relay_port);
2804 if ( remote == SOCKET_ERROR )
2805 fatal( "Unable to connect to relay host, errno=%d\n",
2809 /** resolve destination host (SOCKS) **/
2810 #if !defined(_WIN32) && !defined(__CYGWIN32__)
2811 if (socks_ns.sin_addr.s_addr != 0)
2812 switch_ns (&socks_ns);
2813 #endif /* not _WIN32 && not __CYGWIN32__ */
2814 if (relay_method == METHOD_SOCKS &&
2815 socks_resolve == RESOLVE_LOCAL &&
2816 local_resolve (dest_host, &dest_addr) < 0) {
2817 fatal("Unknown host: %s", dest_host);
2820 /** relay negociation **/
2821 switch ( relay_method ) {
2823 if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
2824 ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
2825 fatal( "failed to begin relaying via SOCKS.\n");
2829 ret = begin_http_relay(remote);
2833 fatal("failed to begin relaying via HTTP.\n");
2837 /* retry with authentication */
2843 if (begin_telnet_relay(remote) < 0)
2844 fatal("failed to begin relaying via telnet.\n");
2847 debug("connected\n");
2850 if (0 < connect_timeout)
2852 #endif /* not _WIN32 */
2855 debug ("start relaying.\n");
2857 reason = do_repeater(local_in, local_out, remote);
2858 debug ("relaying done.\n");
2859 if (local_type == LOCAL_SOCKET &&
2860 reason == REASON_CLOSED_BY_LOCAL &&
2862 /* re-wait at local port without closing remote session */
2863 debug ("re-waiting at local port %d\n", local_port);
2864 local_in = local_out = accept_connection( local_port );
2865 debug ("re-start relaying\n");
2868 closesocket(remote);
2869 if ( local_type == LOCAL_SOCKET)
2870 closesocket(local_in);
2874 debug ("that's all, bye.\n");
2879 /* ------------------------------------------------------------
2881 compile-command: "cc connect.c -o connect"
2886 ------------------------------------------------------------ */
2888 /*** end of connect.c ***/