1 /***********************************************************************
2 * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
4 * Copyright (c) 2000, 2001 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.imasy.or.jp/~gotoh/ssh/connect.c
34 * Pre-compiled biniary for Win32 is also available:
35 * http://www.imasy.or.jp/~gotoh/ssh/connect.exe.gz
37 * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
39 * http://www.imasy.or.jp/~gotoh/ssh/ssh-askpass.exe.gz
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
60 * You can specify proxy method in an environment variable or in a
61 * command line option.
63 * usage: connect [-dnhs45] [-R resolve] [-p local-port] [-w sec]
64 * [-H [user@]proxy-server[:port]]
65 * [-S [user@]socks-server[:port]]
68 * "host" and "port" is for the target hostname and port-number to
71 * The -H option specifys a hostname and port number of the http proxy
72 * server to relay. If port is omitted, 80 is used. You can specify this
73 * value in the environment variable HTTP_PROXY and pass the -h option
76 * The -S option specifys the hostname and port number of the SOCKS
77 * server to relay. Like -H, port number can be omitted and the default
78 * is 1080. You can also specify this value pair in the environment
79 * variable SOCKS5_SERVER and give the -s option to use it.
81 * The '-4' and the '-5' options are for specifying SOCKS relaying and
82 * indicates protocol version to use. It is valid only when used with
83 * '-s' or '-S'. Default is '-5' (protocol version 5)
85 * The '-R' option is for specifying method to resolve the
86 * hostname. Three keywords ("local", "remote", "both") or dot-notation
87 * IP address are acceptable. The keyword "both" means, "Try local
88 * first, then remote". If a dot-notation IP address is specified, use
89 * this host as nameserver. The default is "remote" for SOCKS5 or
90 * "local" for others. On SOCKS4 protocol, remote resolving method
91 * ("remote" and "both") requires protocol 4a supported server.
93 * The '**-p**' option will forward a local TCP port instead of using the
94 * standard input and output.
96 * The '-w' option specifys timeout seconds for making connection with
99 * The '-d' option is used for debug. If you fail to connect, use this
100 * and check request to and response from server.
102 * You can omit the "port" argument when program name is special format
103 * containing port number itself. For example,
104 * $ ln -s connect connect-25
105 * means this connect-25 command is spcifying port number 25 already
106 * so you need not 2nd argument (and ignored if specified).
108 * To use proxy, this example is for SOCKS5 connection to connect to
109 * 'host' at port 25 via SOCKS5 server on 'firewall' host.
110 * $ connect -S firewall host 25
112 * $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER
113 * $ connect -s host 25
115 * For a HTTP-PROXY connection:
116 * $ connect -H proxy-server:8080 host 25
118 * $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY
119 * $ connect -h host 25
120 * To forward a local port, for example to use ssh:
121 * $ connect -p 5550 -H proxy-server:8080 host 22
122 * ($ ssh -l user -p 5550 localhost )
127 * Connect.c doesn't have any configuration to specify the SOCKS server.
128 * If you are a mobile user, this limitation might bother you. However,
129 * You can compile connect.c and link with other standard SOCKS library
130 * like the NEC SOCKS5 library or Dante. This means connect.c is
131 * socksified and uses a configration file like to other SOCKSified
132 * network commands and you can switch configuration file any time
133 * (ex. when ppp startup) that brings you switching of SOCKS server for
134 * connect.c in same way with other commands. For this case, you can
135 * write ~/.ssh/config like this:
137 * ProxyCommand connect -n %h %p
139 * SOCKS5 authentication
140 * =====================
142 * Only USER/PASS authentication is supported.
144 * Proxy authentication
145 * ====================
147 * Only BASIC scheme is supported.
149 * Authentication informations
150 * ===========================
152 * User name for authentication is specifed by an environment variable
153 * or system login name. And Password is specified from environment
154 * variable or external program (specified in $SSH_ASKPASS) or tty.
156 * Following environment variable is used for specifying user name.
157 * SOCKS: $SOCKS5_USER, $LOGNAME, $USER
158 * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER
160 * ssh-askpass support
161 * ===================
163 * You can use ssh-askpass (came from OpenSSH or else) to specify
164 * password on graphical environment (X-Window or MS Windows). To use
165 * this, set program name to environment variable SSH_ASKPASS. On UNIX,
166 * X-Window must be required, so $DISPLAY environment variable is also
167 * needed. On Win32 environment, $DISPLAY is not mentioned.
169 * Related Informations
170 * ====================
172 * SOCKS5 -- RFC 1928, RFC 1929, RFC 1961
173 * NEC SOCKS Reference Implementation is available from:
174 * http://www.socks.nec.com
175 * DeleGate version 5 or earlier can be SOCKS4 server,
176 * and version 6 can be SOCKS5 and SOCKS4 server.
177 * and version 7.7.0 or later can be SOCKS5 and SOCKS4a server.
178 * http://www.delegate.org/delegate/
181 * Many http proxy servers supports this, but https should
182 * be allowed as configuration on your host.
183 * For example on DeleGate, you should add "https" to the
184 * "REMITTABLE" parameter to allow HTTP-Proxy like this:
185 * delegated -Pxxxx ...... REMITTABLE="+,https" ...
187 * Hypertext Transfer Protocol -- HTTP/1.1 -- RFC 2616
188 * HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617
189 * For proxy authentication, refer these documents.
191 ***********************************************************************/
200 #include <sys/types.h>
212 #include <sys/stat.h>
219 #include <sys/time.h>
221 #include <sys/select.h>
223 #include <sys/socket.h>
224 #include <netinet/in.h>
225 #include <arpa/inet.h>
228 #include <arpa/nameser.h>
230 #endif /* !__CYGWIN32__ */
234 #define ECONNRESET WSAECONNRESET
240 static char *vcid = "$Id$";
244 /* consider Borland C */
250 Win32 environment does not support -R option.
251 Win32 native compilers does not support -w option, yet.
255 "usage: %s [-dnhs45] \n"
256 #else /* not _WIN32 */
258 /* help message for UNIX */
259 "usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
260 #else /* not __CYGWIN32__ */
261 "usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
262 #endif /* __CYGWIN32__ */
263 #endif /* not _WIN32 */
264 " [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
267 /* name of this program */
268 char *progname = NULL;
269 char *progdesc = "connect --- simple relaying command via proxy.";
270 char *rcs_revstr = "$Revision$";
276 /* report flag to hide secure information */
279 int connect_timeout = 0;
281 /* local input type */
282 #define LOCAL_STDIO 0
283 #define LOCAL_SOCKET 1
284 char *local_type_names[] = { "stdio", "socket" };
285 int local_type = LOCAL_STDIO;
286 u_short local_port = 0;
288 /* utiity types, pair holder of number and string */
294 /* relay method, server and port */
295 #define METHOD_UNDECIDED 0
296 #define METHOD_DIRECT 1
297 #define METHOD_SOCKS 2
298 #define METHOD_HTTP 3
299 char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP" };
301 int relay_method = METHOD_UNDECIDED; /* relaying method */
302 char *relay_host = NULL; /* hostname of relay server */
303 u_short relay_port = 0; /* port of relay server */
304 char *relay_user = NULL; /* user name for auth */
306 /* destination target host and port */
307 char *dest_host = NULL;
308 struct sockaddr_in dest_addr;
309 u_short dest_port = 0;
311 /* informations for SOCKS */
312 #define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
313 #define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
314 #define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
315 #define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
316 #define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
317 #define SOCKS5_REP_REFUSED 0x05 /* connection refused */
318 #define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
319 #define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
320 #define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
321 #define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
323 LOOKUP_ITEM socks5_rep_names[] = {
324 { SOCKS5_REP_SUCCEEDED, "succeeded"},
325 { SOCKS5_REP_FAIL, "general SOCKS server failure"},
326 { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"},
327 { SOCKS5_REP_NUNREACH, "Network unreachable"},
328 { SOCKS5_REP_HUNREACH, "Host unreachable"},
329 { SOCKS5_REP_REFUSED, "connection refused"},
330 { SOCKS5_REP_EXPIRED, "TTL expired"},
331 { SOCKS5_REP_CNOTSUP, "Command not supported"},
332 { SOCKS5_REP_ANOTSUP, "Address not supported"},
333 { SOCKS5_REP_INVADDR, "Invalid address"},
337 /* SOCKS5 authentication methods */
338 #define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
339 #define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
340 #define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
341 #define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
342 #define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
343 #define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
344 #define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
346 #define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */
347 #define SOCKS4_REP_REJECTED 91 /* request rejected or failed */
348 #define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */
349 #define SOCKS4_REP_USERID 93 /* user id not matched */
351 LOOKUP_ITEM socks4_rep_names[] = {
352 { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"},
353 { SOCKS4_REP_REJECTED, "request rejected or failed"},
354 { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
355 { SOCKS4_REP_USERID, "user id not matched"},
359 #define RESOLVE_UNKNOWN 0
360 #define RESOLVE_LOCAL 1
361 #define RESOLVE_REMOTE 2
362 #define RESOLVE_BOTH 3
363 char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
365 int socks_version = 5; /* SOCKS protocol version */
366 int socks_resolve = RESOLVE_UNKNOWN;
367 struct sockaddr_in socks_ns;
368 char *socks5_auth = NULL;
370 /* Environment variable names */
371 #define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
372 #define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
373 #define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
375 #define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
376 #define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
377 #define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
379 #define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
380 #define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* auth password for SOCKS5 */
382 #define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
383 #define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
384 #define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
386 #define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */
387 #define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
389 #define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
390 #define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT"
391 #define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT"
392 #define ENV_HTTP_DIRECT "HTTP_DIRECT"
393 #define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
395 #define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
396 #define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
398 /* Prefix string of HTTP_PROXY */
399 #define HTTP_PROXY_PREFIX "http://"
400 #define PROXY_AUTH_NONE 0
401 #define PROXY_AUTH_BASIC 1
402 #define PROXY_AUTH_DIGEST 2
403 int proxy_auth_type = PROXY_AUTH_NONE;
406 /* return value of relay start function. */
407 #define START_ERROR -1
409 #define START_RETRY 1
411 /* socket related definitions */
416 #define SOCKET_ERROR -1
420 #define FD_ALLOC(nfds) ((fd_set*)malloc((nfds+7)/8))
421 #endif /* !FD_ALLOC */
424 #define socket_errno() WSAGetLastError()
426 #define closesocket close
427 #define socket_errno() (errno)
434 /* packet operation macro */
435 #define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
437 /* debug message output */
439 debug( const char *fmt, ... )
443 va_start( args, fmt );
444 fprintf(stderr, "DEBUG: ");
445 vfprintf( stderr, fmt, args );
451 debug_( const char *fmt, ... ) /* without prefix */
455 va_start( args, fmt );
456 vfprintf( stderr, fmt, args );
461 /* error message output */
463 error( const char *fmt, ... )
466 va_start( args, fmt );
467 fprintf(stderr, "ERROR: ");
468 vfprintf( stderr, fmt, args );
473 fatal( const char *fmt, ... )
476 va_start( args, fmt );
477 fprintf(stderr, "FATAL: ");
478 vfprintf( stderr, fmt, args );
484 downcase( char *buf )
494 lookup_resolve( const char *str )
496 char *buf = strdup( str );
500 if ( strcmp( buf, "both" ) == 0 )
502 else if ( strcmp( buf, "remote" ) == 0 )
503 ret = RESOLVE_REMOTE;
504 else if ( strcmp( buf, "local" ) == 0 )
506 else if ( strspn(buf, "0123456789.") == strlen(buf) ) {
507 #if defined(_WIN32) || defined(__CYGWIN32__)
508 fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
509 #endif /* _WIN32 || __CYGWIN32__ */
510 ret = RESOLVE_LOCAL; /* this case is also 'local' */
511 socks_ns.sin_addr.s_addr = inet_addr(buf);
512 socks_ns.sin_family = AF_INET;
515 ret = RESOLVE_UNKNOWN;
524 static char buf[1024];
525 DWORD size = sizeof(buf);
527 GetUserName( buf, &size);
529 #else /* not _WIN32 */
530 struct passwd *pw = getpwuid(getuid());
532 fatal("getpwuid() failed for uid: %d\n", getuid());
534 #endif /* not _WIN32 */
538 check STR is begin with substr with case-ignored comparison.
539 Return 1 if matched, otherwise 0.
542 expect( char *str, char *substr)
544 int len = strlen(substr);
545 while ( 0 < len-- ) {
546 if ( toupper(*str) != toupper(*substr) )
547 return 0; /* not matched */
550 return 1; /* good, matched */
554 /** PARAMETER operation **/
555 #define PARAMETER_FILE "/etc/connectrc"
556 #define PARAMETER_DOTFILE ".connectrc"
561 PARAMETER_ITEM parameter_table[] = {
562 { ENV_SOCKS_SERVER, NULL },
563 { ENV_SOCKS5_SERVER, NULL },
564 { ENV_SOCKS4_SERVER, NULL },
565 { ENV_SOCKS_RESOLVE, NULL },
566 { ENV_SOCKS5_RESOLVE, NULL },
567 { ENV_SOCKS4_RESOLVE, NULL },
568 { ENV_SOCKS5_USER, NULL },
569 { ENV_SOCKS5_PASSWORD, NULL },
570 { ENV_HTTP_PROXY, NULL },
571 { ENV_HTTP_PROXY_USER, NULL },
572 { ENV_HTTP_PROXY_PASSWORD, NULL },
573 { ENV_CONNECT_USER, NULL },
574 { ENV_CONNECT_PASSWORD, NULL },
575 { ENV_SSH_ASKPASS, NULL },
576 { ENV_SOCKS5_DIRECT, NULL },
577 { ENV_SOCKS4_DIRECT, NULL },
578 { ENV_SOCKS_DIRECT, NULL },
579 { ENV_HTTP_DIRECT, NULL },
580 { ENV_CONNECT_DIRECT, NULL },
581 { ENV_SOCKS5_AUTH, NULL },
586 find_parameter_item(const char* name){
588 for( i = 0; parameter_table[i].name != NULL; i++ ){
589 if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
590 return ¶meter_table[i];
596 read_parameter_file_1(const char* name){
600 f = fopen(name, "r");
602 debug("Reading parameter file(%s)\n", name);
603 for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
604 char *p, *q, *param = NULL, *value = NULL;
605 p = strchr(lbuf, '\n');
607 fatal("%s:%d: buffer overflow\n", name, line);
609 p = strchr(lbuf, '#');
612 for ( p = lbuf; *p; p++ )
613 if( *p != ' ' && *p != '\t' ) break;
614 if ( *p == '\0' ) continue;
618 error("%s:%d: missing equal sign\n", name, line);
621 for ( q = p - 1; q >= lbuf; q-- )
622 if ( *q != ' ' && *q != '\t' ) break;
625 if ( *p != ' ' && *p != '\t' ) break;
628 for ( p--; p >= lbuf; p-- )
629 if ( *p != ' ' && *p != '\t' ) break;
631 if ( param && value ) {
632 PARAMETER_ITEM *item;
633 item = find_parameter_item(param);
634 if ( item == NULL ) {
635 error("%s:%d: unknown parameter `%s'\n", name, line, param);
638 item->value = strdup(value);
639 debug("Parameter `%s' is set to `%s'\n", param, value);
646 read_parameter_file(void){
652 read_parameter_file_1(PARAMETER_FILE);
654 pw = getpwuid(getuid());
656 fatal("getpwuid() failed for uid: %d\n", getuid());
657 name = malloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
659 fatal("Can't allocate memory\n");
660 strcpy(name, pw->pw_dir);
661 strcat(name, "/" PARAMETER_DOTFILE);
662 read_parameter_file_1(name);
668 getparam(const char* name){
669 char *value = getenv(name);
670 if ( value == NULL ){
671 PARAMETER_ITEM *item = find_parameter_item(name);
679 /** DIRECT connection **/
680 #define MAX_DIRECT_ADDR_LIST 256
688 struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
689 int n_direct_addr_list = 0;
692 mask_addr (void *addr, void *mask, int addrlen)
697 while ( 0 < addrlen-- )
702 add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
704 struct in_addr iaddr;
706 if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
707 error("direct address table is full!\n");
711 mask_addr(&iaddr, mask, sizeof(iaddr));
712 s = strdup(inet_ntoa(iaddr));
713 debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
715 memcpy( &direct_addr_list[n_direct_addr_list].addr,
716 &iaddr, sizeof(iaddr));
717 memcpy( &direct_addr_list[n_direct_addr_list].mask,
718 mask, sizeof(*mask));
719 direct_addr_list[n_direct_addr_list].negative = negative;
720 n_direct_addr_list++;
725 parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
728 /* Assme already be splitted by separator
729 and formatted as folowing:
730 1) 12.34.56.789/255.255.255.0
733 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
739 assert( str != NULL );
740 debug("parsing address pair: '%s'\n", str);
744 dsta = (u_char*)&addr->s_addr;
745 dstm = (u_char*)&mask->s_addr;
746 for (i=0; i<4; i++ ) {
748 break; /* case of format #3 */
749 if ( !isdigit(*ptr) )
750 return -1; /* format error: */
751 *dsta++ = atoi( ptr );
752 *dstm++ = 255; /* automatic mask for format #3 */
753 while ( isdigit(*ptr) ) /* skip digits */
760 /* At this point, *ptr points '/' or EOS ('\0') */
762 return 0; /* complete as format #3 */
764 return -1; /* format error */
765 /* Now parse mask for format #1 or #2 */
767 mask->s_addr = 0; /* clear automatic mask */
769 if ( strchr( ptr, '.') ) {
770 /* case of format #1 */
771 dstm = (u_char*)&mask->s_addr;
772 for (i=0; i<4; i++) {
773 if ( !isdigit(*ptr) )
774 return -1; /* format error: */
776 while ( isdigit(*ptr) ) /* skip digits */
781 break; /* from for loop */
783 /* complete as format #1 */
785 /* case of format #2 */
786 if ( !isdigit(*ptr) )
787 return -1; /* format error: */
790 return -1; /* format error */
791 mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
792 /* complete as format #1 */
798 initialize_direct_addr (void)
802 char *env = NULL, *beg, *next, *envkey = NULL;
803 struct in_addr addr, mask;
805 if ( relay_method == METHOD_SOCKS ){
806 if ( socks_version == 5 )
807 envkey = ENV_SOCKS5_DIRECT;
809 envkey = ENV_SOCKS4_DIRECT;
810 env = getparam(envkey);
812 env = getparam(ENV_SOCKS_DIRECT);
813 } else if ( relay_method == METHOD_HTTP ){
814 env = getparam(ENV_HTTP_DIRECT);
818 env = getparam(ENV_CONNECT_DIRECT);
821 return; /* no entry */
822 debug("making direect addr list from: '%s'\n", env);
823 env = strdup( env ); /* reallocate to modify */
827 if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
828 error("too many entries in %s", envkey);
829 break; /* from do loop */
831 next = strchr( beg, ',');
841 if ( !parse_addr_pair( beg, &addr, &mask ) ) {
842 add_direct_addr( &addr, &mask, negative );
844 error("invalid addr format in %s: %s\n", envkey, beg);
848 } while ( next != NULL );
855 cmp_addr (void *addr1, void *addr2, int addrlen)
857 return memcmp( addr1, addr2, addrlen );
861 is_direct_address (const struct sockaddr_in *addr, int addrlen)
864 struct in_addr saddr, iaddr;
866 saddr = addr->sin_addr;
868 /* Note: assume IPV4 address !! */
869 for (i=0; i<n_direct_addr_list; i++ ) {
871 mask_addr( &iaddr, &direct_addr_list[i].mask,
872 sizeof(struct in_addr));
873 if (cmp_addr(&iaddr, &direct_addr_list[i].addr,
874 sizeof(struct in_addr)) == 0) {
875 if (direct_addr_list[i].negative) {
876 debug("negative match, addr to be SOCKSify: %s\n",
878 return 0; /* not direct */
880 if (!direct_addr_list[i].negative) {
881 debug("positive match, addr to be direct: %s\n",
883 return 1; /* direct*/
887 debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
888 return 0; /* not direct */
892 /** TTY operation **/
898 intr_handler(int sig)
904 tty_change_echo(int fd, int enable)
906 static struct termios ntio, otio; /* new/old termios */
907 static sigset_t nset, oset; /* new/old sigset */
908 static struct sigaction nsa, osa; /* new/old sigaction */
909 static int disabled = 0;
911 if ( disabled && enable ) {
913 tcsetattr(fd, TCSANOW, &otio);
915 /* resotore sigaction */
916 sigprocmask(SIG_SETMASK, &oset, NULL);
917 sigaction(SIGINT, &osa, NULL);
918 if ( intr_flag != 0 ) {
919 /* re-generate signal */
920 kill(getpid(), SIGINT);
925 } else if (!disabled && !enable) {
926 /* set SIGINTR handler and break syscall on singal */
928 sigaddset(&nset, SIGTSTP);
929 sigprocmask(SIG_BLOCK, &nset, &oset);
931 memset(&nsa, 0, sizeof(nsa));
932 nsa.sa_handler = intr_handler;
933 sigaction(SIGINT, &nsa, &osa);
935 if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
938 ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
939 (void) tcsetattr(fd, TCSANOW, &ntio);
946 #define TTY_NAME "/dev/tty"
948 tty_readpass( const char *prompt, char *buf, size_t size )
952 tty = open(TTY_NAME, O_RDWR);
954 error("Unable to open %s\n", TTY_NAME);
955 return -1; /* can't open tty */
958 return -1; /* no room */
959 write(tty, prompt, strlen(prompt));
961 tty_change_echo(tty, 0); /* disable echo */
962 ret = read(tty,buf, size-1);
963 tty_change_echo(tty, 1); /* restore */
964 write(tty, "\n", 1); /* new line */
966 if ( strchr(buf,'\n') == NULL )
976 w32_intr_handler(DWORD dwCtrlType)
978 if ( dwCtrlType == CTRL_C_EVENT ) {
986 #define tty_readpass w32_tty_readpass
988 w32_tty_readpass( const char *prompt, char *buf, size_t size )
990 HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
991 0, NULL, OPEN_EXISTING, 0, NULL);
992 HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
993 0, NULL, OPEN_EXISTING, 0, NULL);
997 if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
998 fatal("Cannot open console. (errno=%d)", GetLastError());
1000 WriteFile(out, prompt, strlen(prompt), &bytes, 0);
1001 SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
1002 GetConsoleMode(in, &mode);
1003 SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
1004 ret = ReadFile(in, buf, size, &bytes, 0);
1005 SetConsoleMode(in, mode); /* enable echo */
1006 SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
1008 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
1009 WriteFile(out,"\n", 1, &bytes, 0);
1017 /*** network operations ***/
1020 Determine relay informations:
1021 method, host, port, and username.
1022 1st arg, METHOD should be METHOD_xxx.
1023 2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
1024 hostname is domain name or dot notation.
1025 If port is omitted, use 80 for METHOD_HTTP method,
1026 use 1080 for METHOD_SOCKS method.
1027 Username is also able to given by 3rd. format.
1028 2nd argument SPEC can be NULL. if NULL, use environment variable.
1031 set_relay( int method, char *spec )
1033 char *buf, *sep, *resolve;
1035 relay_method = method;
1037 read_parameter_file();
1038 initialize_direct_addr();
1039 if (n_direct_addr_list == 0) {
1043 for ( i=0; i<n_direct_addr_list; i++ ) {
1045 s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
1046 s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
1047 debug(" #%d: %c%s/%s\n", i,
1048 (direct_addr_list[i].negative)? '!': ' ',
1057 return -1; /* nothing to do */
1060 if ( spec == NULL ) {
1061 switch ( socks_version ) {
1063 spec = getparam(ENV_SOCKS5_SERVER);
1066 spec = getparam(ENV_SOCKS4_SERVER);
1071 spec = getparam(ENV_SOCKS_SERVER);
1074 fatal("Failed to determine SOCKS server.\n");
1075 relay_port = 1080; /* set default first */
1077 /* determine resolve method */
1078 if ( socks_resolve == RESOLVE_UNKNOWN ) {
1079 if ( ((socks_version == 5) &&
1080 ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
1081 ((socks_version == 4) &&
1082 ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
1083 ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
1084 socks_resolve = lookup_resolve( resolve );
1085 if ( socks_resolve == RESOLVE_UNKNOWN )
1086 fatal("Invalid resolve method: %s\n", resolve);
1089 if ( socks_version == 5 )
1090 socks_resolve = RESOLVE_REMOTE;
1092 socks_resolve = RESOLVE_LOCAL;
1099 spec = getparam(ENV_HTTP_PROXY);
1101 fatal("You must specify http proxy server\n");
1102 relay_port = 80; /* set default first */
1106 if (expect( spec, HTTP_PROXY_PREFIX)) {
1107 /* URL format like: "http://server:port/" */
1108 /* extract server:port part */
1109 buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
1110 buf[strcspn(buf, "/")] = '\0';
1112 /* assume spec is aready "server:port" format */
1113 buf = strdup( spec );
1117 /* check username in spec */
1118 sep = strchr( spec, '@' );
1119 if ( sep != NULL ) {
1121 relay_user = strdup( spec );
1124 if ( (relay_user == NULL) &&
1125 ((relay_user = getenv("LOGNAME")) == NULL) &&
1126 ((relay_user = getenv("USER")) == NULL) &&
1127 ((relay_user = getusername()) == NULL)) {
1128 /* get username from system */
1129 debug("Cannot determine your username.\n");
1132 /* split out hostname and port number from spec */
1133 sep = strchr(spec,':');
1134 if ( sep == NULL ) {
1135 /* hostname only, port is already set as default */
1136 relay_host = strdup( spec );
1138 /* hostname and port */
1139 relay_port = atoi(sep+1);
1141 relay_host = strdup( spec );
1149 resolve_port( const char *service )
1152 if ( service[strspn (service, "0123456789")] == '\0' ) {
1153 /* all digits, port number */
1154 port = atoi(service);
1156 /* treat as service name */
1157 struct servent *ent;
1158 ent = getservbyname( service, NULL );
1159 if ( ent == NULL ) {
1160 debug("Unknown service, '%s'\n", service);
1163 port = ntohs(ent->s_port);
1164 debug("service: %s => %d\n", service, port);
1167 return (u_short)port;
1175 ptr = strstr(rcs_revstr, ": ");
1177 revstr = strdup("unknown");
1181 len = strspn(ptr, "0123456789.");
1183 revstr = malloc(len+1);
1184 memcpy(revstr, ptr, len);
1190 getarg( int argc, char **argv )
1193 char *ptr, *server = (char*)NULL;
1194 int method = METHOD_DIRECT;
1200 while ( (0 < argc) && (**argv == '-') ) {
1204 case 's': /* use SOCKS */
1205 method = METHOD_SOCKS;
1208 case 'n': /* no proxy */
1209 method = METHOD_DIRECT;
1212 case 'h': /* use http-proxy */
1213 method = METHOD_HTTP;
1216 case 'S': /* specify SOCKS server */
1219 method = METHOD_SOCKS;
1222 error("option '-%c' needs argument.\n", *ptr);
1227 case 'H': /* specify http-proxy server */
1230 method = METHOD_HTTP;
1233 error("option '-%c' needs argument.\n", *ptr);
1239 /* destination port number */
1242 dest_port = resolve_port(*argv);
1244 error("option '-%c' needs argument.\n", *ptr);
1249 case 'p': /* specify port to forward */
1252 local_type = LOCAL_SOCKET;
1253 local_port = resolve_port(*argv);
1255 error("option '-%c' needs argument.\n", *ptr);
1264 connect_timeout = atoi(*argv);
1266 error("option '-%c' needs argument.\n", *ptr);
1270 #endif /* not _WIN32 */
1283 socks5_auth = *argv;
1285 error("option '-%c' needs argument.\n", *ptr);
1290 case 'R': /* specify resolve method */
1293 socks_resolve = lookup_resolve( *argv );
1295 error("option '-%c' needs argument.\n", *ptr);
1300 case 'V': /* print version */
1301 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1304 case 'd': /* debug mode */
1309 error("unknown option '-%c'\n", *ptr);
1321 set_relay( method, server );
1323 /* check destination HOST (MUST) */
1325 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1326 fprintf(stderr, usage, progname);
1329 dest_host = argv[0];
1330 /* decide port or service name from programname or argument */
1331 if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
1332 ((ptr=strchr( progname, '\\')) != NULL) )
1336 if ( dest_port == 0 ) {
1337 /* accept only if -P is not specified. */
1339 /* get port number from argument (prior to progname) */
1340 /* NOTE: This way is for cvs ext method. */
1341 dest_port = resolve_port(argv[1]);
1342 } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
1343 /* decide port number from program name */
1344 char *str = strdup( ptr+8 );
1345 str[strcspn( str, "." )] = '\0';
1346 dest_port = resolve_port(str);
1350 /* check port number */
1351 if ( dest_port <= 0 ) {
1352 error( "You must specify the destination port correctly.\n");
1356 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
1357 error("Invalid relay port: %d\n", dest_port);
1363 /* report for debugging */
1364 debug("relay_method = %s (%d)\n",
1365 method_names[relay_method], relay_method);
1366 if ( relay_method != METHOD_DIRECT ) {
1367 debug("relay_host=%s\n", relay_host);
1368 debug("relay_port=%d\n", relay_port);
1369 debug("relay_user=%s\n", relay_user);
1371 if ( relay_method == METHOD_SOCKS ) {
1372 debug("socks_version=%d\n", socks_version);
1373 debug("socks_resolve=%s (%d)\n",
1374 resolve_names[socks_resolve], socks_resolve);
1376 debug("local_type=%s\n", local_type_names[local_type]);
1377 if ( local_type == LOCAL_SOCKET )
1378 debug("local_port=%d\n", local_port);
1379 debug("dest_host=%s\n", dest_host);
1380 debug("dest_port=%d\n", dest_port);
1382 fprintf(stderr, usage, progname);
1389 /* Time-out feature is not allowed for Win32 native compilers. */
1390 /* MSVC and Borland C cannot but Cygwin and UNIXes can. */
1392 /* timeout signal hander */
1396 debug( "timed out\n" );
1397 signal( SIGALRM, SIG_IGN );
1401 /* set timeout param = seconds, 0 clears */
1403 set_timeout(int timeout)
1405 /* This feature is allowed for UNIX or cygwin environments, currently */
1406 if ( timeout == 0 ) {
1407 debug( "clearing timeout\n" );
1408 signal( SIGALRM, SIG_IGN );
1411 debug( "setting timeout: %d seconds\n", timeout );
1412 signal(SIGALRM, (void *)sig_timeout);
1422 local_resolve (const char *host, struct sockaddr_in *addr,
1423 struct sockaddr_in *ns)
1425 struct hostent *ent;
1426 if ( strspn(host, "0123456789.") == strlen(host) ) {
1427 /* given by IPv4 address */
1428 addr->sin_family = AF_INET;
1429 addr->sin_addr.s_addr = inet_addr(host);
1431 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1432 if (ns != 0 && ns->sin_addr.s_addr != 0) {
1434 memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
1436 debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
1438 #endif /* !_WIN32 && !__CYGWIN32__ */
1439 debug("resolving host by name: %s\n", host);
1440 ent = gethostbyname (host);
1442 memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
1443 addr->sin_family = ent->h_addrtype;
1444 debug("resolved: %s (%s)\n",
1445 host, inet_ntoa(addr->sin_addr));
1447 debug("failed to resolve locally.\n");
1448 return -1; /* failed */
1451 return 0; /* good */
1455 open_connection( const char *host, u_short port )
1458 struct sockaddr_in saddr;
1460 if ( relay_method == METHOD_DIRECT ) {
1463 } else if ((local_resolve (dest_host, &saddr, NULL) >= 0)&&
1464 (is_direct_address(&saddr, sizeof(saddr)))) {
1465 debug("%s is connected directly\n", dest_host);
1466 relay_method = METHOD_DIRECT;
1474 if (local_resolve (host, &saddr, NULL) < 0) {
1475 error("can't resolve hostname: %s\n", host);
1476 return SOCKET_ERROR;
1478 saddr.sin_port = htons(port);
1480 debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
1481 s = socket( AF_INET, SOCK_STREAM, 0 );
1482 if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
1484 debug( "connect() failed.\n");
1485 return SOCKET_ERROR;
1491 report_text( char *prefix, char *buf )
1493 static char work[1024];
1499 return; /* don't report */
1500 debug("%s \"", prefix);
1502 memset( work, 0, sizeof(work));
1504 while ( *buf && ((tmp-work) < sizeof(work)-5) ) {
1506 case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
1507 case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
1508 case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
1509 case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
1511 if ( isprint(*buf) ) {
1514 sprintf( tmp, "\\x%02X", (unsigned char)*buf);
1529 report_bytes( char *prefix, char *buf, int len )
1533 debug( "%s", prefix );
1535 fprintf( stderr, " %02x", *(unsigned char *)buf);
1539 fprintf(stderr, "\n");
1544 atomic_out( SOCKET s, char *buf, int size )
1548 assert( buf != NULL );
1552 while ( 0 < size ) {
1553 len = send( s, buf+ret, size, 0 );
1555 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1560 debug("atomic_out() [some bytes]\n");
1561 debug(">>> xx xx xx xx ...\n");
1563 debug("atomic_out() [%d bytes]\n", ret);
1564 report_bytes(">>>", buf, ret);
1570 atomic_in( SOCKET s, char *buf, int size )
1574 assert( buf != NULL );
1579 while ( 0 < size ) {
1580 len = recv( s, buf+ret, size, 0 );
1582 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1583 } else if ( len == 0 ) {
1584 fatal( "Connection closed by peer.\n");
1590 debug("atomic_in() [some bytes]\n");
1591 debug("<<< xx xx xx xx ...\n");
1593 debug("atomic_in() [%d bytes]\n", ret);
1594 report_bytes("<<<", buf, ret);
1600 line_input( SOCKET s, char *buf, int size )
1604 return 0; /* no error */
1606 while ( 0 < size ) {
1607 switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */
1609 error("recv() error\n");
1610 return -1; /* error */
1612 size = 0; /* end of stream */
1615 /* continue reading until last 1 char is EOL? */
1616 if ( *dst == '\n' ) {
1627 report_text( "<<<", buf);
1632 skip_all_lines (SOCKET s)
1635 while (0 < recv (s, buf, sizeof(buf), 0))
1640 Span token in given string STR until char in DELIM is appeared.
1641 Then replace contiguous DELIMS with '\0' for string termination
1642 and returns next pointer.
1643 If no next token, return NULL.
1646 cut_token( char *str, char *delim)
1648 char *ptr = str + strcspn(str, delim);
1649 char *end = ptr + strspn(ptr, delim);
1658 lookup(int num, LOOKUP_ITEM *items)
1661 while (0 <= items[i].num) {
1662 if (items[i].num == num)
1663 return items[i].str;
1670 password input routine
1671 Use ssh-askpass (same mechanism to OpenSSH)
1674 readpass( const char* prompt, ...)
1676 static char buf[1000]; /* XXX, don't be fix length */
1678 va_start(args, prompt);
1679 vsprintf(buf, prompt, args);
1682 if ( getparam(ENV_SSH_ASKPASS)
1683 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1684 && getenv("DISPLAY")
1685 #endif /* not _WIN32 && not __CYGWIN32__ */
1687 /* use ssh-askpass to get password */
1689 char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
1690 cmd = malloc(strlen(askpass) +1 +1 +strlen(buf) +1);
1691 sprintf(cmd, "%s \"%s\"", askpass, buf);
1692 fp = popen(cmd, "r");
1695 return NULL; /* fail */
1697 if (fgets(buf, sizeof(buf), fp) == NULL)
1698 return NULL; /* fail */
1701 tty_readpass( buf, buf, sizeof(buf));
1703 buf[strcspn(buf, "\r\n")] = '\0';
1708 socks5_do_auth_userpass( int s )
1710 unsigned char buf[1024], *ptr;
1714 /* do User/Password authentication. */
1715 /* This feature requires username and password from
1716 command line argument or environment variable,
1718 if ( relay_user == NULL )
1719 fatal("User name cannot be decided.\n");
1721 /* get password from environment variable if exists. */
1722 if ((pass=getparam(ENV_SOCKS5_PASSWORD)) == NULL &&
1723 (pass=readpass("Enter SOCKS5 password for %s@%s: ",
1724 relay_user, relay_host)) == NULL )
1725 fatal("Cannot get password for user: %s\n", relay_user);
1727 /* make authentication packet */
1729 PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */
1730 len = strlen( relay_user ); /* ULEN and UNAME */
1731 PUT_BYTE( ptr++, len );
1732 strcpy( ptr, relay_user );
1734 len = strlen( pass ); /* PLEN and PASSWD */
1735 PUT_BYTE( ptr++, strlen(pass));
1736 strcpy( ptr, pass );
1739 /* send it and get answer */
1741 atomic_out( s, buf, ptr-buf );
1743 atomic_in( s, buf, 2 );
1747 return 0; /* success */
1749 return -1; /* fail */
1753 socks5_getauthname( int auth )
1756 case SOCKS5_AUTH_REJECT: return "REJECTED";
1757 case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
1758 case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
1759 case SOCKS5_AUTH_USERPASS: return "USERPASS";
1760 case SOCKS5_AUTH_CHAP: return "CHAP";
1761 case SOCKS5_AUTH_EAP: return "EAP";
1762 case SOCKS5_AUTH_MAF: return "MAF";
1763 default: return "(unknown)";
1772 AUTH_METHOD_ITEM socks5_auth_table[] = {
1773 { "none", SOCKS5_AUTH_NOAUTH },
1774 { "gssapi", SOCKS5_AUTH_GSSAPI },
1775 { "userpass", SOCKS5_AUTH_USERPASS },
1776 { "chap", SOCKS5_AUTH_CHAP },
1781 socks5_auth_parse_1(char *start, char *end){
1783 for ( ; *start; start++ )
1784 if ( *start != ' ' && *start != '\t') break;
1785 for ( end--; end >= start; end-- ) {
1786 if ( *end != ' ' && *end != '\t'){
1792 for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
1793 if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
1794 return socks5_auth_table[i].auth;
1797 fatal("Unknown auth method: %s\n", start);
1802 socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
1805 while ( i < max_auth ) {
1806 if ( *start && ( end = strchr(start, ',') ) ){
1807 auth_list[i++] = socks5_auth_parse_1(start, end);
1813 if ( *start && ( i < max_auth ) ){
1814 for( end = start; *end; end++ );
1815 auth_list[i++] = socks5_auth_parse_1(start, end);
1817 fatal("Too much auth method.\n");
1822 /* begin SOCKS5 relaying
1823 And no authentication is supported.
1826 begin_socks5_relay( SOCKET s )
1828 unsigned char buf[256], *ptr, *env = socks5_auth;
1829 unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
1830 int len, auth_result, i;
1832 debug( "begin_socks_relay()\n");
1834 /* request authentication */
1836 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
1839 env = getparam(ENV_SOCKS5_AUTH);
1840 if ( env == NULL ) {
1841 /* add no-auth authentication */
1842 auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
1843 /* add user/pass authentication */
1844 auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
1846 n_auth = socks5_auth_parse(env, auth_list, 10);
1848 PUT_BYTE( ptr++, n_auth); /* num auth */
1849 for (i=0; i<n_auth; i++) {
1850 debug("available auth method[%d] = %s (0x%02x)\n",
1851 i, socks5_getauthname(auth_list[i]), auth_list[i]);
1852 PUT_BYTE( ptr++, auth_list[i]); /* authentications */
1854 atomic_out( s, buf, ptr-buf ); /* send requst */
1855 atomic_in( s, buf, 2 ); /* recv response */
1856 if ( (buf[0] != 5) || /* ver5 response */
1857 (buf[1] == 0xFF) ) { /* check auth method */
1858 error("No auth method accepted.\n");
1861 auth_method = buf[1];
1863 debug("auth method: %s\n", socks5_getauthname(auth_method));
1865 switch ( auth_method ) {
1866 case SOCKS5_AUTH_REJECT:
1867 error("No acceptable authentication method\n");
1868 return -1; /* fail */
1870 case SOCKS5_AUTH_NOAUTH:
1875 case SOCKS5_AUTH_USERPASS:
1876 auth_result = socks5_do_auth_userpass(s);
1880 error("Unsupported authentication method: %s\n",
1881 socks5_getauthname( auth_method ));
1882 return -1; /* fail */
1884 if ( auth_result != 0 ) {
1885 error("Authentication failed.\n");
1888 /* request to connect */
1890 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
1891 PUT_BYTE( ptr++, 1); /* CMD: CONNECT */
1892 PUT_BYTE( ptr++, 0); /* FLG: 0 */
1893 if ( dest_addr.sin_addr.s_addr == 0 ) {
1894 /* resolved by SOCKS server */
1895 PUT_BYTE( ptr++, 3); /* ATYP: DOMAINNAME */
1896 len = strlen(dest_host);
1897 PUT_BYTE( ptr++, len); /* DST.ADDR (len) */
1898 memcpy( ptr, dest_host, len ); /* (hostname) */
1901 /* resolved localy */
1902 PUT_BYTE( ptr++, 1 ); /* ATYP: IPv4 */
1903 memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
1904 ptr += sizeof(dest_addr.sin_addr);
1906 PUT_BYTE( ptr++, dest_port>>8); /* DST.PORT */
1907 PUT_BYTE( ptr++, dest_port&0xFF);
1908 atomic_out( s, buf, ptr-buf); /* send request */
1909 atomic_in( s, buf, 4 ); /* recv response */
1910 if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */
1911 error("Got error response from SOCKS server: %d (%s).\n",
1912 buf[1], lookup(buf[1], socks5_rep_names));
1916 switch ( buf[3] ) { /* case by ATYP */
1917 case 1: /* IP v4 ADDR*/
1918 atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */
1920 case 3: /* DOMAINNAME */
1921 atomic_in( s, ptr, 1 ); /* recv name and port */
1922 atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
1924 case 4: /* IP v6 ADDR */
1925 atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */
1929 /* Conguraturation, connected via SOCKS5 server! */
1933 /* begin SOCKS protocol 4 relaying
1934 And no authentication is supported.
1936 There's SOCKS protocol version 4 and 4a. Protocol version
1937 4a has capability to resolve hostname by SOCKS server, so
1938 we don't need resolving IP address of destination host on
1941 Environment variable SOCKS_RESOLVE directs how to resolve
1942 IP addess. There's 3 keywords allowed; "local", "remote"
1943 and "both" (case insensitive). Keyword "local" means taht
1944 target host name is resolved by localhost resolver
1945 (usualy with gethostbyname()), "remote" means by remote
1946 SOCKS server, "both" means to try resolving by localhost
1949 SOCKS4 protocol and authentication of SOCKS5 protocol
1950 requires user name on connect request.
1951 User name is determined by following method.
1953 1. If server spec has user@hostname:port format then
1954 user part is used for this SOCKS server.
1956 2. Get user name from environment variable LOGNAME, USER
1961 begin_socks4_relay( SOCKET s )
1963 unsigned char buf[256], *ptr;
1965 debug( "begin_socks_relay()\n");
1967 /* make connect request packet
1969 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
1971 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
1974 PUT_BYTE( ptr++, 4); /* protocol version (4) */
1975 PUT_BYTE( ptr++, 1); /* CONNECT command */
1976 PUT_BYTE( ptr++, dest_port>>8); /* destination Port */
1977 PUT_BYTE( ptr++, dest_port&0xFF);
1978 /* destination IP */
1979 memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
1980 ptr += sizeof(dest_addr.sin_addr);
1981 if ( dest_addr.sin_addr.s_addr == 0 )
1982 *(ptr-1) = 1; /* fake, protocol 4a */
1984 strcpy( ptr, relay_user );
1985 ptr += strlen( relay_user ) +1;
1986 /* destination host name (for protocol 4a) */
1987 if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
1988 strcpy( ptr, dest_host );
1989 ptr += strlen( dest_host ) +1;
1991 /* send command and get response
1992 response is: VN:1, CD:1, PORT:2, ADDR:4 */
1993 atomic_out( s, buf, ptr-buf); /* send request */
1994 atomic_in( s, buf, 8 ); /* recv response */
1995 if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */
1996 error("Got error response: %d: '%s'.\n",
1997 buf[1], lookup(buf[1], socks4_rep_names));
1998 return -1; /* failed */
2001 /* Conguraturation, connected via SOCKS4 server! */
2006 sendf(SOCKET s, const char *fmt,...)
2008 static char buf[10240]; /* xxx, enough? */
2011 va_start( args, fmt );
2012 vsprintf( buf, fmt, args );
2015 report_text(">>>", buf);
2016 if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
2017 debug("failed to send http request. errno=%d\n", socket_errno());
2023 const char *base64_table =
2024 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2027 make_base64_string(const char *str)
2032 int bits, data, src_len, dst_len;
2033 /* make base64 string */
2034 src_len = strlen(str);
2035 dst_len = (src_len+2)/3*4;
2036 buf = malloc(dst_len+1);
2038 src = (unsigned char *)str;
2039 dst = (unsigned char *)buf;
2040 while ( dst_len-- ) {
2042 data = (data << 8) | *src;
2047 *dst++ = base64_table[0x3F & (data >> (bits-6))];
2051 /* fix-up tail padding */
2052 switch ( src_len%3 ) {
2063 basic_auth( SOCKET s, const char *user, const char *pass )
2069 len = strlen(user)+strlen(pass)+1;
2070 userpass = malloc(len+1);
2071 sprintf(userpass,"%s:%s", user, pass);
2072 cred = make_base64_string(userpass);
2073 memset( userpass, 0, len );
2075 f_report = 0; /* don't report for security */
2076 ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
2078 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
2080 memset(cred, 0, strlen(cred));
2086 /* begin relaying via HTTP proxy
2087 Directs CONNECT method to proxy server to connect to
2088 destination host (and port). It may not be allowed on your
2092 begin_http_relay( SOCKET s )
2096 char *user = NULL, *pass = NULL;
2099 debug("begin_http_relay()\n");
2101 if (proxy_auth_type != PROXY_AUTH_NONE) {
2102 /* Get username/password for authentication */
2103 if ((user = relay_user) == NULL &&
2104 (user = getparam(ENV_HTTP_PROXY_USER)) == NULL &&
2105 (user = getparam(ENV_CONNECT_USER)) == NULL &&
2106 (user = getusername()) == NULL )
2107 fatal("Cannot decide username for proxy authentication.");
2108 if ((pass = getparam(ENV_HTTP_PROXY_PASSWORD)) == NULL &&
2109 (pass = getparam(ENV_CONNECT_PASSWORD)) == NULL &&
2110 (pass = readpass("Enter proxy authentication password for %s@%s: ",
2111 user, relay_host)) == NULL)
2112 fatal("Cannot decide password for proxy authentication.");
2113 user = strdup(user);
2114 pass = strdup(pass);
2116 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
2118 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth(s, user, pass) < 0)
2120 if (sendf(s,"\r\n") < 0)
2122 if ( user ) { memset(user, 0, strlen(user)); free(user); }
2123 if ( pass ) { memset(pass, 0, strlen(pass)); free(pass); }
2126 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
2127 debug("failed to read http response.\n");
2131 result = atoi(strchr(buf,' '));
2134 /* Conguraturation, connected via http proxy server! */
2135 debug("connected, start user session.\n");
2137 case 302: /* redirect */
2139 if (line_input(s, buf, sizeof(buf)))
2142 if (expect(buf, "Location: ")) {
2143 relay_host = cut_token(buf, "//");
2144 cut_token(buf, "/");
2145 relay_port = atoi(cut_token(buf, ":"));
2147 } while (strcmp(buf,"\r\n") != 0);
2151 /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
2152 * not strictly the correct response, but some proxies do send this (e.g.
2153 * Symantec's Raptor firewall) */
2154 case 401: /* WWW-Auth required */
2155 case 407: /* Proxy-Auth required */
2156 /** NOTE: As easy implementation, we support only BASIC scheme
2157 and ignore realm. */
2158 /* If proxy_auth_type is PROXY_AUTH_BASIC and get
2159 this result code, authentication was failed. */
2160 if (proxy_auth_type != PROXY_AUTH_NONE) {
2161 error("Authentication failed.\n");
2164 auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2166 if ( line_input(s, buf, sizeof(buf)) ) {
2170 if (expect(buf, auth_what)) {
2171 /* parse type and realm */
2172 char *scheme, *realm;
2173 scheme = cut_token(buf, " ");
2174 realm = cut_token(scheme, " ");
2175 if ( scheme == NULL || realm == NULL ) {
2176 debug("Invalid format of %s field.", auth_what);
2177 return -1; /* fail */
2180 if (expect(scheme, "basic")) {
2181 proxy_auth_type = PROXY_AUTH_BASIC;
2183 debug("Unsupported authentication type: %s", scheme);
2187 } while (strcmp(buf,"\r\n") != 0);
2189 if ( proxy_auth_type == PROXY_AUTH_NONE ) {
2190 debug("Can't find %s in response header.", auth_what);
2198 debug("http proxy is not allowed.\n");
2201 /* skip to end of response header */
2203 if ( line_input(s, buf, sizeof(buf) ) ) {
2204 debug("Can't skip response headers\n");
2207 } while ( strcmp(buf,"\r\n") != 0 );
2215 Returns 1 if data is available, otherwise return 0
2218 fddatalen( SOCKET fd )
2223 if ( st.st_mode & _S_IFIFO ) {
2224 /* in case of PIPE */
2225 if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
2226 NULL, 0, NULL, &len, NULL) ) {
2227 if ( GetLastError() == ERROR_BROKEN_PIPE ) {
2228 /* PIPE source is closed */
2229 /* read() will detects EOF */
2232 fatal("PeekNamedPipe() failed, errno=%d\n",
2236 } else if ( st.st_mode & _S_IFREG ) {
2237 /* in case of regular file (redirected) */
2238 len = 1; /* always data ready */
2239 } else if ( _kbhit() ) {
2240 /* in case of console */
2247 /* relay byte from stdin to socket and fro socket to stdout */
2249 do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
2251 /** vars for local input data **/
2252 char lbuf[1024]; /* local input buffer */
2253 int lbuf_len; /* available data in lbuf */
2254 int f_local; /* read local input more? */
2255 /** vars for remote input data **/
2256 char rbuf[1024]; /* remote input buffer */
2257 int rbuf_len; /* available data in rbuf */
2258 int f_remote; /* read remote input more? */
2259 /** other variables **/
2261 fd_set *ifds, *ofds;
2262 struct timeval *tmo;
2264 struct timeval win32_tmo;
2267 /* repeater between stdin/out and socket */
2268 nfds = ((local_in<remote)? remote: local_in) +1;
2269 ifds = FD_ALLOC(nfds);
2270 ofds = FD_ALLOC(nfds);
2271 f_local = 1; /* yes, read from local */
2272 f_remote = 1; /* yes, read from remote */
2276 while ( f_local || f_remote ) {
2281 /** prepare for reading local input **/
2282 if ( f_local && (lbuf_len < sizeof(lbuf)) ) {
2284 if ( local_type != LOCAL_SOCKET ) {
2285 /* select() on Winsock is not accept standard handle.
2286 So use select() with short timeout and checking data
2287 in stdin by another method. */
2288 win32_tmo.tv_sec = 0;
2289 win32_tmo.tv_usec = 10*1000; /* 10 ms */
2292 #endif /* !_WIN32 */
2293 FD_SET( local_in, ifds );
2296 /** prepare for reading remote input **/
2297 if ( f_remote && (rbuf_len < sizeof(rbuf)) ) {
2298 FD_SET( remote, ifds );
2301 /* FD_SET( local_out, ofds ); */
2302 /* FD_SET( remote, ofds ); */
2304 if ( select( nfds, ifds, ofds, NULL, tmo ) == -1 ) {
2306 error( "select() failed, %d\n", socket_errno());
2310 /* fake ifds if local is stdio handle because
2311 select() of Winsock does not accept stdio
2313 if (f_local && (local_type!=LOCAL_SOCKET) && (0<fddatalen(local_in)))
2314 FD_SET(0,ifds); /* data ready */
2317 /* remote => local */
2318 if ( FD_ISSET(remote, ifds) && (rbuf_len < sizeof(rbuf)) ) {
2319 len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
2321 debug("connection closed by peer\n");
2322 f_remote = 0; /* no more read from socket */
2324 } else if ( len == -1 ) {
2325 if (socket_errno() != ECONNRESET) {
2327 fatal("recv() faield, %d\n", socket_errno());
2329 debug("ECONNRESET detected\n");
2332 debug("recv %d bytes\n", len);
2333 if ( 1 < f_debug ) /* more verbose */
2334 report_bytes( "<<<", rbuf, rbuf_len);
2339 /* local => remote */
2340 if ( FD_ISSET(local_in, ifds) && (lbuf_len < sizeof(lbuf)) ) {
2341 if (local_type == LOCAL_SOCKET)
2342 len = recv(local_in, lbuf + lbuf_len,
2343 sizeof(lbuf)-lbuf_len, 0);
2345 len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
2348 debug("local input is EOF\n");
2349 shutdown(remote, 1); /* no-more writing */
2351 } else if ( len == -1 ) {
2352 /* error on reading from stdin */
2353 fatal("recv() failed, errno = %d\n", errno);
2360 /* flush data in buffer to socket */
2361 if ( 0 < lbuf_len ) {
2362 len = send(remote, lbuf, lbuf_len, 0);
2363 if ( 1 < f_debug ) /* more verbose */
2364 report_bytes( ">>>", lbuf, lbuf_len);
2366 fatal("send() failed, %d\n", socket_errno());
2367 } else if ( 0 < len ) {
2368 /* move data on to top of buffer */
2369 debug("send %d bytes\n", len);
2372 memcpy( lbuf, lbuf+len, lbuf_len );
2373 assert( 0 <= lbuf_len );
2377 /* flush data in buffer to local output */
2378 if ( 0 < rbuf_len ) {
2379 if (local_type == LOCAL_SOCKET)
2380 len = send( local_out, rbuf, rbuf_len, 0);
2382 len = write( local_out, rbuf, rbuf_len);
2384 fatal("output (local) failed, errno=%d\n", errno);
2387 if ( len < rbuf_len )
2388 memcpy( rbuf, rbuf+len, rbuf_len );
2389 assert( 0 <= rbuf_len );
2399 accept_connection (u_short port)
2403 struct sockaddr_in name;
2405 struct sockaddr client;
2408 /* Create the socket. */
2409 debug("Creating source port to forward.\n");
2410 sock = socket (PF_INET, SOCK_STREAM, 0);
2412 fatal("socket() failed, errno=%d\n", socket_errno());
2414 /* Give the socket a name. */
2415 name.sin_family = AF_INET;
2416 name.sin_port = htons (port);
2417 name.sin_addr.s_addr = htonl (INADDR_ANY);
2418 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
2419 fatal ("bind() failed, errno=%d\n", socket_errno());
2421 if (listen( sock, 1) < 0)
2422 fatal ("listen() failed, errno=%d\n", socket_errno());
2424 socklen = sizeof(client);
2425 connection = accept( sock, &client, &socklen);
2426 if ( connection < 0 )
2427 fatal ("accept() failed, errno=%d\n", socket_errno());
2436 /** Main of program **/
2438 main( int argc, char **argv )
2441 SOCKET remote; /* socket */
2442 SOCKET local_in; /* Local input */
2443 SOCKET local_out; /* Local output */
2446 WSAStartup( 0x101, &wsadata);
2449 /* initialization */
2451 getarg( argc, argv );
2452 debug("Program is $Revision$\n");
2454 /* Open local_in and local_out if forwarding a port */
2455 if ( local_type == LOCAL_SOCKET ) {
2456 /* Relay between local port and destination */
2457 local_in = local_out = accept_connection( local_port );
2459 /* Relay between stdin/stdout and desteination */
2463 _setmode(local_in, O_BINARY);
2464 _setmode(local_out, O_BINARY);
2470 if (0 < connect_timeout)
2471 set_timeout (connect_timeout);
2472 #endif /* not _WIN32 */
2474 /* make connection */
2475 if ( relay_method == METHOD_DIRECT ) {
2476 remote = open_connection (dest_host, dest_port);
2477 if ( remote == SOCKET_ERROR )
2478 fatal( "Unable to connect to destination host, errno=%d\n",
2481 remote = open_connection (relay_host, relay_port);
2482 if ( remote == SOCKET_ERROR )
2483 fatal( "Unable to connect to relay host, errno=%d\n",
2487 /** resolve destination host (SOCKS) **/
2488 if (relay_method == METHOD_SOCKS &&
2489 socks_resolve == RESOLVE_LOCAL &&
2490 local_resolve (dest_host, &dest_addr, &socks_ns) < 0) {
2491 fatal("Unknown host: %s", dest_host);
2494 /** relay negociation **/
2495 switch ( relay_method ) {
2497 if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
2498 ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
2499 fatal( "failed to begin relaying via SOCKS.\n");
2503 ret = begin_http_relay(remote);
2506 fatal("failed to begin relaying via HTTP.\n");
2510 /* retry with authentication */
2515 debug("connected\n");
2518 if (0 < connect_timeout)
2520 #endif /* not _WIN32 */
2523 debug ("start relaying.\n");
2524 do_repeater(local_in, local_out, remote);
2525 debug ("relaying done.\n");
2526 closesocket(remote);
2527 if ( local_type == LOCAL_SOCKET)
2528 closesocket(local_in);
2532 debug ("that's all, bye.\n");
2537 /* ------------------------------------------------------------
2539 compile-command: "cc connect.c -o connect"
2543 ------------------------------------------------------------ */
2545 /*** end of connect.c ***/