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$";
259 /* consider Borland C */
262 #define _setmode setmode
266 Win32 environment does not support -R option (vc and cygwin)
267 Win32 native compilers does not support -w option, yet (vc)
269 static char *usage = "usage: %s [-dnhst45] [-p local-port]"
272 "[-w timeout] \n" /* cygwin cannot -R */
273 #else /* not __CYGWIN32__ */
274 " \n" /* VC cannot -w nor -R */
275 #endif /* not __CYGWIN32__ */
276 #else /* not _WIN32 */
277 /* help message for UNIX */
278 "[-R resolve] [-w timeout] \n"
279 #endif /* not _WIN32 */
280 " [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
281 " [-T proxy-server[:port]]\n"
282 " [-c telnet-proxy-command]\n"
285 /* name of this program */
286 char *progname = NULL;
287 char *progdesc = "connect --- simple relaying command via proxy.";
288 char *rcs_revstr = "$Revision$";
291 /* set of character for strspn() */
292 const char *digits = "0123456789";
293 const char *dotdigits = "0123456789.";
298 /* report flag to hide secure information */
301 int connect_timeout = 0;
303 /* local input type */
304 #define LOCAL_STDIO 0
305 #define LOCAL_SOCKET 1
306 char *local_type_names[] = { "stdio", "socket" };
307 int local_type = LOCAL_STDIO;
308 u_short local_port = 0; /* option 'p' */
309 int f_hold_session = 0; /* option 'P' */
311 char *telnet_command = "telnet %h %p";
313 /* utiity types, pair holder of number and string */
319 /* relay method, server and port */
320 #define METHOD_UNDECIDED 0
321 #define METHOD_DIRECT 1
322 #define METHOD_SOCKS 2
323 #define METHOD_HTTP 3
324 #define METHOD_TELNET 4
325 char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
327 int relay_method = METHOD_UNDECIDED; /* relaying method */
328 char *relay_host = NULL; /* hostname of relay server */
329 u_short relay_port = 0; /* port of relay server */
330 char *relay_user = NULL; /* user name for auth */
332 /* destination target host and port */
333 char *dest_host = NULL;
334 struct sockaddr_in dest_addr;
335 u_short dest_port = 0;
337 /* informations for SOCKS */
338 #define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
339 #define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
340 #define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
341 #define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
342 #define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
343 #define SOCKS5_REP_REFUSED 0x05 /* connection refused */
344 #define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
345 #define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
346 #define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
347 #define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
349 LOOKUP_ITEM socks5_rep_names[] = {
350 { SOCKS5_REP_SUCCEEDED, "succeeded"},
351 { SOCKS5_REP_FAIL, "general SOCKS server failure"},
352 { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"},
353 { SOCKS5_REP_NUNREACH, "Network unreachable"},
354 { SOCKS5_REP_HUNREACH, "Host unreachable"},
355 { SOCKS5_REP_REFUSED, "connection refused"},
356 { SOCKS5_REP_EXPIRED, "TTL expired"},
357 { SOCKS5_REP_CNOTSUP, "Command not supported"},
358 { SOCKS5_REP_ANOTSUP, "Address not supported"},
359 { SOCKS5_REP_INVADDR, "Invalid address"},
363 /* SOCKS5 authentication methods */
364 #define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
365 #define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
366 #define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
367 #define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
368 #define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
369 #define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
370 #define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
372 #define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */
373 #define SOCKS4_REP_REJECTED 91 /* request rejected or failed */
374 #define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */
375 #define SOCKS4_REP_USERID 93 /* user id not matched */
377 LOOKUP_ITEM socks4_rep_names[] = {
378 { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"},
379 { SOCKS4_REP_REJECTED, "request rejected or failed"},
380 { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
381 { SOCKS4_REP_USERID, "user id not matched"},
385 #define RESOLVE_UNKNOWN 0
386 #define RESOLVE_LOCAL 1
387 #define RESOLVE_REMOTE 2
388 #define RESOLVE_BOTH 3
389 char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
391 int socks_version = 5; /* SOCKS protocol version */
392 int socks_resolve = RESOLVE_UNKNOWN;
393 struct sockaddr_in socks_ns;
394 char *socks5_auth = NULL;
396 /* Environment variable names */
397 #define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
398 #define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
399 #define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
401 #define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
402 #define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
403 #define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
405 #define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
406 #define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */
407 #define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */
408 #define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */
409 #define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */
411 #define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
412 #define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
413 #define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
415 #define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
417 #define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */
418 #define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
420 #define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
421 #define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT"
422 #define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT"
423 #define ENV_HTTP_DIRECT "HTTP_DIRECT"
424 #define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
426 #define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
427 #define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
429 /* Prefix string of HTTP_PROXY */
430 #define HTTP_PROXY_PREFIX "http://"
431 #define PROXY_AUTH_NONE 0
432 #define PROXY_AUTH_BASIC 1
433 #define PROXY_AUTH_DIGEST 2
434 int proxy_auth_type = PROXY_AUTH_NONE;
436 /* reason of end repeating */
437 #define REASON_UNK -2
438 #define REASON_ERROR -1
439 #define REASON_CLOSED_BY_LOCAL 0
440 #define REASON_CLOSED_BY_REMOTE 1
442 /* return value of relay start function. */
443 #define START_ERROR -1
445 #define START_RETRY 1
447 /* socket related definitions */
452 #define SOCKET_ERROR -1
456 #define socket_errno() WSAGetLastError()
458 #define closesocket close
459 #define socket_errno() (errno)
466 /* packet operation macro */
467 #define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
469 /* debug message output */
471 debug( const char *fmt, ... )
475 va_start( args, fmt );
476 fprintf(stderr, "DEBUG: ");
477 vfprintf( stderr, fmt, args );
483 debug_( const char *fmt, ... ) /* without prefix */
487 va_start( args, fmt );
488 vfprintf( stderr, fmt, args );
493 /* error message output */
495 error( const char *fmt, ... )
498 va_start( args, fmt );
499 fprintf(stderr, "ERROR: ");
500 vfprintf( stderr, fmt, args );
505 fatal( const char *fmt, ... )
508 va_start( args, fmt );
509 fprintf(stderr, "FATAL: ");
510 vfprintf( stderr, fmt, args );
517 xmalloc (size_t size)
519 void *ret = malloc(size);
521 fatal("Cannot allocate memory: %d bytes.\n", size);
526 downcase( char *buf )
536 expand_host_and_port (const char *fmt, const char *host, int port)
539 char *buf, *dst, *ptr;
540 size_t len = strlen(fmt) + strlen(host) + 20;
553 sprintf (dst, "%d", port);
560 dst = buf + strlen (buf);
561 } else if (*src == '\\') {
585 assert (strlen(buf) < len);
591 lookup_resolve( const char *str )
593 char *buf = strdup( str );
597 if ( strcmp( buf, "both" ) == 0 )
599 else if ( strcmp( buf, "remote" ) == 0 )
600 ret = RESOLVE_REMOTE;
601 else if ( strcmp( buf, "local" ) == 0 )
603 else if ( strspn(buf, dotdigits) == strlen(buf) ) {
604 #ifndef WITH_RESOLVER
605 fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
606 #endif /* not WITH_RESOLVER */
607 ret = RESOLVE_LOCAL; /* this case is also 'local' */
608 socks_ns.sin_addr.s_addr = inet_addr(buf);
609 socks_ns.sin_family = AF_INET;
612 ret = RESOLVE_UNKNOWN;
621 static char buf[1024];
622 DWORD size = sizeof(buf);
624 GetUserName( buf, &size);
626 #else /* not _WIN32 */
627 struct passwd *pw = getpwuid(getuid());
629 fatal("getpwuid() failed for uid: %d\n", getuid());
631 #endif /* not _WIN32 */
635 check STR is begin with substr with case-ignored comparison.
636 Return 1 if matched, otherwise 0.
639 expect( char *str, char *substr)
641 int len = strlen(substr);
642 while ( 0 < len-- ) {
643 if ( toupper(*str) != toupper(*substr) )
644 return 0; /* not matched */
647 return 1; /* good, matched */
651 /** PARAMETER operation **/
652 #define PARAMETER_FILE "/etc/connectrc"
653 #define PARAMETER_DOTFILE ".connectrc"
658 PARAMETER_ITEM parameter_table[] = {
659 { ENV_SOCKS_SERVER, NULL },
660 { ENV_SOCKS5_SERVER, NULL },
661 { ENV_SOCKS4_SERVER, NULL },
662 { ENV_SOCKS_RESOLVE, NULL },
663 { ENV_SOCKS5_RESOLVE, NULL },
664 { ENV_SOCKS4_RESOLVE, NULL },
665 { ENV_SOCKS5_USER, NULL },
666 { ENV_SOCKS5_PASSWD, NULL },
667 { ENV_SOCKS5_PASSWORD, NULL },
668 { ENV_HTTP_PROXY, NULL },
669 { ENV_HTTP_PROXY_USER, NULL },
670 { ENV_HTTP_PROXY_PASSWORD, NULL },
671 { ENV_CONNECT_USER, NULL },
672 { ENV_CONNECT_PASSWORD, NULL },
673 { ENV_SSH_ASKPASS, NULL },
674 { ENV_SOCKS5_DIRECT, NULL },
675 { ENV_SOCKS4_DIRECT, NULL },
676 { ENV_SOCKS_DIRECT, NULL },
677 { ENV_HTTP_DIRECT, NULL },
678 { ENV_CONNECT_DIRECT, NULL },
679 { ENV_SOCKS5_AUTH, NULL },
684 find_parameter_item(const char* name)
687 for( i = 0; parameter_table[i].name != NULL; i++ ){
688 if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
689 return ¶meter_table[i];
695 read_parameter_file_1(const char* name)
700 f = fopen(name, "r");
702 debug("Reading parameter file(%s)\n", name);
703 for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
704 char *p, *q, *param, *value;
705 p = strchr(lbuf, '\n');
707 fatal("%s:%d: buffer overflow\n", name, line);
709 p = strchr(lbuf, '#');
712 for ( p = lbuf; *p; p++ )
713 if( *p != ' ' && *p != '\t' ) break;
714 if ( *p == '\0' ) continue;
718 error("%s:%d: missing equal sign\n", name, line);
721 for ( q = p - 1; q >= lbuf; q-- )
722 if ( *q != ' ' && *q != '\t' ) break;
725 if ( *p != ' ' && *p != '\t' ) break;
728 for ( p--; p >= lbuf; p-- )
729 if ( *p != ' ' && *p != '\t' ) break;
731 if ( param && value ) {
732 PARAMETER_ITEM *item;
733 item = find_parameter_item(param);
734 if ( item == NULL ) {
735 error("%s:%d: unknown parameter `%s'\n", name, line, param);
738 item->value = strdup(value);
739 debug("Parameter `%s' is set to `%s'\n", param, value);
746 read_parameter_file(void)
753 read_parameter_file_1(PARAMETER_FILE);
755 pw = getpwuid(getuid());
757 fatal("getpwuid() failed for uid: %d\n", getuid());
758 name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
759 strcpy(name, pw->pw_dir);
760 strcat(name, "/" PARAMETER_DOTFILE);
761 read_parameter_file_1(name);
767 getparam(const char* name)
769 char *value = getenv(name);
770 if ( value == NULL ){
771 PARAMETER_ITEM *item = find_parameter_item(name);
779 /** DIRECT connection **/
780 #define MAX_DIRECT_ADDR_LIST 256
788 struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
789 int n_direct_addr_list = 0;
792 mask_addr (void *addr, void *mask, int addrlen)
797 while ( 0 < addrlen-- )
802 add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
804 struct in_addr iaddr;
806 if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
807 error("direct address table is full!\n");
811 mask_addr(&iaddr, mask, sizeof(iaddr));
812 s = strdup(inet_ntoa(iaddr));
813 debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
815 memcpy( &direct_addr_list[n_direct_addr_list].addr,
816 &iaddr, sizeof(iaddr));
817 memcpy( &direct_addr_list[n_direct_addr_list].mask,
818 mask, sizeof(*mask));
819 direct_addr_list[n_direct_addr_list].negative = negative;
820 n_direct_addr_list++;
825 parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
828 /* Assume already be splitted by separator
829 and formatted as folowing:
830 1) 12.34.56.789/255.255.255.0
833 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
839 assert( str != NULL );
840 debug("parsing address pair: '%s'\n", str);
844 dsta = (u_char*)&addr->s_addr;
845 dstm = (u_char*)&mask->s_addr;
846 for (i=0; i<4; i++ ) {
848 break; /* case of format #3 */
849 if ( !isdigit(*ptr) )
850 return -1; /* format error: */
851 *dsta++ = atoi( ptr );
852 *dstm++ = 255; /* automatic mask for format #3 */
853 while ( isdigit(*ptr) ) /* skip digits */
860 /* At this point, *ptr points '/' or EOS ('\0') */
862 return 0; /* complete as format #3 */
864 return -1; /* format error */
865 /* Now parse mask for format #1 or #2 */
867 mask->s_addr = 0; /* clear automatic mask */
869 if ( strchr( ptr, '.') ) {
870 /* case of format #1 */
871 dstm = (u_char*)&mask->s_addr;
872 for (i=0; i<4; i++) {
873 if ( !isdigit(*ptr) )
874 return -1; /* format error: */
876 while ( isdigit(*ptr) ) /* skip digits */
881 break; /* from for loop */
883 /* complete as format #1 */
885 /* case of format #2 */
886 if ( !isdigit(*ptr) )
887 return -1; /* format error: */
890 return -1; /* format error */
891 mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
892 /* complete as format #1 */
898 initialize_direct_addr (void)
902 char *env = NULL, *beg, *next, *envkey = NULL;
903 struct in_addr addr, mask;
905 if ( relay_method == METHOD_SOCKS ){
906 if ( socks_version == 5 )
907 envkey = ENV_SOCKS5_DIRECT;
909 envkey = ENV_SOCKS4_DIRECT;
910 env = getparam(envkey);
912 env = getparam(ENV_SOCKS_DIRECT);
913 } else if ( relay_method == METHOD_HTTP ){
914 env = getparam(ENV_HTTP_DIRECT);
918 env = getparam(ENV_CONNECT_DIRECT);
921 return; /* no entry */
922 debug("making direct addr list from: '%s'\n", env);
923 env = strdup( env ); /* reallocate to modify */
927 if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
928 error("too many entries in %s", envkey);
929 break; /* from do loop */
931 next = strchr( beg, ',');
941 if ( !parse_addr_pair( beg, &addr, &mask ) ) {
942 add_direct_addr( &addr, &mask, negative );
944 error("invalid addr format in %s: %s\n", envkey, beg);
948 } while ( next != NULL );
955 cmp_addr (void *addr1, void *addr2, int addrlen)
957 return memcmp( addr1, addr2, addrlen );
961 is_direct_address (const struct sockaddr_in *addr)
964 struct in_addr saddr, iaddr;
966 saddr = addr->sin_addr;
968 /* Note: assume IPV4 address !! */
969 for (i=0; i<n_direct_addr_list; i++ ) {
971 mask_addr( &iaddr, &direct_addr_list[i].mask,
972 sizeof(struct in_addr));
973 if (cmp_addr(&iaddr, &direct_addr_list[i].addr,
974 sizeof(struct in_addr)) == 0) {
975 if (direct_addr_list[i].negative) {
976 debug("negative match, addr to be SOCKSify: %s\n",
978 return 0; /* not direct */
980 if (!direct_addr_list[i].negative) {
981 debug("positive match, addr to be direct: %s\n",
983 return 1; /* direct*/
987 debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
988 return 0; /* not direct */
992 /** TTY operation **/
998 intr_handler(int sig)
1004 tty_change_echo(int fd, int enable)
1006 static struct termios ntio, otio; /* new/old termios */
1007 static sigset_t nset, oset; /* new/old sigset */
1008 static struct sigaction nsa, osa; /* new/old sigaction */
1009 static int disabled = 0;
1011 if ( disabled && enable ) {
1013 tcsetattr(fd, TCSANOW, &otio);
1015 /* resotore sigaction */
1016 sigprocmask(SIG_SETMASK, &oset, NULL);
1017 sigaction(SIGINT, &osa, NULL);
1018 if ( intr_flag != 0 ) {
1019 /* re-generate signal */
1020 kill(getpid(), SIGINT);
1025 } else if (!disabled && !enable) {
1026 /* set SIGINTR handler and break syscall on singal */
1028 sigaddset(&nset, SIGTSTP);
1029 sigprocmask(SIG_BLOCK, &nset, &oset);
1031 memset(&nsa, 0, sizeof(nsa));
1032 nsa.sa_handler = intr_handler;
1033 sigaction(SIGINT, &nsa, &osa);
1035 if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
1038 ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
1039 (void) tcsetattr(fd, TCSANOW, &ntio);
1046 #define TTY_NAME "/dev/tty"
1048 tty_readpass( const char *prompt, char *buf, size_t size )
1052 tty = open(TTY_NAME, O_RDWR);
1054 error("Unable to open %s\n", TTY_NAME);
1055 return -1; /* can't open tty */
1058 return -1; /* no room */
1059 write(tty, prompt, strlen(prompt));
1061 tty_change_echo(tty, 0); /* disable echo */
1062 ret = read(tty,buf, size-1);
1063 tty_change_echo(tty, 1); /* restore */
1064 write(tty, "\n", 1); /* new line */
1066 if ( strchr(buf,'\n') == NULL )
1076 w32_intr_handler(DWORD dwCtrlType)
1078 if ( dwCtrlType == CTRL_C_EVENT ) {
1086 #define tty_readpass w32_tty_readpass
1088 w32_tty_readpass( const char *prompt, char *buf, size_t size )
1090 HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
1091 0, NULL, OPEN_EXISTING, 0, NULL);
1092 HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
1093 0, NULL, OPEN_EXISTING, 0, NULL);
1097 if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
1098 fatal("Cannot open console. (errno=%d)", GetLastError());
1100 WriteFile(out, prompt, strlen(prompt), &bytes, 0);
1101 SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
1102 GetConsoleMode(in, &mode);
1103 SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
1104 ret = ReadFile(in, buf, size, &bytes, 0);
1105 SetConsoleMode(in, mode); /* enable echo */
1106 SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
1108 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
1109 WriteFile(out,"\n", 1, &bytes, 0);
1117 /*** User / Password ***/
1119 /* SOCKS5 and HTTP Proxy authentication may requires username and
1120 password. We ll give it via environment variable or tty.
1121 Username and password for authentication are decided by
1124 Username is taken from
1125 1) server location spec (i.e. user@host:port)
1126 2) environment variables (see tables.1)
1127 3) system account name currently logged in.
1129 Table.1 Order of environment variables for username
1131 | SOCKS v5 | SOCKS v4 | HTTP proxy |
1132 --+-------------+-------------+-----------------+
1133 1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
1134 --+-------------+-------------+ |
1136 --+---------------------------+-----------------+
1138 --+---------------------------------------------+
1140 Password is taken from
1141 1) by environment variables (see table.2)
1142 2) by entering from tty.
1144 Table.2 Order of environment variables for password
1146 | SOCKS v5 | HTTP proxy |
1147 --+-----------------+---------------------+
1148 1 | SOCKS5_PASSWD | |
1149 --+-----------------+ HTTP_PROXY_PASSWORD |
1150 2 | SOCKS5_PASSWORD | |
1151 --+-----------------+---------------------+
1152 3 | CONNECT_PASSWORD |
1153 --+---------------------------------------+
1155 Note: SOCKS5_PASSWD which is added in rev. 1.79
1156 to share value with NEC SOCKS implementation.
1160 determine_relay_user ()
1163 /* get username from environment variable, or system. */
1164 if (relay_method == METHOD_SOCKS) {
1165 if (user == NULL && socks_version == 5)
1166 user = getparam (ENV_SOCKS5_USER);
1167 if (user == NULL && socks_version == 4)
1168 user = getparam (ENV_SOCKS4_USER);
1170 user = getparam (ENV_SOCKS_USER);
1171 } else if (relay_method == METHOD_HTTP) {
1173 user = getparam (ENV_HTTP_PROXY_USER);
1176 user = getparam (ENV_CONNECT_USER);
1177 /* determine relay user by system call if not yet. */
1179 user = getusername();
1184 determine_relay_password ()
1187 if (pass == NULL && relay_method == METHOD_HTTP)
1188 pass = getparam(ENV_HTTP_PROXY_PASSWORD);
1189 if (pass == NULL && relay_method == METHOD_SOCKS)
1190 pass = getparam(ENV_SOCKS5_PASSWD);
1191 if (pass == NULL && relay_method == METHOD_SOCKS)
1192 pass = getparam(ENV_SOCKS5_PASSWORD);
1194 pass = getparam(ENV_CONNECT_PASSWORD);
1199 /*** network operations ***/
1203 Determine relay informations:
1204 method, host, port, and username.
1205 1st arg, METHOD should be METHOD_xxx.
1206 2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
1207 hostname is domain name or dot notation.
1208 If port is omitted, use 80 for METHOD_HTTP method,
1209 use 1080 for METHOD_SOCKS method.
1210 Username is also able to given by 3rd. format.
1211 2nd argument SPEC can be NULL. if NULL, use environment variable.
1214 set_relay( int method, char *spec )
1216 char *buf, *sep, *resolve;
1218 relay_method = method;
1220 read_parameter_file();
1221 initialize_direct_addr();
1222 if (n_direct_addr_list == 0) {
1226 for ( i=0; i<n_direct_addr_list; i++ ) {
1228 s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
1229 s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
1230 debug(" #%d: %c%s/%s\n", i,
1231 (direct_addr_list[i].negative)? '!': ' ',
1240 return -1; /* nothing to do */
1243 if ( spec == NULL ) {
1244 switch ( socks_version ) {
1246 spec = getparam(ENV_SOCKS5_SERVER);
1249 spec = getparam(ENV_SOCKS4_SERVER);
1254 spec = getparam(ENV_SOCKS_SERVER);
1257 fatal("Failed to determine SOCKS server.\n");
1258 relay_port = 1080; /* set default first */
1260 /* determine resolve method */
1261 if ( socks_resolve == RESOLVE_UNKNOWN ) {
1262 if ( ((socks_version == 5) &&
1263 ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
1264 ((socks_version == 4) &&
1265 ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
1266 ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
1267 socks_resolve = lookup_resolve( resolve );
1268 if ( socks_resolve == RESOLVE_UNKNOWN )
1269 fatal("Invalid resolve method: %s\n", resolve);
1272 if ( socks_version == 5 )
1273 socks_resolve = RESOLVE_REMOTE;
1275 socks_resolve = RESOLVE_LOCAL;
1282 spec = getparam(ENV_HTTP_PROXY);
1284 fatal("You must specify http proxy server\n");
1285 relay_port = 80; /* set default first */
1289 spec = getparam(ENV_TELNET_PROXY);
1291 fatal("You must specify telnet proxy server\n");
1292 relay_port = 23; /* set default first */
1295 if (expect( spec, HTTP_PROXY_PREFIX)) {
1296 /* URL format like: "http://server:port/" */
1297 /* extract server:port part */
1298 buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
1299 buf[strcspn(buf, "/")] = '\0';
1301 /* assume spec is aready "server:port" format */
1302 buf = strdup( spec );
1306 /* check username in spec */
1307 sep = strchr( spec, '@' );
1308 if ( sep != NULL ) {
1310 relay_user = strdup( spec );
1313 if (relay_user == NULL)
1314 relay_user = determine_relay_user();
1316 /* split out hostname and port number from spec */
1317 sep = strchr(spec,':');
1318 if ( sep == NULL ) {
1319 /* hostname only, port is already set as default */
1320 relay_host = strdup( spec );
1322 /* hostname and port */
1323 relay_port = atoi(sep+1);
1325 relay_host = strdup( spec );
1333 resolve_port( const char *service )
1336 if ( service[strspn (service, digits)] == '\0' ) {
1337 /* all digits, port number */
1338 port = atoi(service);
1340 /* treat as service name */
1341 struct servent *ent;
1342 ent = getservbyname( service, NULL );
1343 if ( ent == NULL ) {
1344 debug("Unknown service, '%s'\n", service);
1347 port = ntohs(ent->s_port);
1348 debug("service: %s => %d\n", service, port);
1351 return (u_short)port;
1359 ptr = strstr(rcs_revstr, ": ");
1361 revstr = strdup("unknown");
1365 len = strspn(ptr, dotdigits);
1367 revstr = xmalloc(len+1);
1368 memcpy(revstr, ptr, len);
1374 getarg( int argc, char **argv )
1377 char *ptr, *server = (char*)NULL;
1378 int method = METHOD_DIRECT;
1384 while ( (0 < argc) && (**argv == '-') ) {
1388 case 's': /* use SOCKS */
1389 method = METHOD_SOCKS;
1392 case 'n': /* no proxy */
1393 method = METHOD_DIRECT;
1396 case 'h': /* use http-proxy */
1397 method = METHOD_HTTP;
1400 method = METHOD_TELNET;
1403 case 'S': /* specify SOCKS server */
1406 method = METHOD_SOCKS;
1409 error("option '-%c' needs argument.\n", *ptr);
1414 case 'H': /* specify http-proxy server */
1417 method = METHOD_HTTP;
1420 error("option '-%c' needs argument.\n", *ptr);
1424 case 'T': /* specify telnet proxy server */
1427 method = METHOD_TELNET;
1430 error("option '-%c' needs argument.\n", *ptr);
1438 telnet_command = *argv;
1440 error("option '%c' needs argument.\n", *ptr);
1448 case 'p': /* specify port to forward */
1451 local_type = LOCAL_SOCKET;
1452 local_port = resolve_port(*argv);
1454 error("option '-%c' needs argument.\n", *ptr);
1463 connect_timeout = atoi(*argv);
1465 error("option '-%c' needs argument.\n", *ptr);
1469 #endif /* not _WIN32 */
1482 socks5_auth = *argv;
1484 error("option '-%c' needs argument.\n", *ptr);
1489 case 'R': /* specify resolve method */
1492 socks_resolve = lookup_resolve( *argv );
1494 error("option '-%c' needs argument.\n", *ptr);
1499 case 'V': /* print version */
1500 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1503 case 'd': /* debug mode */
1508 error("unknown option '-%c'\n", *ptr);
1520 set_relay( method, server );
1522 /* check destination HOST (MUST) */
1524 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1525 fprintf(stderr, usage, progname);
1528 dest_host = argv[0];
1529 /* decide port or service name from programname or argument */
1530 if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
1531 ((ptr=strchr( progname, '\\')) != NULL) )
1535 if ( dest_port == 0 ) {
1536 /* accept only if -P is not specified. */
1538 /* get port number from argument (prior to progname) */
1539 /* NOTE: This way is for cvs ext method. */
1540 dest_port = resolve_port(argv[1]);
1541 } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
1542 /* decide port number from program name */
1543 char *str = strdup( ptr+8 );
1544 str[strcspn( str, "." )] = '\0';
1545 dest_port = resolve_port(str);
1549 /* check port number */
1550 if ( dest_port <= 0 ) {
1551 error( "You must specify the destination port correctly.\n");
1555 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
1556 error("Invalid relay port: %d\n", dest_port);
1562 /* report for debugging */
1563 debug("relay_method = %s (%d)\n",
1564 method_names[relay_method], relay_method);
1565 if ( relay_method != METHOD_DIRECT ) {
1566 debug("relay_host=%s\n", relay_host);
1567 debug("relay_port=%d\n", relay_port);
1568 debug("relay_user=%s\n", relay_user);
1570 if ( relay_method == METHOD_SOCKS ) {
1571 debug("socks_version=%d\n", socks_version);
1572 debug("socks_resolve=%s (%d)\n",
1573 resolve_names[socks_resolve], socks_resolve);
1575 debug("local_type=%s\n", local_type_names[local_type]);
1576 if ( local_type == LOCAL_SOCKET ) {
1577 debug("local_port=%d\n", local_port);
1579 debug (" with holding remote session.\n");
1581 debug("dest_host=%s\n", dest_host);
1582 debug("dest_port=%d\n", dest_port);
1584 fprintf(stderr, usage, progname);
1591 /* Time-out feature is not allowed for Win32 native compilers. */
1592 /* MSVC and Borland C cannot but Cygwin and UNIXes can. */
1594 /* timeout signal hander */
1598 debug( "timed out\n" );
1599 signal( SIGALRM, SIG_IGN );
1603 /* set timeout param = seconds, 0 clears */
1605 set_timeout(int timeout)
1607 /* This feature is allowed for UNIX or cygwin environments, currently */
1608 if ( timeout == 0 ) {
1609 debug( "clearing timeout\n" );
1610 signal( SIGALRM, SIG_IGN );
1613 debug( "setting timeout: %d seconds\n", timeout );
1614 signal(SIGALRM, (void *)sig_timeout);
1620 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1622 switch_ns (struct sockaddr_in *ns)
1625 memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
1627 debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
1629 #endif /* !_WIN32 && !__CYGWIN32__ */
1632 TODO: fallback if askpass execution failed.
1636 local_resolve (const char *host, struct sockaddr_in *addr)
1638 struct hostent *ent;
1639 if ( strspn(host, dotdigits) == strlen(host) ) {
1640 /* given by IPv4 address */
1641 addr->sin_family = AF_INET;
1642 addr->sin_addr.s_addr = inet_addr(host);
1644 debug("resolving host by name: %s\n", host);
1645 ent = gethostbyname (host);
1647 memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
1648 addr->sin_family = ent->h_addrtype;
1649 debug("resolved: %s (%s)\n",
1650 host, inet_ntoa(addr->sin_addr));
1652 debug("failed to resolve locally.\n");
1653 return -1; /* failed */
1656 return 0; /* good */
1660 open_connection( const char *host, u_short port )
1663 struct sockaddr_in saddr;
1665 if ( relay_method == METHOD_DIRECT ) {
1668 } else if ((local_resolve (dest_host, &saddr) >= 0)&&
1669 (is_direct_address(&saddr))) {
1670 debug("%s is connected directly\n", dest_host);
1671 relay_method = METHOD_DIRECT;
1679 if (local_resolve (host, &saddr) < 0) {
1680 error("can't resolve hostname: %s\n", host);
1681 return SOCKET_ERROR;
1683 saddr.sin_port = htons(port);
1685 debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
1686 s = socket( AF_INET, SOCK_STREAM, 0 );
1687 if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
1689 debug( "connect() failed.\n");
1690 return SOCKET_ERROR;
1696 report_text( char *prefix, char *buf )
1698 static char work[1024];
1704 return; /* don't report */
1705 debug("%s \"", prefix);
1707 memset( work, 0, sizeof(work));
1709 while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) {
1711 case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
1712 case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
1713 case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
1714 case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
1716 if ( isprint(*buf) ) {
1719 sprintf( tmp, "\\x%02X", (unsigned char)*buf);
1734 report_bytes( char *prefix, char *buf, int len )
1738 debug( "%s", prefix );
1740 fprintf( stderr, " %02x", *(unsigned char *)buf);
1744 fprintf(stderr, "\n");
1749 atomic_out( SOCKET s, char *buf, int size )
1753 assert( buf != NULL );
1757 while ( 0 < size ) {
1758 len = send( s, buf+ret, size, 0 );
1760 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1765 debug("atomic_out() [some bytes]\n");
1766 debug(">>> xx xx xx xx ...\n");
1768 debug("atomic_out() [%d bytes]\n", ret);
1769 report_bytes(">>>", buf, ret);
1775 atomic_in( SOCKET s, char *buf, int size )
1779 assert( buf != NULL );
1784 while ( 0 < size ) {
1785 len = recv( s, buf+ret, size, 0 );
1787 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1788 } else if ( len == 0 ) {
1789 fatal( "Connection closed by peer.\n");
1795 debug("atomic_in() [some bytes]\n");
1796 debug("<<< xx xx xx xx ...\n");
1798 debug("atomic_in() [%d bytes]\n", ret);
1799 report_bytes("<<<", buf, ret);
1805 line_input( SOCKET s, char *buf, int size )
1809 return 0; /* no error */
1811 while ( 0 < size ) {
1812 switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */
1814 error("recv() error\n");
1815 return -1; /* error */
1817 size = 0; /* end of stream */
1820 /* continue reading until last 1 char is EOL? */
1821 if ( *dst == '\n' ) {
1832 report_text( "<<<", buf);
1837 Span token in given string STR until char in DELIM is appeared.
1838 Then replace contiguous DELIMS with '\0' for string termination
1839 and returns next pointer.
1840 If no next token, return NULL.
1843 cut_token( char *str, char *delim)
1845 char *ptr = str + strcspn(str, delim);
1846 char *end = ptr + strspn(ptr, delim);
1855 lookup(int num, LOOKUP_ITEM *items)
1858 while (0 <= items[i].num) {
1859 if (items[i].num == num)
1860 return items[i].str;
1867 password input routine
1868 Use ssh-askpass (same mechanism to OpenSSH)
1871 readpass( const char* prompt, ...)
1873 static char buf[1000]; /* XXX, don't be fix length */
1875 va_start(args, prompt);
1876 vsprintf(buf, prompt, args);
1879 if ( getparam(ENV_SSH_ASKPASS)
1880 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1881 && getenv("DISPLAY")
1882 #endif /* not _WIN32 && not __CYGWIN32__ */
1884 /* use ssh-askpass to get password */
1886 char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
1887 cmd = xmalloc(strlen(askpass) +1 +1 +strlen(buf) +1);
1888 sprintf(cmd, "%s \"%s\"", askpass, buf);
1889 fp = popen(cmd, "r");
1892 return NULL; /* fail */
1894 if (fgets(buf, sizeof(buf), fp) == NULL)
1895 return NULL; /* fail */
1898 tty_readpass( buf, buf, sizeof(buf));
1900 buf[strcspn(buf, "\r\n")] = '\0';
1905 socks5_do_auth_userpass( int s )
1907 unsigned char buf[1024], *ptr;
1911 /* do User/Password authentication. */
1912 /* This feature requires username and password from
1913 command line argument or environment variable,
1915 if (relay_user == NULL)
1916 fatal("cannot determine user name.\n");
1918 /* get password from environment variable if exists. */
1919 if ((pass=determine_relay_password()) == NULL &&
1920 (pass=readpass("Enter SOCKS5 password for %s@%s: ",
1921 relay_user, relay_host)) == NULL)
1922 fatal("Cannot get password for user: %s\n", relay_user);
1924 /* make authentication packet */
1926 PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */
1927 len = strlen( relay_user ); /* ULEN and UNAME */
1928 PUT_BYTE( ptr++, len );
1929 strcpy( ptr, relay_user );
1931 len = strlen( pass ); /* PLEN and PASSWD */
1932 PUT_BYTE( ptr++, strlen(pass));
1933 strcpy( ptr, pass );
1935 memset (pass, 0, strlen(pass)); /* erase password */
1937 /* send it and get answer */
1939 atomic_out( s, buf, ptr-buf );
1941 atomic_in( s, buf, 2 );
1945 return 0; /* success */
1947 return -1; /* fail */
1951 socks5_getauthname( int auth )
1954 case SOCKS5_AUTH_REJECT: return "REJECTED";
1955 case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
1956 case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
1957 case SOCKS5_AUTH_USERPASS: return "USERPASS";
1958 case SOCKS5_AUTH_CHAP: return "CHAP";
1959 case SOCKS5_AUTH_EAP: return "EAP";
1960 case SOCKS5_AUTH_MAF: return "MAF";
1961 default: return "(unknown)";
1970 AUTH_METHOD_ITEM socks5_auth_table[] = {
1971 { "none", SOCKS5_AUTH_NOAUTH },
1972 { "gssapi", SOCKS5_AUTH_GSSAPI },
1973 { "userpass", SOCKS5_AUTH_USERPASS },
1974 { "chap", SOCKS5_AUTH_CHAP },
1979 socks5_auth_parse_1(char *start, char *end){
1981 for ( ; *start; start++ )
1982 if ( *start != ' ' && *start != '\t') break;
1983 for ( end--; end >= start; end-- ) {
1984 if ( *end != ' ' && *end != '\t'){
1990 for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
1991 if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
1992 return socks5_auth_table[i].auth;
1995 fatal("Unknown auth method: %s\n", start);
2000 socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
2003 while ( i < max_auth ) {
2004 end = strchr(start, ',');
2005 if (*start && end) {
2006 auth_list[i++] = socks5_auth_parse_1(start, end);
2012 if ( *start && ( i < max_auth ) ){
2013 for( end = start; *end; end++ );
2014 auth_list[i++] = socks5_auth_parse_1(start, end);
2016 fatal("Too much auth method.\n");
2021 /* begin SOCKS5 relaying
2022 And no authentication is supported.
2025 begin_socks5_relay( SOCKET s )
2027 unsigned char buf[256], *ptr, *env = socks5_auth;
2028 unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
2029 int len, auth_result, i;
2031 debug( "begin_socks_relay()\n");
2033 /* request authentication */
2035 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2038 env = getparam(ENV_SOCKS5_AUTH);
2039 if ( env == NULL ) {
2040 /* add no-auth authentication */
2041 auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
2042 /* add user/pass authentication */
2043 auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
2045 n_auth = socks5_auth_parse(env, auth_list, 10);
2047 PUT_BYTE( ptr++, n_auth); /* num auth */
2048 for (i=0; i<n_auth; i++) {
2049 debug("available auth method[%d] = %s (0x%02x)\n",
2050 i, socks5_getauthname(auth_list[i]), auth_list[i]);
2051 PUT_BYTE( ptr++, auth_list[i]); /* authentications */
2053 atomic_out( s, buf, ptr-buf ); /* send requst */
2054 atomic_in( s, buf, 2 ); /* recv response */
2055 if ( (buf[0] != 5) || /* ver5 response */
2056 (buf[1] == 0xFF) ) { /* check auth method */
2057 error("No auth method accepted.\n");
2060 auth_method = buf[1];
2062 debug("auth method: %s\n", socks5_getauthname(auth_method));
2064 switch ( auth_method ) {
2065 case SOCKS5_AUTH_REJECT:
2066 error("No acceptable authentication method\n");
2067 return -1; /* fail */
2069 case SOCKS5_AUTH_NOAUTH:
2074 case SOCKS5_AUTH_USERPASS:
2075 auth_result = socks5_do_auth_userpass(s);
2079 error("Unsupported authentication method: %s\n",
2080 socks5_getauthname( auth_method ));
2081 return -1; /* fail */
2083 if ( auth_result != 0 ) {
2084 error("Authentication failed.\n");
2087 /* request to connect */
2089 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2090 PUT_BYTE( ptr++, 1); /* CMD: CONNECT */
2091 PUT_BYTE( ptr++, 0); /* FLG: 0 */
2092 if ( dest_addr.sin_addr.s_addr == 0 ) {
2093 /* resolved by SOCKS server */
2094 PUT_BYTE( ptr++, 3); /* ATYP: DOMAINNAME */
2095 len = strlen(dest_host);
2096 PUT_BYTE( ptr++, len); /* DST.ADDR (len) */
2097 memcpy( ptr, dest_host, len ); /* (hostname) */
2100 /* resolved localy */
2101 PUT_BYTE( ptr++, 1 ); /* ATYP: IPv4 */
2102 memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
2103 ptr += sizeof(dest_addr.sin_addr);
2105 PUT_BYTE( ptr++, dest_port>>8); /* DST.PORT */
2106 PUT_BYTE( ptr++, dest_port&0xFF);
2107 atomic_out( s, buf, ptr-buf); /* send request */
2108 atomic_in( s, buf, 4 ); /* recv response */
2109 if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */
2110 error("Got error response from SOCKS server: %d (%s).\n",
2111 buf[1], lookup(buf[1], socks5_rep_names));
2115 switch ( buf[3] ) { /* case by ATYP */
2116 case 1: /* IP v4 ADDR*/
2117 atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */
2119 case 3: /* DOMAINNAME */
2120 atomic_in( s, ptr, 1 ); /* recv name and port */
2121 atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
2123 case 4: /* IP v6 ADDR */
2124 atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */
2128 /* Conguraturation, connected via SOCKS5 server! */
2132 /* begin SOCKS protocol 4 relaying
2133 And no authentication is supported.
2135 There's SOCKS protocol version 4 and 4a. Protocol version
2136 4a has capability to resolve hostname by SOCKS server, so
2137 we don't need resolving IP address of destination host on
2140 Environment variable SOCKS_RESOLVE directs how to resolve
2141 IP addess. There's 3 keywords allowed; "local", "remote"
2142 and "both" (case insensitive). Keyword "local" means taht
2143 target host name is resolved by localhost resolver
2144 (usualy with gethostbyname()), "remote" means by remote
2145 SOCKS server, "both" means to try resolving by localhost
2148 SOCKS4 protocol and authentication of SOCKS5 protocol
2149 requires user name on connect request.
2150 User name is determined by following method.
2152 1. If server spec has user@hostname:port format then
2153 user part is used for this SOCKS server.
2155 2. Get user name from environment variable LOGNAME, USER
2160 begin_socks4_relay( SOCKET s )
2162 unsigned char buf[256], *ptr;
2164 debug( "begin_socks_relay()\n");
2166 /* make connect request packet
2168 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
2170 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
2173 PUT_BYTE( ptr++, 4); /* protocol version (4) */
2174 PUT_BYTE( ptr++, 1); /* CONNECT command */
2175 PUT_BYTE( ptr++, dest_port>>8); /* destination Port */
2176 PUT_BYTE( ptr++, dest_port&0xFF);
2177 /* destination IP */
2178 memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
2179 ptr += sizeof(dest_addr.sin_addr);
2180 if ( dest_addr.sin_addr.s_addr == 0 )
2181 *(ptr-1) = 1; /* fake, protocol 4a */
2183 if (relay_user == NULL)
2184 fatal( "Cannot determine user name.\n");
2185 strcpy( ptr, relay_user );
2186 ptr += strlen( relay_user ) +1;
2187 /* destination host name (for protocol 4a) */
2188 if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
2189 strcpy( ptr, dest_host );
2190 ptr += strlen( dest_host ) +1;
2192 /* send command and get response
2193 response is: VN:1, CD:1, PORT:2, ADDR:4 */
2194 atomic_out( s, buf, ptr-buf); /* send request */
2195 atomic_in( s, buf, 8 ); /* recv response */
2196 if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */
2197 error("Got error response: %d: '%s'.\n",
2198 buf[1], lookup(buf[1], socks4_rep_names));
2199 return -1; /* failed */
2202 /* Conguraturation, connected via SOCKS4 server! */
2207 sendf(SOCKET s, const char *fmt,...)
2209 static char buf[10240]; /* xxx, enough? */
2212 va_start( args, fmt );
2213 vsprintf( buf, fmt, args );
2216 report_text(">>>", buf);
2217 if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
2218 debug("failed to send http request. errno=%d\n", socket_errno());
2224 const char *base64_table =
2225 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2228 make_base64_string(const char *str)
2233 int bits, data, src_len, dst_len;
2234 /* make base64 string */
2235 src_len = strlen(str);
2236 dst_len = (src_len+2)/3*4;
2237 buf = xmalloc(dst_len+1);
2239 src = (unsigned char *)str;
2240 dst = (unsigned char *)buf;
2241 while ( dst_len-- ) {
2243 data = (data << 8) | *src;
2248 *dst++ = base64_table[0x3F & (data >> (bits-6))];
2252 /* fix-up tail padding */
2253 switch ( src_len%3 ) {
2264 basic_auth (SOCKET s)
2268 const char *user = relay_user;
2272 /* Get username/password for authentication */
2274 fatal("Cannot decide username for proxy authentication.");
2275 if ((pass = determine_relay_password ()) == NULL &&
2276 (pass = readpass("Enter proxy authentication password for %s@%s: ",
2277 relay_user, relay_host)) == NULL)
2278 fatal("Cannot decide password for proxy authentication.");
2280 len = strlen(user)+strlen(pass)+1;
2281 userpass = xmalloc(len+1);
2282 sprintf(userpass,"%s:%s", user, pass);
2283 memset (pass, 0, strlen(pass));
2284 cred = make_base64_string(userpass);
2285 memset (userpass, 0, len);
2287 f_report = 0; /* don't report for security */
2288 ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
2290 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
2292 memset(cred, 0, strlen(cred));
2298 /* begin relaying via HTTP proxy
2299 Directs CONNECT method to proxy server to connect to
2300 destination host (and port). It may not be allowed on your
2304 begin_http_relay( SOCKET s )
2310 debug("begin_http_relay()\n");
2312 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
2314 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
2316 if (sendf(s,"\r\n") < 0)
2320 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
2321 debug("failed to read http response.\n");
2326 if (!strchr(buf, ' ')) {
2327 error ("Unexpected http response: '%s'.\n", buf);
2330 result = atoi(strchr(buf,' '));
2334 /* Conguraturation, connected via http proxy server! */
2335 debug("connected, start user session.\n");
2337 case 302: /* redirect */
2339 if (line_input(s, buf, sizeof(buf)))
2342 if (expect(buf, "Location: ")) {
2343 relay_host = cut_token(buf, "//");
2344 cut_token(buf, "/");
2345 relay_port = atoi(cut_token(buf, ":"));
2347 } while (strcmp(buf,"\r\n") != 0);
2350 /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
2351 * not strictly the correct response, but some proxies do send this (e.g.
2352 * Symantec's Raptor firewall) */
2353 case 401: /* WWW-Auth required */
2354 case 407: /* Proxy-Auth required */
2355 /** NOTE: As easy implementation, we support only BASIC scheme
2356 and ignore realm. */
2357 /* If proxy_auth_type is PROXY_AUTH_BASIC and get
2358 this result code, authentication was failed. */
2359 if (proxy_auth_type != PROXY_AUTH_NONE) {
2360 error("Authentication failed.\n");
2363 auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2365 if ( line_input(s, buf, sizeof(buf)) ) {
2369 if (expect(buf, auth_what)) {
2370 /* parse type and realm */
2371 char *scheme, *realm;
2372 scheme = cut_token(buf, " ");
2373 realm = cut_token(scheme, " ");
2374 if ( scheme == NULL || realm == NULL ) {
2375 debug("Invalid format of %s field.", auth_what);
2376 return START_ERROR; /* fail */
2378 /* check supported auth type */
2379 if (expect(scheme, "basic")) {
2380 proxy_auth_type = PROXY_AUTH_BASIC;
2382 debug("Unsupported authentication type: %s", scheme);
2385 } while (strcmp(buf,"\r\n") != 0);
2386 if ( proxy_auth_type == PROXY_AUTH_NONE ) {
2387 debug("Can't find %s in response header.", auth_what);
2395 debug("http proxy is not allowed.\n");
2398 /* skip to end of response header */
2400 if ( line_input(s, buf, sizeof(buf) ) ) {
2401 debug("Can't skip response headers\n");
2404 } while ( strcmp(buf,"\r\n") != 0 );
2409 /* begin relaying via TELNET proxy.
2410 Sends string specified by telnet_command (-c option) with
2411 replacing host name and port number to the socket. */
2413 begin_telnet_relay( SOCKET s )
2417 char *good_phrase = "connected to";
2418 char *bad_phrase_list[] = {
2419 " failed", " refused", " rejected", " closed"
2424 debug("begin_telnet_relay()\n");
2427 debug("good phrase: '%s'\n", good_phrase);
2428 debug("bad phrases");
2430 for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
2431 debug_("%c '%s'", sep, bad_phrase_list[i]);
2436 /* make request string with replacing %h by destination hostname
2437 and %p by port number, etc. */
2438 cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
2440 /* Sorry, we send request string now without waiting a prompt. */
2441 if (sendf(s, "%s\r\n", cmd) < 0) {
2447 /* Process answer from proxy until good or bad phrase is detected. We
2448 assume that the good phrase should be appeared only in the final
2449 line of proxy responses. Bad keywods in the line causes operation
2450 fail. First checks a good phrase, then checks bad phrases.
2451 If no match, continue reading line from proxy. */
2452 while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
2454 /* first, check good phrase */
2455 if (strstr(buf, good_phrase)) {
2456 debug("good phrase is detected: '%s'\n", good_phrase);
2459 /* then, check bad phrase */
2460 for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) {
2461 if (strstr(buf, bad_phrase_list[i]) != NULL) {
2462 debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]);
2467 debug("error reading from telnet proxy\n");
2475 Returns 1 if data is available, otherwise return 0
2483 if ( st.st_mode & _S_IFIFO ) {
2484 /* in case of PIPE */
2485 if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
2486 NULL, 0, NULL, &len, NULL) ) {
2487 if ( GetLastError() == ERROR_BROKEN_PIPE ) {
2488 /* PIPE source is closed */
2489 /* read() will detects EOF */
2492 fatal("PeekNamedPipe() failed, errno=%d\n",
2496 } else if ( st.st_mode & _S_IFREG ) {
2497 /* in case of regular file (redirected) */
2498 len = 1; /* always data ready */
2499 } else if ( _kbhit() ) {
2500 /* in case of console */
2507 /* relay byte from stdin to socket and fro socket to stdout.
2508 returns reason of termination */
2510 do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
2512 /** vars for local input data **/
2513 char lbuf[1024]; /* local input buffer */
2514 int lbuf_len; /* available data in lbuf */
2515 int f_local; /* read local input more? */
2516 /** vars for remote input data **/
2517 char rbuf[1024]; /* remote input buffer */
2518 int rbuf_len; /* available data in rbuf */
2519 int f_remote; /* read remote input more? */
2520 int close_reason = REASON_UNK; /* reason of end repeating */
2521 /** other variables **/
2524 struct timeval *tmo;
2526 struct timeval win32_tmo;
2529 /* repeater between stdin/out and socket */
2530 nfds = ((local_in<remote)? remote: local_in) +1;
2531 f_local = 1; /* yes, read from local */
2532 f_remote = 1; /* yes, read from remote */
2536 while ( f_local || f_remote ) {
2541 /** prepare for reading local input **/
2542 if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
2544 if ( local_type != LOCAL_SOCKET ) {
2545 /* select() on Winsock is not accept standard handle.
2546 So use select() with short timeout and checking data
2547 in stdin by another method. */
2548 win32_tmo.tv_sec = 0;
2549 win32_tmo.tv_usec = 10*1000; /* 10 ms */
2552 #endif /* !_WIN32 */
2553 FD_SET( local_in, &ifds );
2556 /** prepare for reading remote input **/
2557 if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
2558 FD_SET( remote, &ifds );
2561 /* FD_SET( local_out, ofds ); */
2562 /* FD_SET( remote, ofds ); */
2564 if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
2566 error( "select() failed, %d\n", socket_errno());
2567 return REASON_ERROR;
2570 /* fake ifds if local is stdio handle because
2571 select() of Winsock does not accept stdio
2573 if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
2574 FD_SET(0,&ifds); /* data ready */
2577 /* remote => local */
2578 if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
2579 len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
2581 debug("connection closed by peer\n");
2582 close_reason = REASON_CLOSED_BY_REMOTE;
2583 f_remote = 0; /* no more read from socket */
2585 } else if ( len == -1 ) {
2586 if (socket_errno() != ECONNRESET) {
2588 fatal("recv() faield, %d\n", socket_errno());
2590 debug("ECONNRESET detected\n");
2593 debug("recv %d bytes\n", len);
2594 if ( 1 < f_debug ) /* more verbose */
2595 report_bytes( "<<<", rbuf, rbuf_len);
2600 /* local => remote */
2601 if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) {
2602 if (local_type == LOCAL_SOCKET)
2603 len = recv(local_in, lbuf + lbuf_len,
2604 sizeof(lbuf)-lbuf_len, 0);
2606 len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
2609 debug("local input is EOF\n");
2610 if (!f_hold_session)
2611 shutdown(remote, 1); /* no-more writing */
2613 close_reason = REASON_CLOSED_BY_LOCAL;
2614 } else if ( len == -1 ) {
2615 /* error on reading from stdin */
2616 if (f_hold_session) {
2617 debug ("failed to read from local\n");
2619 close_reason = REASON_CLOSED_BY_LOCAL;
2621 fatal("recv() failed, errno = %d\n", errno);
2628 /* flush data in buffer to socket */
2629 if ( 0 < lbuf_len ) {
2630 len = send(remote, lbuf, lbuf_len, 0);
2631 if ( 1 < f_debug ) /* more verbose */
2632 report_bytes( ">>>", lbuf, lbuf_len);
2634 fatal("send() failed, %d\n", socket_errno());
2635 } else if ( 0 < len ) {
2636 /* move data on to top of buffer */
2637 debug("send %d bytes\n", len);
2640 memcpy( lbuf, lbuf+len, lbuf_len );
2641 assert( 0 <= lbuf_len );
2645 /* flush data in buffer to local output */
2646 if ( 0 < rbuf_len ) {
2647 if (local_type == LOCAL_SOCKET)
2648 len = send( local_out, rbuf, rbuf_len, 0);
2650 len = write( local_out, rbuf, rbuf_len);
2652 fatal("output (local) failed, errno=%d\n", errno);
2655 if ( len < rbuf_len )
2656 memcpy( rbuf, rbuf+len, rbuf_len );
2657 assert( 0 <= rbuf_len );
2659 if (f_local == 0 && f_hold_session) {
2660 debug ("closing local port without disconnecting from remote\n");
2662 shutdown (local_out, 2);
2668 return close_reason;
2672 accept_connection (u_short port)
2674 static int sock = -1;
2676 struct sockaddr_in name;
2677 struct sockaddr client;
2683 /* Create the socket. */
2684 debug("Creating source port to forward.\n");
2685 sock = socket (PF_INET, SOCK_STREAM, 0);
2687 fatal("socket() failed, errno=%d\n", socket_errno());
2689 setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
2690 (void*)&sockopt, sizeof(sockopt));
2692 /* Give the socket a name. */
2693 name.sin_family = AF_INET;
2694 name.sin_port = htons (port);
2695 name.sin_addr.s_addr = htonl (INADDR_ANY);
2696 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
2697 fatal ("bind() failed, errno=%d\n", socket_errno());
2699 if (listen( sock, 1) < 0)
2700 fatal ("listen() failed, errno=%d\n", socket_errno());
2702 /* wait for new connection with watching EOF of stdin. */
2703 debug ("waiting new connection at port %d (socket=%d)\n", port, sock);
2707 struct timeval *ptmo = NULL;
2711 tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */
2715 FD_SET ((SOCKET)sock, &ifds);
2717 FD_SET (0, &ifds); /* watch stdin */
2719 n = select (nfds, &ifds, NULL, NULL, ptmo);
2721 fatal ("select() failed, %d\n", socket_errno());
2725 if (0 < stdindatalen()) {
2726 FD_SET (0, &ifds); /* fake */
2731 if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
2733 debug ("Give-up waiting port because stdin is closed.");
2736 if (FD_ISSET(sock, &ifds))
2737 break; /* socket is stimulated */
2740 socklen = sizeof(client);
2741 connection = accept( sock, &client, &socklen);
2742 if ( connection < 0 )
2743 fatal ("accept() failed, errno=%d\n", socket_errno());
2749 /** Main of program **/
2751 main( int argc, char **argv )
2754 int remote; /* socket */
2755 int local_in; /* Local input */
2756 int local_out; /* Local output */
2760 WSAStartup( 0x101, &wsadata);
2763 /* initialization */
2765 getarg( argc, argv );
2766 debug("Program is $Revision$\n");
2768 /* Open local_in and local_out if forwarding a port */
2769 if ( local_type == LOCAL_SOCKET ) {
2770 /* Relay between local port and destination */
2771 local_in = local_out = accept_connection( local_port );
2773 /* Relay between stdin/stdout and desteination */
2777 _setmode(local_in, O_BINARY);
2778 _setmode(local_out, O_BINARY);
2784 if (0 < connect_timeout)
2785 set_timeout (connect_timeout);
2786 #endif /* not _WIN32 */
2788 /* make connection */
2789 if ( relay_method == METHOD_DIRECT ) {
2790 remote = open_connection (dest_host, dest_port);
2791 if ( remote == SOCKET_ERROR )
2792 fatal( "Unable to connect to destination host, errno=%d\n",
2795 remote = open_connection (relay_host, relay_port);
2796 if ( remote == SOCKET_ERROR )
2797 fatal( "Unable to connect to relay host, errno=%d\n",
2801 /** resolve destination host (SOCKS) **/
2802 #if !defined(_WIN32) && !defined(__CYGWIN32__)
2803 if (socks_ns.sin_addr.s_addr != 0)
2804 switch_ns (&socks_ns);
2805 #endif /* not _WIN32 && not __CYGWIN32__ */
2806 if (relay_method == METHOD_SOCKS &&
2807 socks_resolve == RESOLVE_LOCAL &&
2808 local_resolve (dest_host, &dest_addr) < 0) {
2809 fatal("Unknown host: %s", dest_host);
2812 /** relay negociation **/
2813 switch ( relay_method ) {
2815 if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
2816 ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
2817 fatal( "failed to begin relaying via SOCKS.\n");
2821 ret = begin_http_relay(remote);
2825 fatal("failed to begin relaying via HTTP.\n");
2829 /* retry with authentication */
2835 if (begin_telnet_relay(remote) < 0)
2836 fatal("failed to begin relaying via telnet.\n");
2839 debug("connected\n");
2842 if (0 < connect_timeout)
2844 #endif /* not _WIN32 */
2847 debug ("start relaying.\n");
2849 reason = do_repeater(local_in, local_out, remote);
2850 debug ("relaying done.\n");
2851 if (local_type == LOCAL_SOCKET &&
2852 reason == REASON_CLOSED_BY_LOCAL &&
2854 /* re-wait at local port without closing remote session */
2855 debug ("re-waiting at local port %d\n", local_port);
2856 local_in = local_out = accept_connection( local_port );
2857 debug ("re-start relaying\n");
2860 closesocket(remote);
2861 if ( local_type == LOCAL_SOCKET)
2862 closesocket(local_in);
2866 debug ("that's all, bye.\n");
2871 /* ------------------------------------------------------------
2873 compile-command: "cc connect.c -o connect"
2878 ------------------------------------------------------------ */
2880 /*** end of connect.c ***/