1 /***********************************************************************
2 * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
4 * Copyright (c) 2000-2006 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: 100 $
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: connect.c 100 2007-07-03 10:48:26Z gotoh $";
258 /* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */
260 #define snprintf _snprintf
261 #define vsnprintf _vsnprintf
264 /* consider Borland C */
267 #define _setmode setmode
271 Win32 environment does not support -R option (vc and cygwin)
272 Win32 native compilers does not support -w option, yet (vc)
274 static char *usage
= "usage: %s [-dnhst45] [-p local-port]"
277 "[-w timeout] \n" /* cygwin cannot -R */
278 #else /* not __CYGWIN32__ */
279 " \n" /* VC cannot -w nor -R */
280 #endif /* not __CYGWIN32__ */
281 #else /* not _WIN32 */
282 /* help message for UNIX */
283 "[-R resolve] [-w timeout] \n"
284 #endif /* not _WIN32 */
285 " [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
286 " [-T proxy-server[:port]]\n"
287 " [-c telnet-proxy-command]\n"
290 /* name of this program */
291 char *progname
= NULL
;
292 char *progdesc
= "connect --- simple relaying command via proxy.";
293 char *rcs_revstr
= "$Revision: 100 $";
295 int major_version
= 1;
296 int minor_version
= 0;
298 /* set of character for strspn() */
299 const char *digits
= "0123456789";
300 const char *dotdigits
= "0123456789.";
305 /* report flag to hide secure information */
308 int connect_timeout
= 0;
310 /* local input type */
311 #define LOCAL_STDIO 0
312 #define LOCAL_SOCKET 1
313 char *local_type_names
[] = { "stdio", "socket" };
314 int local_type
= LOCAL_STDIO
;
315 u_short local_port
= 0; /* option 'p' */
316 int f_hold_session
= 0; /* option 'P' */
318 char *telnet_command
= "telnet %h %p";
320 /* utiity types, pair holder of number and string */
326 /* relay method, server and port */
327 #define METHOD_UNDECIDED 0
328 #define METHOD_DIRECT 1
329 #define METHOD_SOCKS 2
330 #define METHOD_HTTP 3
331 #define METHOD_TELNET 4
332 char *method_names
[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
334 int relay_method
= METHOD_UNDECIDED
; /* relaying method */
335 char *relay_host
= NULL
; /* hostname of relay server */
336 u_short relay_port
= 0; /* port of relay server */
337 char *relay_user
= NULL
; /* user name for auth */
339 /* destination target host and port */
340 char *dest_host
= NULL
;
341 struct sockaddr_in dest_addr
;
342 u_short dest_port
= 0;
344 /* informations for SOCKS */
345 #define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
346 #define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
347 #define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
348 #define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
349 #define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
350 #define SOCKS5_REP_REFUSED 0x05 /* connection refused */
351 #define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
352 #define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
353 #define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
354 #define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
356 LOOKUP_ITEM socks5_rep_names
[] = {
357 { SOCKS5_REP_SUCCEEDED
, "succeeded"},
358 { SOCKS5_REP_FAIL
, "general SOCKS server failure"},
359 { SOCKS5_REP_NALLOWED
, "connection not allowed by ruleset"},
360 { SOCKS5_REP_NUNREACH
, "Network unreachable"},
361 { SOCKS5_REP_HUNREACH
, "Host unreachable"},
362 { SOCKS5_REP_REFUSED
, "connection refused"},
363 { SOCKS5_REP_EXPIRED
, "TTL expired"},
364 { SOCKS5_REP_CNOTSUP
, "Command not supported"},
365 { SOCKS5_REP_ANOTSUP
, "Address not supported"},
366 { SOCKS5_REP_INVADDR
, "Invalid address"},
370 /* SOCKS5 authentication methods */
371 #define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
372 #define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
373 #define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
374 #define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
375 #define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
376 #define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
377 #define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
379 #define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */
380 #define SOCKS4_REP_REJECTED 91 /* request rejected or failed */
381 #define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */
382 #define SOCKS4_REP_USERID 93 /* user id not matched */
384 LOOKUP_ITEM socks4_rep_names
[] = {
385 { SOCKS4_REP_SUCCEEDED
, "request granted (succeeded)"},
386 { SOCKS4_REP_REJECTED
, "request rejected or failed"},
387 { SOCKS4_REP_IDENT_FAIL
, "cannot connect identd"},
388 { SOCKS4_REP_USERID
, "user id not matched"},
392 #define RESOLVE_UNKNOWN 0
393 #define RESOLVE_LOCAL 1
394 #define RESOLVE_REMOTE 2
395 #define RESOLVE_BOTH 3
396 char *resolve_names
[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
398 int socks_version
= 5; /* SOCKS protocol version */
399 int socks_resolve
= RESOLVE_UNKNOWN
;
400 struct sockaddr_in socks_ns
;
401 char *socks5_auth
= NULL
;
403 /* Environment variable names */
404 #define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
405 #define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
406 #define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
408 #define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
409 #define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
410 #define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
412 #define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
413 #define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */
414 #define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */
415 #define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */
416 #define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */
418 #define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
419 #define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
420 #define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
422 #define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
424 #define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */
425 #define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
427 #define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
428 #define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT"
429 #define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT"
430 #define ENV_HTTP_DIRECT "HTTP_DIRECT"
431 #define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
433 #define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
434 #define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
436 /* Prefix string of HTTP_PROXY */
437 #define HTTP_PROXY_PREFIX "http://"
438 #define PROXY_AUTH_NONE 0
439 #define PROXY_AUTH_BASIC 1
440 #define PROXY_AUTH_DIGEST 2
441 int proxy_auth_type
= PROXY_AUTH_NONE
;
443 /* reason of end repeating */
444 #define REASON_UNK -2
445 #define REASON_ERROR -1
446 #define REASON_CLOSED_BY_LOCAL 0
447 #define REASON_CLOSED_BY_REMOTE 1
449 /* return value of relay start function. */
450 #define START_ERROR -1
452 #define START_RETRY 1
454 /* socket related definitions */
459 #define SOCKET_ERROR -1
463 #define socket_errno() WSAGetLastError()
465 #define closesocket close
466 #define socket_errno() (errno)
473 /* packet operation macro */
474 #define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
476 /* debug message output */
478 debug( const char *fmt
, ... )
482 va_start( args
, fmt
);
483 fprintf(stderr
, "DEBUG: ");
484 vfprintf( stderr
, fmt
, args
);
490 debug_( const char *fmt
, ... ) /* without prefix */
494 va_start( args
, fmt
);
495 vfprintf( stderr
, fmt
, args
);
500 /* error message output */
502 error( const char *fmt
, ... )
505 va_start( args
, fmt
);
506 fprintf(stderr
, "ERROR: ");
507 vfprintf( stderr
, fmt
, args
);
512 fatal( const char *fmt
, ... )
515 va_start( args
, fmt
);
516 fprintf(stderr
, "FATAL: ");
517 vfprintf( stderr
, fmt
, args
);
524 xmalloc (size_t size
)
526 void *ret
= malloc(size
);
528 fatal("Cannot allocate memory: %d bytes.\n", size
);
533 downcase( char *str
)
541 return str
; /* return converted arg */
545 expand_host_and_port (const char *fmt
, const char *host
, int port
)
548 char *buf
, *dst
, *ptr
;
549 size_t len
= strlen(fmt
) + strlen(host
) + 20;
562 snprintf (dst
, len
, "%d", port
);
569 dst
= buf
+ strlen (buf
);
570 } else if (*src
== '\\') {
594 assert (strlen(buf
) < len
);
600 lookup_resolve( const char *str
)
602 char *buf
= strdup( str
);
606 if ( strcmp( buf
, "both" ) == 0 )
608 else if ( strcmp( buf
, "remote" ) == 0 )
609 ret
= RESOLVE_REMOTE
;
610 else if ( strcmp( buf
, "local" ) == 0 )
612 else if ( strspn(buf
, dotdigits
) == strlen(buf
) ) {
613 #ifndef WITH_RESOLVER
614 fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
615 #endif /* not WITH_RESOLVER */
616 ret
= RESOLVE_LOCAL
; /* this case is also 'local' */
617 socks_ns
.sin_addr
.s_addr
= inet_addr(buf
);
618 socks_ns
.sin_family
= AF_INET
;
621 ret
= RESOLVE_UNKNOWN
;
630 static char buf
[1024];
631 DWORD size
= sizeof(buf
);
633 GetUserName( buf
, &size
);
635 #else /* not _WIN32 */
636 struct passwd
*pw
= getpwuid(getuid());
638 fatal("getpwuid() failed for uid: %d\n", getuid());
640 #endif /* not _WIN32 */
644 check STR is begin with substr with case-ignored comparison.
645 Return 1 if matched, otherwise 0.
648 expect( char *str
, char *substr
)
650 int len
= strlen(substr
);
651 while ( 0 < len
-- ) {
652 if ( toupper(*str
) != toupper(*substr
) )
653 return 0; /* not matched */
656 return 1; /* good, matched */
660 /** PARAMETER operation **/
661 #define PARAMETER_FILE "/etc/connectrc"
662 #define PARAMETER_DOTFILE ".connectrc"
667 PARAMETER_ITEM parameter_table
[] = {
668 { ENV_SOCKS_SERVER
, NULL
},
669 { ENV_SOCKS5_SERVER
, NULL
},
670 { ENV_SOCKS4_SERVER
, NULL
},
671 { ENV_SOCKS_RESOLVE
, NULL
},
672 { ENV_SOCKS5_RESOLVE
, NULL
},
673 { ENV_SOCKS4_RESOLVE
, NULL
},
674 { ENV_SOCKS5_USER
, NULL
},
675 { ENV_SOCKS5_PASSWD
, NULL
},
676 { ENV_SOCKS5_PASSWORD
, NULL
},
677 { ENV_HTTP_PROXY
, NULL
},
678 { ENV_HTTP_PROXY_USER
, NULL
},
679 { ENV_HTTP_PROXY_PASSWORD
, NULL
},
680 { ENV_CONNECT_USER
, NULL
},
681 { ENV_CONNECT_PASSWORD
, NULL
},
682 { ENV_SSH_ASKPASS
, NULL
},
683 { ENV_SOCKS5_DIRECT
, NULL
},
684 { ENV_SOCKS4_DIRECT
, NULL
},
685 { ENV_SOCKS_DIRECT
, NULL
},
686 { ENV_HTTP_DIRECT
, NULL
},
687 { ENV_CONNECT_DIRECT
, NULL
},
688 { ENV_SOCKS5_AUTH
, NULL
},
693 find_parameter_item(const char* name
)
696 for( i
= 0; parameter_table
[i
].name
!= NULL
; i
++ ){
697 if ( strcmp(name
, parameter_table
[i
].name
) == 0 )
698 return ¶meter_table
[i
];
704 read_parameter_file_1(const char* name
)
709 f
= fopen(name
, "r");
711 debug("Reading parameter file(%s)\n", name
);
712 for ( line
= 1; fgets(lbuf
, 1024, f
); line
++ ) {
713 char *p
, *q
, *param
, *value
;
714 p
= strchr(lbuf
, '\n');
716 fatal("%s:%d: buffer overflow\n", name
, line
);
718 p
= strchr(lbuf
, '#');
721 for ( p
= lbuf
; *p
; p
++ )
722 if( *p
!= ' ' && *p
!= '\t' ) break;
723 if ( *p
== '\0' ) continue;
727 error("%s:%d: missing equal sign\n", name
, line
);
730 for ( q
= p
- 1; q
>= lbuf
; q
-- )
731 if ( *q
!= ' ' && *q
!= '\t' ) break;
734 if ( *p
!= ' ' && *p
!= '\t' ) break;
737 for ( p
--; p
>= lbuf
; p
-- )
738 if ( *p
!= ' ' && *p
!= '\t' ) break;
740 if ( param
&& value
) {
741 PARAMETER_ITEM
*item
;
742 item
= find_parameter_item(param
);
743 if ( item
== NULL
) {
744 error("%s:%d: unknown parameter `%s'\n", name
, line
, param
);
747 item
->value
= strdup(value
);
748 debug("Parameter `%s' is set to `%s'\n", param
, value
);
755 read_parameter_file(void)
757 #if !defined(_WIN32) || defined(cygwin)
762 read_parameter_file_1(PARAMETER_FILE
);
763 #if !defined(_WIN32) || defined(cygwin)
764 pw
= getpwuid(getuid());
766 fatal("getpwuid() failed for uid: %d\n", getuid());
767 name
= xmalloc(strlen(pw
->pw_dir
) + strlen(PARAMETER_DOTFILE
) + 2);
768 strcpy(name
, pw
->pw_dir
);
769 strcat(name
, "/" PARAMETER_DOTFILE
);
770 read_parameter_file_1(name
);
776 getparam(const char* name
)
778 char *value
= getenv(name
);
779 if ( value
== NULL
){
780 PARAMETER_ITEM
*item
= find_parameter_item(name
);
788 /** DIRECT connection **/
789 #define MAX_DIRECT_ADDR_LIST 256
798 struct ADDRPAIR direct_addr_list
[MAX_DIRECT_ADDR_LIST
];
799 int n_direct_addr_list
= 0;
802 mask_addr (void *addr
, void *mask
, int addrlen
)
807 while ( 0 < addrlen
-- )
812 add_direct_addr (struct in_addr
*addr
, struct in_addr
*mask
, int negative
)
814 struct in_addr iaddr
;
816 if ( MAX_DIRECT_ADDR_LIST
<= n_direct_addr_list
) {
817 error("direct address table is full!\n");
821 mask_addr(&iaddr
, mask
, sizeof(iaddr
));
822 s
= strdup(inet_ntoa(iaddr
));
823 debug("adding direct addr entry: %s%s/%s\n",
824 negative
? "!": "", s
, inet_ntoa(*mask
));
826 memcpy( &direct_addr_list
[n_direct_addr_list
].addr
,
827 &iaddr
, sizeof(iaddr
));
828 memcpy( &direct_addr_list
[n_direct_addr_list
].mask
,
829 mask
, sizeof(*mask
));
830 direct_addr_list
[n_direct_addr_list
].name
= NULL
;
831 direct_addr_list
[n_direct_addr_list
].negative
= negative
;
832 n_direct_addr_list
++;
837 /* add domain/host name entry to direct name table */
839 add_direct_host( const char *name
, int negative
)
841 if ( MAX_DIRECT_ADDR_LIST
<= n_direct_addr_list
) {
842 error("direct address table is full!\n");
849 debug("adding direct name entry: %s%s\n", negative
? "!": "", name
);
850 direct_addr_list
[n_direct_addr_list
].name
= downcase(strdup(name
));
851 direct_addr_list
[n_direct_addr_list
].negative
= negative
;
852 n_direct_addr_list
++;
858 parse_addr_pair (const char *str
, struct in_addr
*addr
, struct in_addr
*mask
)
861 /* Assume already be splitted by separator
862 and formatted as folowing:
863 1) 12.34.56.789/255.255.255.0
866 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
872 assert( str
!= NULL
);
876 dsta
= (u_char
*)&addr
->s_addr
;
877 dstm
= (u_char
*)&mask
->s_addr
;
878 for (i
=0; i
<4; i
++ ) {
880 break; /* case of format #3 */
881 if ( !isdigit(*ptr
) )
882 return -1; /* format error: */
883 *dsta
++ = atoi( ptr
);
884 *dstm
++ = 255; /* automatic mask for format #3 */
885 while ( isdigit(*ptr
) ) /* skip digits */
892 /* At this point, *ptr points '/' or EOS ('\0') */
894 return 0; /* complete as format #3 */
896 return -1; /* format error */
897 /* Now parse mask for format #1 or #2 */
899 mask
->s_addr
= 0; /* clear automatic mask */
901 if ( strchr( ptr
, '.') ) {
902 /* case of format #1 */
903 dstm
= (u_char
*)&mask
->s_addr
;
904 for (i
=0; i
<4; i
++) {
905 if ( !isdigit(*ptr
) )
906 return -1; /* format error: */
908 while ( isdigit(*ptr
) ) /* skip digits */
913 break; /* from for loop */
915 /* complete as format #1 */
917 /* case of format #2 */
918 if ( !isdigit(*ptr
) )
919 return -1; /* format error: */
922 return -1; /* format error */
923 mask
->s_addr
= (n
==0)? 0: htonl(((u_long
)0xFFFFFFFF)<<(32-n
));
924 /* complete as format #1 */
930 initialize_direct_addr (void)
934 char *env
= NULL
, *beg
, *next
, *envkey
= NULL
;
935 struct in_addr addr
, mask
;
937 if ( relay_method
== METHOD_SOCKS
){
938 if ( socks_version
== 5 )
939 envkey
= ENV_SOCKS5_DIRECT
;
941 envkey
= ENV_SOCKS4_DIRECT
;
942 env
= getparam(envkey
);
944 env
= getparam(ENV_SOCKS_DIRECT
);
945 } else if ( relay_method
== METHOD_HTTP
){
946 env
= getparam(ENV_HTTP_DIRECT
);
950 env
= getparam(ENV_CONNECT_DIRECT
);
953 return; /* no entry */
954 debug("making direct addr list from: '%s'\n", env
);
955 env
= strdup( env
); /* reallocate to modify */
959 if ( MAX_DIRECT_ADDR_LIST
<= n_entries
) {
960 error("too many entries in %s", envkey
);
961 break; /* from do loop */
963 next
= strchr( beg
, ',');
973 if ( !parse_addr_pair( beg
, &addr
, &mask
) ) {
974 add_direct_addr( &addr
, &mask
, negative
);
976 add_direct_host( beg
, negative
);
980 } while ( next
!= NULL
);
987 cmp_addr (void *addr1
, void *addr2
, int addrlen
)
989 return memcmp( addr1
, addr2
, addrlen
);
993 is_direct_address (const struct in_addr addr
)
996 struct in_addr iaddr
;
998 /* Note: assume IPV4 address !! */
999 for (i
=0; i
<n_direct_addr_list
; i
++ ) {
1000 if (direct_addr_list
[i
].name
!= NULL
)
1001 continue; /* it's name entry */
1002 neg
= direct_addr_list
[i
].negative
;
1004 mask_addr( &iaddr
, &direct_addr_list
[i
].mask
,
1005 sizeof(struct in_addr
));
1006 if (cmp_addr(&iaddr
, &direct_addr_list
[i
].addr
,
1007 sizeof(struct in_addr
)) == 0) {
1009 a
= strdup(inet_ntoa(direct_addr_list
[i
].addr
));
1010 m
= strdup(inet_ntoa(direct_addr_list
[i
].mask
));
1011 debug("match with: %s/%s%s\n", a
, m
, neg
? " (negative)": "");
1017 debug("not matched, addr to be relayed: %s\n", inet_ntoa(addr
));
1018 return 0; /* not direct */
1022 /* check s1 is ends with s2.
1023 return 1 if exact match or domain part match.
1024 return 0 if s1 is shorter than s2 or partial match.
1026 ends_with("bar.com", "bar.com") => 1 (exact match)
1027 ends_with("foo.bar.com", "bar.com") => 1 (domain match)
1028 ends_with("foo.beebar.com", "bar.com") => 0 (partial match)
1029 ends_with("bar", "bar.com") => 0 (shorter)
1031 domain_match(const char *s1
, const char *s2
)
1034 const char *tail1
, *tail2
;
1037 if (len1
< len2
|| len1
== 0 || len2
== 0)
1038 return 0; /* not match */
1041 while (0 < len1
&& 0 < len2
) {
1042 if (*--tail1
!= *--tail2
)
1043 break; /* not match */
1047 return 0; /* not match */
1048 /* Now exact match, domain match or partial match.
1049 Return true if exact or domain match.
1050 Or continue checking. */
1051 if (tail1
== s1
|| tail1
[-1] == '.')
1052 return 1; /* match! */
1053 return 0; /* not match */
1056 /* Check given NAME is ends with one of
1057 registered direct name entry.
1058 Return 1 if matched, or 0.
1061 is_direct_name (const char *name
)
1065 debug("checking %s is for direct?\n", name
);
1066 name
= downcase(strdup(name
));
1069 return 0; /* false */
1071 for (i
=0; i
<n_direct_addr_list
; i
++ ) {
1075 dname
= direct_addr_list
[i
].name
;
1077 continue; /* it's addr/mask entry */
1078 neg
= direct_addr_list
[i
].negative
;
1079 if (domain_match(name
, dname
)) {
1080 debug("match with: %s%s\n", dname
, neg
? " (negative)": "");
1082 return 0; /* not direct */
1084 return 1; /* direct*/
1088 return 0; /* not matched */
1091 /* check to connect to HOST directyly?
1092 return 1 if to be direct, 0 for else. */
1094 check_direct(const char *host
)
1096 struct in_addr addr
;
1097 addr
.s_addr
= inet_addr(host
);
1098 if (addr
.s_addr
!= INADDR_NONE
) {
1099 /* case of IP address */
1100 if (is_direct_address(addr
)) {
1101 debug("%s is for direct.\n", host
);
1102 return 1; /* true */
1105 /* case of hostname */
1106 if (is_direct_name(host
)) {
1107 debug("%s is for direct.\n", host
);
1108 return 1; /* true */
1111 debug("%s is for not direct.\n", host
);
1112 return 0; /* false */
1116 /** TTY operation **/
1122 intr_handler(int sig
)
1128 tty_change_echo(int fd
, int enable
)
1130 static struct termios ntio
, otio
; /* new/old termios */
1131 static sigset_t nset
, oset
; /* new/old sigset */
1132 static struct sigaction nsa
, osa
; /* new/old sigaction */
1133 static int disabled
= 0;
1135 if ( disabled
&& enable
) {
1137 tcsetattr(fd
, TCSANOW
, &otio
);
1139 /* resotore sigaction */
1140 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1141 sigaction(SIGINT
, &osa
, NULL
);
1142 if ( intr_flag
!= 0 ) {
1143 /* re-generate signal */
1144 kill(getpid(), SIGINT
);
1149 } else if (!disabled
&& !enable
) {
1150 /* set SIGINTR handler and break syscall on singal */
1152 sigaddset(&nset
, SIGTSTP
);
1153 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1155 memset(&nsa
, 0, sizeof(nsa
));
1156 nsa
.sa_handler
= intr_handler
;
1157 sigaction(SIGINT
, &nsa
, &osa
);
1159 if (tcgetattr(fd
, &otio
) == 0 && (otio
.c_lflag
& ECHO
)) {
1162 ntio
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
1163 (void) tcsetattr(fd
, TCSANOW
, &ntio
);
1170 #define TTY_NAME "/dev/tty"
1172 tty_readpass( const char *prompt
, char *buf
, size_t size
)
1176 tty
= open(TTY_NAME
, O_RDWR
);
1178 error("Unable to open %s\n", TTY_NAME
);
1179 return -1; /* can't open tty */
1182 return -1; /* no room */
1183 write(tty
, prompt
, strlen(prompt
));
1185 tty_change_echo(tty
, 0); /* disable echo */
1186 ret
= read(tty
,buf
, size
-1);
1187 tty_change_echo(tty
, 1); /* restore */
1188 write(tty
, "\n", 1); /* new line */
1190 if ( strchr(buf
,'\n') == NULL
)
1200 w32_intr_handler(DWORD dwCtrlType
)
1202 if ( dwCtrlType
== CTRL_C_EVENT
) {
1210 #define tty_readpass w32_tty_readpass
1212 w32_tty_readpass( const char *prompt
, char *buf
, size_t size
)
1214 HANDLE in
= CreateFile("CONIN$", GENERIC_READ
|GENERIC_WRITE
,
1215 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1216 HANDLE out
= CreateFile("CONOUT$", GENERIC_WRITE
,
1217 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1221 if (in
== INVALID_HANDLE_VALUE
|| out
== INVALID_HANDLE_VALUE
)
1222 fatal("Cannot open console. (errno=%d)", GetLastError());
1224 WriteFile(out
, prompt
, strlen(prompt
), &bytes
, 0);
1225 SetConsoleCtrlHandler(w32_intr_handler
, TRUE
); /* add handler */
1226 GetConsoleMode(in
, &mode
);
1227 SetConsoleMode(in
, mode
&~ENABLE_ECHO_INPUT
); /* disable echo */
1228 ret
= ReadFile(in
, buf
, size
, &bytes
, 0);
1229 SetConsoleMode(in
, mode
); /* enable echo */
1230 SetConsoleCtrlHandler( w32_intr_handler
, FALSE
); /* remove handler */
1232 GenerateConsoleCtrlEvent(CTRL_C_EVENT
, 0); /* re-signal */
1233 WriteFile(out
,"\n", 1, &bytes
, 0);
1241 /*** User / Password ***/
1243 /* SOCKS5 and HTTP Proxy authentication may requires username and
1244 password. We ll give it via environment variable or tty.
1245 Username and password for authentication are decided by
1248 Username is taken from
1249 1) server location spec (i.e. user@host:port)
1250 2) environment variables (see tables.1)
1251 3) system account name currently logged in.
1253 Table.1 Order of environment variables for username
1255 | SOCKS v5 | SOCKS v4 | HTTP proxy |
1256 --+-------------+-------------+-----------------+
1257 1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
1258 --+-------------+-------------+ |
1260 --+---------------------------+-----------------+
1262 --+---------------------------------------------+
1264 Password is taken from
1265 1) by environment variables (see table.2)
1266 2) by entering from tty.
1268 Table.2 Order of environment variables for password
1270 | SOCKS v5 | HTTP proxy |
1271 --+-----------------+---------------------+
1272 1 | SOCKS5_PASSWD | |
1273 --+-----------------+ HTTP_PROXY_PASSWORD |
1274 2 | SOCKS5_PASSWORD | |
1275 --+-----------------+---------------------+
1276 3 | CONNECT_PASSWORD |
1277 --+---------------------------------------+
1279 Note: SOCKS5_PASSWD which is added in rev. 1.79
1280 to share value with NEC SOCKS implementation.
1284 determine_relay_user ()
1287 /* get username from environment variable, or system. */
1288 if (relay_method
== METHOD_SOCKS
) {
1289 if (user
== NULL
&& socks_version
== 5)
1290 user
= getparam (ENV_SOCKS5_USER
);
1291 if (user
== NULL
&& socks_version
== 4)
1292 user
= getparam (ENV_SOCKS4_USER
);
1294 user
= getparam (ENV_SOCKS_USER
);
1295 } else if (relay_method
== METHOD_HTTP
) {
1297 user
= getparam (ENV_HTTP_PROXY_USER
);
1300 user
= getparam (ENV_CONNECT_USER
);
1301 /* determine relay user by system call if not yet. */
1303 user
= getusername();
1308 determine_relay_password ()
1311 if (pass
== NULL
&& relay_method
== METHOD_HTTP
)
1312 pass
= getparam(ENV_HTTP_PROXY_PASSWORD
);
1313 if (pass
== NULL
&& relay_method
== METHOD_SOCKS
)
1314 pass
= getparam(ENV_SOCKS5_PASSWD
);
1315 if (pass
== NULL
&& relay_method
== METHOD_SOCKS
)
1316 pass
= getparam(ENV_SOCKS5_PASSWORD
);
1318 pass
= getparam(ENV_CONNECT_PASSWORD
);
1323 /*** network operations ***/
1327 Determine relay informations:
1328 method, host, port, and username.
1329 1st arg, METHOD should be METHOD_xxx.
1330 2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
1331 hostname is domain name or dot notation.
1332 If port is omitted, use 80 for METHOD_HTTP method,
1333 use 1080 for METHOD_SOCKS method.
1334 Username is also able to given by 3rd. format.
1335 2nd argument SPEC can be NULL. if NULL, use environment variable.
1338 set_relay( int method
, char *spec
)
1340 char *buf
, *sep
, *resolve
;
1342 relay_method
= method
;
1344 read_parameter_file();
1345 initialize_direct_addr();
1346 if (n_direct_addr_list
== 0) {
1347 debug ("No direct address are specified.\n");
1349 debug ("%d direct address entries.\n", n_direct_addr_list
);
1354 return -1; /* nothing to do */
1357 if ( spec
== NULL
) {
1358 switch ( socks_version
) {
1360 spec
= getparam(ENV_SOCKS5_SERVER
);
1363 spec
= getparam(ENV_SOCKS4_SERVER
);
1368 spec
= getparam(ENV_SOCKS_SERVER
);
1371 fatal("Failed to determine SOCKS server.\n");
1372 relay_port
= 1080; /* set default first */
1374 /* determine resolve method */
1375 if ( socks_resolve
== RESOLVE_UNKNOWN
) {
1376 if ( ((socks_version
== 5) &&
1377 ((resolve
= getparam(ENV_SOCKS5_RESOLVE
)) != NULL
)) ||
1378 ((socks_version
== 4) &&
1379 ((resolve
= getparam(ENV_SOCKS4_RESOLVE
)) != NULL
)) ||
1380 ((resolve
= getparam(ENV_SOCKS_RESOLVE
)) != NULL
) ) {
1381 socks_resolve
= lookup_resolve( resolve
);
1382 if ( socks_resolve
== RESOLVE_UNKNOWN
)
1383 fatal("Invalid resolve method: %s\n", resolve
);
1386 if ( socks_version
== 5 )
1387 socks_resolve
= RESOLVE_REMOTE
;
1389 socks_resolve
= RESOLVE_LOCAL
;
1396 spec
= getparam(ENV_HTTP_PROXY
);
1398 fatal("You must specify http proxy server\n");
1399 relay_port
= 80; /* set default first */
1403 spec
= getparam(ENV_TELNET_PROXY
);
1405 fatal("You must specify telnet proxy server\n");
1406 relay_port
= 23; /* set default first */
1409 if (expect( spec
, HTTP_PROXY_PREFIX
)) {
1410 /* URL format like: "http://server:port/" */
1411 /* extract server:port part */
1412 buf
= strdup( spec
+ strlen(HTTP_PROXY_PREFIX
));
1413 buf
[strcspn(buf
, "/")] = '\0';
1415 /* assume spec is aready "server:port" format */
1416 buf
= strdup( spec
);
1420 /* check username in spec */
1421 sep
= strchr( spec
, '@' );
1422 if ( sep
!= NULL
) {
1424 relay_user
= strdup( spec
);
1427 if (relay_user
== NULL
)
1428 relay_user
= determine_relay_user();
1430 /* split out hostname and port number from spec */
1431 sep
= strchr(spec
,':');
1432 if ( sep
== NULL
) {
1433 /* hostname only, port is already set as default */
1434 relay_host
= strdup( spec
);
1436 /* hostname and port */
1437 relay_port
= atoi(sep
+1);
1439 relay_host
= strdup( spec
);
1447 resolve_port( const char *service
)
1450 if ( service
[strspn (service
, digits
)] == '\0' ) {
1451 /* all digits, port number */
1452 port
= atoi(service
);
1454 /* treat as service name */
1455 struct servent
*ent
;
1456 ent
= getservbyname( service
, NULL
);
1457 if ( ent
== NULL
) {
1458 debug("Unknown service, '%s'\n", service
);
1461 port
= ntohs(ent
->s_port
);
1462 debug("service: %s => %d\n", service
, port
);
1465 return (u_short
)port
;
1473 ptr
= strstr(rcs_revstr
, ": ");
1475 revstr
= strdup("unknown");
1479 /* assume subversion's keyword expansion like "Revision: 96". */
1480 minor_version
= atoi(ptr
);
1481 revstr
= xmalloc(20);
1482 snprintf(revstr
, 20, "%d.%d", major_version
, minor_version
);
1486 getarg( int argc
, char **argv
)
1489 char *ptr
, *server
= (char*)NULL
;
1490 int method
= METHOD_DIRECT
;
1496 while ( (0 < argc
) && (**argv
== '-') ) {
1500 case 's': /* use SOCKS */
1501 method
= METHOD_SOCKS
;
1504 case 'n': /* no proxy */
1505 method
= METHOD_DIRECT
;
1508 case 'h': /* use http-proxy */
1509 method
= METHOD_HTTP
;
1512 method
= METHOD_TELNET
;
1515 case 'S': /* specify SOCKS server */
1518 method
= METHOD_SOCKS
;
1521 error("option '-%c' needs argument.\n", *ptr
);
1526 case 'H': /* specify http-proxy server */
1529 method
= METHOD_HTTP
;
1532 error("option '-%c' needs argument.\n", *ptr
);
1536 case 'T': /* specify telnet proxy server */
1539 method
= METHOD_TELNET
;
1542 error("option '-%c' needs argument.\n", *ptr
);
1550 telnet_command
= *argv
;
1552 error("option '%c' needs argument.\n", *ptr
);
1560 case 'p': /* specify port to forward */
1563 local_type
= LOCAL_SOCKET
;
1564 local_port
= resolve_port(*argv
);
1566 error("option '-%c' needs argument.\n", *ptr
);
1575 connect_timeout
= atoi(*argv
);
1577 error("option '-%c' needs argument.\n", *ptr
);
1581 #endif /* not _WIN32 */
1594 socks5_auth
= *argv
;
1596 error("option '-%c' needs argument.\n", *ptr
);
1601 case 'R': /* specify resolve method */
1604 socks_resolve
= lookup_resolve( *argv
);
1606 error("option '-%c' needs argument.\n", *ptr
);
1611 case 'V': /* print version */
1612 fprintf(stderr
, "%s\nVersion %s\n", progdesc
, revstr
);
1615 case 'd': /* debug mode */
1620 error("unknown option '-%c'\n", *ptr
);
1632 set_relay( method
, server
);
1634 /* check destination HOST (MUST) */
1636 fprintf(stderr
, "%s\nVersion %s\n", progdesc
, revstr
);
1637 fprintf(stderr
, usage
, progname
);
1640 dest_host
= argv
[0];
1641 /* decide port or service name from programname or argument */
1642 if ( ((ptr
=strrchr( progname
, '/' )) != NULL
) ||
1643 ((ptr
=strchr( progname
, '\\')) != NULL
) )
1647 if ( dest_port
== 0 ) {
1648 /* accept only if -P is not specified. */
1650 /* get port number from argument (prior to progname) */
1651 /* NOTE: This way is for cvs ext method. */
1652 dest_port
= resolve_port(argv
[1]);
1653 } else if ( strncmp( ptr
, "connect-", 8) == 0 ) {
1654 /* decide port number from program name */
1655 char *str
= strdup( ptr
+8 );
1656 str
[strcspn( str
, "." )] = '\0';
1657 dest_port
= resolve_port(str
);
1661 /* check port number */
1662 if ( dest_port
<= 0 ) {
1663 error( "You must specify the destination port correctly.\n");
1667 if ( (relay_method
!= METHOD_DIRECT
) && (relay_port
<= 0) ) {
1668 error("Invalid relay port: %d\n", dest_port
);
1674 /* report for debugging */
1675 debug("relay_method = %s (%d)\n",
1676 method_names
[relay_method
], relay_method
);
1677 if ( relay_method
!= METHOD_DIRECT
) {
1678 debug("relay_host=%s\n", relay_host
);
1679 debug("relay_port=%d\n", relay_port
);
1680 debug("relay_user=%s\n", relay_user
);
1682 if ( relay_method
== METHOD_SOCKS
) {
1683 debug("socks_version=%d\n", socks_version
);
1684 debug("socks_resolve=%s (%d)\n",
1685 resolve_names
[socks_resolve
], socks_resolve
);
1687 debug("local_type=%s\n", local_type_names
[local_type
]);
1688 if ( local_type
== LOCAL_SOCKET
) {
1689 debug("local_port=%d\n", local_port
);
1691 debug (" with holding remote session.\n");
1693 debug("dest_host=%s\n", dest_host
);
1694 debug("dest_port=%d\n", dest_port
);
1696 fprintf(stderr
, usage
, progname
);
1703 /* Time-out feature is not allowed for Win32 native compilers. */
1704 /* MSVC and Borland C cannot but Cygwin and UNIXes can. */
1706 /* timeout signal hander */
1710 signal( SIGALRM
, SIG_IGN
);
1712 error( "timed out\n" );
1716 /* set timeout param = seconds, 0 clears */
1718 set_timeout(int timeout
)
1720 /* This feature is allowed for UNIX or cygwin environments, currently */
1721 if ( timeout
== 0 ) {
1722 debug( "clearing timeout\n" );
1723 signal( SIGALRM
, SIG_IGN
);
1726 debug( "setting timeout: %d seconds\n", timeout
);
1727 signal(SIGALRM
, (void *)sig_timeout
);
1733 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1735 switch_ns (struct sockaddr_in
*ns
)
1738 memcpy (&_res
.nsaddr_list
[0], ns
, sizeof(*ns
));
1740 debug("Using nameserver at %s\n", inet_ntoa(ns
->sin_addr
));
1742 #endif /* !_WIN32 && !__CYGWIN32__ */
1745 TODO: fallback if askpass execution failed.
1749 local_resolve (const char *host
, struct sockaddr_in
*addr
)
1751 struct hostent
*ent
;
1752 if ( strspn(host
, dotdigits
) == strlen(host
) ) {
1753 /* given by IPv4 address */
1754 addr
->sin_family
= AF_INET
;
1755 addr
->sin_addr
.s_addr
= inet_addr(host
);
1757 debug("resolving host by name: %s\n", host
);
1758 ent
= gethostbyname (host
);
1760 memcpy (&addr
->sin_addr
, ent
->h_addr
, ent
->h_length
);
1761 addr
->sin_family
= ent
->h_addrtype
;
1762 debug("resolved: %s (%s)\n",
1763 host
, inet_ntoa(addr
->sin_addr
));
1765 debug("failed to resolve locally.\n");
1766 return -1; /* failed */
1769 return 0; /* good */
1773 open_connection( const char *host
, u_short port
)
1776 struct sockaddr_in saddr
;
1778 /* resolve address of proxy or direct target */
1779 if (local_resolve (host
, &saddr
) < 0) {
1780 error("can't resolve hostname: %s\n", host
);
1781 return SOCKET_ERROR
;
1783 saddr
.sin_port
= htons(port
);
1785 debug("connecting to %s:%u\n", inet_ntoa(saddr
.sin_addr
), port
);
1786 s
= socket( AF_INET
, SOCK_STREAM
, 0 );
1787 if ( connect( s
, (struct sockaddr
*)&saddr
, sizeof(saddr
))
1789 debug( "connect() failed.\n");
1790 return SOCKET_ERROR
;
1796 report_text( char *prefix
, char *buf
)
1798 static char work
[1024];
1804 return; /* don't report */
1805 debug("%s \"", prefix
);
1807 memset( work
, 0, sizeof(work
));
1809 while ( *buf
&& ((tmp
-work
) < (int)sizeof(work
)-5) ) {
1811 case '\t': *tmp
++ = '\\'; *tmp
++ = 't'; break;
1812 case '\r': *tmp
++ = '\\'; *tmp
++ = 'r'; break;
1813 case '\n': *tmp
++ = '\\'; *tmp
++ = 'n'; break;
1814 case '\\': *tmp
++ = '\\'; *tmp
++ = '\\'; break;
1816 if ( isprint(*buf
) ) {
1819 int consumed
= tmp
- work
;
1820 snprintf( tmp
, sizeof(work
)-consumed
,
1821 "\\x%02X", (unsigned char)*buf
);
1836 report_bytes( char *prefix
, char *buf
, int len
)
1840 debug( "%s", prefix
);
1842 fprintf( stderr
, " %02x", *(unsigned char *)buf
);
1846 fprintf(stderr
, "\n");
1851 atomic_out( SOCKET s
, char *buf
, int size
)
1855 assert( buf
!= NULL
);
1859 while ( 0 < size
) {
1860 len
= send( s
, buf
+ret
, size
, 0 );
1862 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1867 debug("atomic_out() [some bytes]\n");
1868 debug(">>> xx xx xx xx ...\n");
1870 debug("atomic_out() [%d bytes]\n", ret
);
1871 report_bytes(">>>", buf
, ret
);
1877 atomic_in( SOCKET s
, char *buf
, int size
)
1881 assert( buf
!= NULL
);
1886 while ( 0 < size
) {
1887 len
= recv( s
, buf
+ret
, size
, 0 );
1889 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1890 } else if ( len
== 0 ) {
1891 fatal( "Connection closed by peer.\n");
1897 debug("atomic_in() [some bytes]\n");
1898 debug("<<< xx xx xx xx ...\n");
1900 debug("atomic_in() [%d bytes]\n", ret
);
1901 report_bytes("<<<", buf
, ret
);
1907 line_input( SOCKET s
, char *buf
, int size
)
1911 return 0; /* no error */
1913 while ( 0 < size
) {
1914 switch ( recv( s
, dst
, 1, 0) ) { /* recv one-by-one */
1916 error("recv() error\n");
1917 return -1; /* error */
1919 size
= 0; /* end of stream */
1922 /* continue reading until last 1 char is EOL? */
1923 if ( *dst
== '\n' ) {
1934 report_text( "<<<", buf
);
1939 Span token in given string STR until char in DELIM is appeared.
1940 Then replace contiguous DELIMS with '\0' for string termination
1941 and returns next pointer.
1942 If no next token, return NULL.
1945 cut_token( char *str
, char *delim
)
1947 char *ptr
= str
+ strcspn(str
, delim
);
1948 char *end
= ptr
+ strspn(ptr
, delim
);
1957 lookup(int num
, LOOKUP_ITEM
*items
)
1960 while (0 <= items
[i
].num
) {
1961 if (items
[i
].num
== num
)
1962 return items
[i
].str
;
1969 password input routine
1970 Use ssh-askpass (same mechanism to OpenSSH)
1973 readpass( const char* prompt
, ...)
1975 static char buf
[1000]; /* XXX, don't be fix length */
1977 va_start(args
, prompt
);
1978 vsnprintf(buf
, sizeof(buf
), prompt
, args
);
1981 if ( getparam(ENV_SSH_ASKPASS
)
1982 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1983 && getenv("DISPLAY")
1984 #endif /* not _WIN32 && not __CYGWIN32__ */
1986 /* use ssh-askpass to get password */
1988 char *askpass
= getparam(ENV_SSH_ASKPASS
), *cmd
;
1989 int cmd_size
= strlen(askpass
) +1 +1 +strlen(buf
) +1 +1;
1990 cmd
= xmalloc(cmd_size
);
1991 snprintf(cmd
, cmd_size
, "%s \"%s\"", askpass
, buf
);
1992 fp
= popen(cmd
, "r");
1995 return NULL
; /* fail */
1997 if (fgets(buf
, sizeof(buf
), fp
) == NULL
)
1998 return NULL
; /* fail */
2001 tty_readpass( buf
, buf
, sizeof(buf
));
2003 buf
[strcspn(buf
, "\r\n")] = '\0';
2008 socks5_do_auth_userpass( int s
)
2010 unsigned char buf
[1024], *ptr
;
2014 /* do User/Password authentication. */
2015 /* This feature requires username and password from
2016 command line argument or environment variable,
2018 if (relay_user
== NULL
)
2019 fatal("cannot determine user name.\n");
2021 /* get password from environment variable if exists. */
2022 if ((pass
=determine_relay_password()) == NULL
&&
2023 (pass
=readpass("Enter SOCKS5 password for %s@%s: ",
2024 relay_user
, relay_host
)) == NULL
)
2025 fatal("Cannot get password for user: %s\n", relay_user
);
2027 /* make authentication packet */
2029 PUT_BYTE( ptr
++, 1 ); /* subnegotiation ver.: 1 */
2030 len
= strlen( relay_user
); /* ULEN and UNAME */
2031 PUT_BYTE( ptr
++, len
);
2032 strcpy( ptr
, relay_user
);
2034 len
= strlen( pass
); /* PLEN and PASSWD */
2035 PUT_BYTE( ptr
++, strlen(pass
));
2036 strcpy( ptr
, pass
);
2038 memset (pass
, 0, strlen(pass
)); /* erase password */
2040 /* send it and get answer */
2042 atomic_out( s
, buf
, ptr
-buf
);
2044 atomic_in( s
, buf
, 2 );
2048 return 0; /* success */
2050 return -1; /* fail */
2054 socks5_getauthname( int auth
)
2057 case SOCKS5_AUTH_REJECT
: return "REJECTED";
2058 case SOCKS5_AUTH_NOAUTH
: return "NO-AUTH";
2059 case SOCKS5_AUTH_GSSAPI
: return "GSSAPI";
2060 case SOCKS5_AUTH_USERPASS
: return "USERPASS";
2061 case SOCKS5_AUTH_CHAP
: return "CHAP";
2062 case SOCKS5_AUTH_EAP
: return "EAP";
2063 case SOCKS5_AUTH_MAF
: return "MAF";
2064 default: return "(unknown)";
2073 AUTH_METHOD_ITEM socks5_auth_table
[] = {
2074 { "none", SOCKS5_AUTH_NOAUTH
},
2075 { "gssapi", SOCKS5_AUTH_GSSAPI
},
2076 { "userpass", SOCKS5_AUTH_USERPASS
},
2077 { "chap", SOCKS5_AUTH_CHAP
},
2082 socks5_auth_parse_1(char *start
, char *end
){
2084 for ( ; *start
; start
++ )
2085 if ( *start
!= ' ' && *start
!= '\t') break;
2086 for ( end
--; end
>= start
; end
-- ) {
2087 if ( *end
!= ' ' && *end
!= '\t'){
2093 for ( i
= 0; socks5_auth_table
[i
].name
!= NULL
; i
++ ){
2094 if ( strncmp(start
, socks5_auth_table
[i
].name
, len
) == 0) {
2095 return socks5_auth_table
[i
].auth
;
2098 fatal("Unknown auth method: %s\n", start
);
2103 socks5_auth_parse(char *start
, unsigned char *auth_list
, int max_auth
){
2106 while ( i
< max_auth
) {
2107 end
= strchr(start
, ',');
2108 if (*start
&& end
) {
2109 auth_list
[i
++] = socks5_auth_parse_1(start
, end
);
2115 if ( *start
&& ( i
< max_auth
) ){
2116 for( end
= start
; *end
; end
++ );
2117 auth_list
[i
++] = socks5_auth_parse_1(start
, end
);
2119 fatal("Too much auth method.\n");
2124 /* begin SOCKS5 relaying
2125 And no authentication is supported.
2128 begin_socks5_relay( SOCKET s
)
2130 unsigned char buf
[256], *ptr
, *env
= socks5_auth
;
2131 unsigned char n_auth
= 0; unsigned char auth_list
[10], auth_method
;
2132 int len
, auth_result
, i
;
2134 debug( "begin_socks_relay()\n");
2136 /* request authentication */
2138 PUT_BYTE( ptr
++, 5); /* SOCKS version (5) */
2141 env
= getparam(ENV_SOCKS5_AUTH
);
2142 if ( env
== NULL
) {
2143 /* add no-auth authentication */
2144 auth_list
[n_auth
++] = SOCKS5_AUTH_NOAUTH
;
2145 /* add user/pass authentication */
2146 auth_list
[n_auth
++] = SOCKS5_AUTH_USERPASS
;
2148 n_auth
= socks5_auth_parse(env
, auth_list
, 10);
2150 PUT_BYTE( ptr
++, n_auth
); /* num auth */
2151 for (i
=0; i
<n_auth
; i
++) {
2152 debug("available auth method[%d] = %s (0x%02x)\n",
2153 i
, socks5_getauthname(auth_list
[i
]), auth_list
[i
]);
2154 PUT_BYTE( ptr
++, auth_list
[i
]); /* authentications */
2156 atomic_out( s
, buf
, ptr
-buf
); /* send requst */
2157 atomic_in( s
, buf
, 2 ); /* recv response */
2158 if ( (buf
[0] != 5) || /* ver5 response */
2159 (buf
[1] == 0xFF) ) { /* check auth method */
2160 error("No auth method accepted.\n");
2163 auth_method
= buf
[1];
2165 debug("auth method: %s\n", socks5_getauthname(auth_method
));
2167 switch ( auth_method
) {
2168 case SOCKS5_AUTH_REJECT
:
2169 error("No acceptable authentication method\n");
2170 return -1; /* fail */
2172 case SOCKS5_AUTH_NOAUTH
:
2177 case SOCKS5_AUTH_USERPASS
:
2178 auth_result
= socks5_do_auth_userpass(s
);
2182 error("Unsupported authentication method: %s\n",
2183 socks5_getauthname( auth_method
));
2184 return -1; /* fail */
2186 if ( auth_result
!= 0 ) {
2187 error("Authentication failed.\n");
2190 /* request to connect */
2192 PUT_BYTE( ptr
++, 5); /* SOCKS version (5) */
2193 PUT_BYTE( ptr
++, 1); /* CMD: CONNECT */
2194 PUT_BYTE( ptr
++, 0); /* FLG: 0 */
2195 if ( dest_addr
.sin_addr
.s_addr
== 0 ) {
2196 /* resolved by SOCKS server */
2197 PUT_BYTE( ptr
++, 3); /* ATYP: DOMAINNAME */
2198 len
= strlen(dest_host
);
2199 PUT_BYTE( ptr
++, len
); /* DST.ADDR (len) */
2200 memcpy( ptr
, dest_host
, len
); /* (hostname) */
2203 /* resolved localy */
2204 PUT_BYTE( ptr
++, 1 ); /* ATYP: IPv4 */
2205 memcpy( ptr
, &dest_addr
.sin_addr
.s_addr
, sizeof(dest_addr
.sin_addr
));
2206 ptr
+= sizeof(dest_addr
.sin_addr
);
2208 PUT_BYTE( ptr
++, dest_port
>>8); /* DST.PORT */
2209 PUT_BYTE( ptr
++, dest_port
&0xFF);
2210 atomic_out( s
, buf
, ptr
-buf
); /* send request */
2211 atomic_in( s
, buf
, 4 ); /* recv response */
2212 if ( (buf
[1] != SOCKS5_REP_SUCCEEDED
) ) { /* check reply code */
2213 error("Got error response from SOCKS server: %d (%s).\n",
2214 buf
[1], lookup(buf
[1], socks5_rep_names
));
2218 switch ( buf
[3] ) { /* case by ATYP */
2219 case 1: /* IP v4 ADDR*/
2220 atomic_in( s
, ptr
, 4+2 ); /* recv IPv4 addr and port */
2222 case 3: /* DOMAINNAME */
2223 atomic_in( s
, ptr
, 1 ); /* recv name and port */
2224 atomic_in( s
, ptr
+1, *(unsigned char*)ptr
+ 2);
2226 case 4: /* IP v6 ADDR */
2227 atomic_in( s
, ptr
, 16+2 ); /* recv IPv6 addr and port */
2231 /* Conguraturation, connected via SOCKS5 server! */
2235 /* begin SOCKS protocol 4 relaying
2236 And no authentication is supported.
2238 There's SOCKS protocol version 4 and 4a. Protocol version
2239 4a has capability to resolve hostname by SOCKS server, so
2240 we don't need resolving IP address of destination host on
2243 Environment variable SOCKS_RESOLVE directs how to resolve
2244 IP addess. There's 3 keywords allowed; "local", "remote"
2245 and "both" (case insensitive). Keyword "local" means taht
2246 target host name is resolved by localhost resolver
2247 (usualy with gethostbyname()), "remote" means by remote
2248 SOCKS server, "both" means to try resolving by localhost
2251 SOCKS4 protocol and authentication of SOCKS5 protocol
2252 requires user name on connect request.
2253 User name is determined by following method.
2255 1. If server spec has user@hostname:port format then
2256 user part is used for this SOCKS server.
2258 2. Get user name from environment variable LOGNAME, USER
2263 begin_socks4_relay( SOCKET s
)
2265 unsigned char buf
[256], *ptr
;
2267 debug( "begin_socks_relay()\n");
2269 /* make connect request packet
2271 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
2273 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
2276 PUT_BYTE( ptr
++, 4); /* protocol version (4) */
2277 PUT_BYTE( ptr
++, 1); /* CONNECT command */
2278 PUT_BYTE( ptr
++, dest_port
>>8); /* destination Port */
2279 PUT_BYTE( ptr
++, dest_port
&0xFF);
2280 /* destination IP */
2281 memcpy(ptr
, &dest_addr
.sin_addr
, sizeof(dest_addr
.sin_addr
));
2282 ptr
+= sizeof(dest_addr
.sin_addr
);
2283 if ( dest_addr
.sin_addr
.s_addr
== 0 )
2284 *(ptr
-1) = 1; /* fake, protocol 4a */
2286 if (relay_user
== NULL
)
2287 fatal( "Cannot determine user name.\n");
2288 strcpy( ptr
, relay_user
);
2289 ptr
+= strlen( relay_user
) +1;
2290 /* destination host name (for protocol 4a) */
2291 if ( (socks_version
== 4) && (dest_addr
.sin_addr
.s_addr
== 0)) {
2292 strcpy( ptr
, dest_host
);
2293 ptr
+= strlen( dest_host
) +1;
2295 /* send command and get response
2296 response is: VN:1, CD:1, PORT:2, ADDR:4 */
2297 atomic_out( s
, buf
, ptr
-buf
); /* send request */
2298 atomic_in( s
, buf
, 8 ); /* recv response */
2299 if ( (buf
[1] != SOCKS4_REP_SUCCEEDED
) ) { /* check reply code */
2300 error("Got error response: %d: '%s'.\n",
2301 buf
[1], lookup(buf
[1], socks4_rep_names
));
2302 return -1; /* failed */
2305 /* Conguraturation, connected via SOCKS4 server! */
2310 sendf(SOCKET s
, const char *fmt
,...)
2312 static char buf
[10240]; /* xxx, enough? */
2315 va_start( args
, fmt
);
2316 vsnprintf( buf
, sizeof(buf
), fmt
, args
);
2319 report_text(">>>", buf
);
2320 if ( send(s
, buf
, strlen(buf
), 0) == SOCKET_ERROR
) {
2321 debug("failed to send http request. errno=%d\n", socket_errno());
2327 const char *base64_table
=
2328 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2331 make_base64_string(const char *str
)
2336 int bits
, data
, src_len
, dst_len
;
2337 /* make base64 string */
2338 src_len
= strlen(str
);
2339 dst_len
= (src_len
+2)/3*4;
2340 buf
= xmalloc(dst_len
+1);
2342 src
= (unsigned char *)str
;
2343 dst
= (unsigned char *)buf
;
2344 while ( dst_len
-- ) {
2346 data
= (data
<< 8) | *src
;
2351 *dst
++ = base64_table
[0x3F & (data
>> (bits
-6))];
2355 /* fix-up tail padding */
2356 switch ( src_len
%3 ) {
2367 basic_auth (SOCKET s
)
2371 const char *user
= relay_user
;
2375 /* Get username/password for authentication */
2377 fatal("Cannot decide username for proxy authentication.");
2378 if ((pass
= determine_relay_password ()) == NULL
&&
2379 (pass
= readpass("Enter proxy authentication password for %s@%s: ",
2380 relay_user
, relay_host
)) == NULL
)
2381 fatal("Cannot decide password for proxy authentication.");
2383 len
= strlen(user
)+strlen(pass
)+1;
2384 userpass
= xmalloc(len
+1);
2385 snprintf(userpass
, len
+1, "%s:%s", user
, pass
);
2386 memset (pass
, 0, strlen(pass
));
2387 cred
= make_base64_string(userpass
);
2388 memset (userpass
, 0, len
);
2390 f_report
= 0; /* don't report for security */
2391 ret
= sendf(s
, "Proxy-Authorization: Basic %s\r\n", cred
);
2393 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
2395 memset(cred
, 0, strlen(cred
));
2401 /* begin relaying via HTTP proxy
2402 Directs CONNECT method to proxy server to connect to
2403 destination host (and port). It may not be allowed on your
2407 begin_http_relay( SOCKET s
)
2413 debug("begin_http_relay()\n");
2415 if (sendf(s
,"CONNECT %s:%d HTTP/1.0\r\n", dest_host
, dest_port
) < 0)
2417 if (proxy_auth_type
== PROXY_AUTH_BASIC
&& basic_auth (s
) < 0)
2419 if (sendf(s
,"\r\n") < 0)
2423 if ( line_input(s
, buf
, sizeof(buf
)) < 0 ) {
2424 debug("failed to read http response.\n");
2429 if (!strchr(buf
, ' ')) {
2430 error ("Unexpected http response: '%s'.\n", buf
);
2433 result
= atoi(strchr(buf
,' '));
2437 /* Conguraturation, connected via http proxy server! */
2438 debug("connected, start user session.\n");
2440 case 302: /* redirect */
2442 if (line_input(s
, buf
, sizeof(buf
)))
2445 if (expect(buf
, "Location: ")) {
2446 relay_host
= cut_token(buf
, "//");
2447 cut_token(buf
, "/");
2448 relay_port
= atoi(cut_token(buf
, ":"));
2450 } while (strcmp(buf
,"\r\n") != 0);
2453 /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
2454 * not strictly the correct response, but some proxies do send this (e.g.
2455 * Symantec's Raptor firewall) */
2456 case 401: /* WWW-Auth required */
2457 case 407: /* Proxy-Auth required */
2458 /** NOTE: As easy implementation, we support only BASIC scheme
2459 and ignore realm. */
2460 /* If proxy_auth_type is PROXY_AUTH_BASIC and get
2461 this result code, authentication was failed. */
2462 if (proxy_auth_type
!= PROXY_AUTH_NONE
) {
2463 error("Authentication failed.\n");
2466 auth_what
= (result
== 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2468 if ( line_input(s
, buf
, sizeof(buf
)) ) {
2472 if (expect(buf
, auth_what
)) {
2473 /* parse type and realm */
2474 char *scheme
, *realm
;
2475 scheme
= cut_token(buf
, " ");
2476 realm
= cut_token(scheme
, " ");
2477 if ( scheme
== NULL
|| realm
== NULL
) {
2478 debug("Invalid format of %s field.", auth_what
);
2479 return START_ERROR
; /* fail */
2481 /* check supported auth type */
2482 if (expect(scheme
, "basic")) {
2483 proxy_auth_type
= PROXY_AUTH_BASIC
;
2485 debug("Unsupported authentication type: %s", scheme
);
2488 } while (strcmp(buf
,"\r\n") != 0);
2489 if ( proxy_auth_type
== PROXY_AUTH_NONE
) {
2490 debug("Can't find %s in response header.", auth_what
);
2498 debug("http proxy is not allowed.\n");
2501 /* skip to end of response header */
2503 if ( line_input(s
, buf
, sizeof(buf
) ) ) {
2504 debug("Can't skip response headers\n");
2507 } while ( strcmp(buf
,"\r\n") != 0 );
2512 /* begin relaying via TELNET proxy.
2513 Sends string specified by telnet_command (-c option) with
2514 replacing host name and port number to the socket. */
2516 begin_telnet_relay( SOCKET s
)
2520 char *good_phrase
= "connected to";
2521 char *bad_phrase_list
[] = {
2522 " failed", " refused", " rejected", " closed"
2527 debug("begin_telnet_relay()\n");
2530 debug("good phrase: '%s'\n", good_phrase
);
2531 debug("bad phrases");
2533 for (i
=0; i
< (sizeof(bad_phrase_list
) / sizeof(char*)); i
++) {
2534 debug_("%c '%s'", sep
, bad_phrase_list
[i
]);
2539 /* make request string with replacing %h by destination hostname
2540 and %p by port number, etc. */
2541 cmd
= expand_host_and_port(telnet_command
, dest_host
, dest_port
);
2543 /* Sorry, we send request string now without waiting a prompt. */
2544 if (sendf(s
, "%s\r\n", cmd
) < 0) {
2550 /* Process answer from proxy until good or bad phrase is detected. We
2551 assume that the good phrase should be appeared only in the final
2552 line of proxy responses. Bad keywods in the line causes operation
2553 fail. First checks a good phrase, then checks bad phrases.
2554 If no match, continue reading line from proxy. */
2555 while (!line_input(s
, buf
, sizeof(buf
)) && buf
[0] != '\0') {
2557 /* first, check good phrase */
2558 if (strstr(buf
, good_phrase
)) {
2559 debug("good phrase is detected: '%s'\n", good_phrase
);
2562 /* then, check bad phrase */
2563 for (i
=0; i
<(sizeof(bad_phrase_list
)/sizeof(char*)); i
++) {
2564 if (strstr(buf
, bad_phrase_list
[i
]) != NULL
) {
2565 debug("bad phrase is detected: '%s'\n", bad_phrase_list
[i
]);
2570 debug("error reading from telnet proxy\n");
2578 Returns 1 if data is available, otherwise return 0
2586 if ( st
.st_mode
& _S_IFIFO
) {
2587 /* in case of PIPE */
2588 if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE
),
2589 NULL
, 0, NULL
, &len
, NULL
) ) {
2590 if ( GetLastError() == ERROR_BROKEN_PIPE
) {
2591 /* PIPE source is closed */
2592 /* read() will detects EOF */
2595 fatal("PeekNamedPipe() failed, errno=%d\n",
2599 } else if ( st
.st_mode
& _S_IFREG
) {
2600 /* in case of regular file (redirected) */
2601 len
= 1; /* always data ready */
2602 } else if ( _kbhit() ) {
2603 /* in case of console */
2610 /* relay byte from stdin to socket and fro socket to stdout.
2611 returns reason of termination */
2613 do_repeater( SOCKET local_in
, SOCKET local_out
, SOCKET remote
)
2615 /** vars for local input data **/
2616 char lbuf
[1024]; /* local input buffer */
2617 int lbuf_len
; /* available data in lbuf */
2618 int f_local
; /* read local input more? */
2619 /** vars for remote input data **/
2620 char rbuf
[1024]; /* remote input buffer */
2621 int rbuf_len
; /* available data in rbuf */
2622 int f_remote
; /* read remote input more? */
2623 int close_reason
= REASON_UNK
; /* reason of end repeating */
2624 /** other variables **/
2627 struct timeval
*tmo
;
2629 struct timeval win32_tmo
;
2632 /* repeater between stdin/out and socket */
2633 nfds
= ((local_in
<remote
)? remote
: local_in
) +1;
2634 f_local
= 1; /* yes, read from local */
2635 f_remote
= 1; /* yes, read from remote */
2639 while ( f_local
|| f_remote
) {
2644 /** prepare for reading local input **/
2645 if ( f_local
&& (lbuf_len
< (int)sizeof(lbuf
)) ) {
2647 if ( local_type
!= LOCAL_SOCKET
) {
2648 /* select() on Winsock is not accept standard handle.
2649 So use select() with short timeout and checking data
2650 in stdin by another method. */
2651 win32_tmo
.tv_sec
= 0;
2652 win32_tmo
.tv_usec
= 10*1000; /* 10 ms */
2655 #endif /* !_WIN32 */
2656 FD_SET( local_in
, &ifds
);
2659 /** prepare for reading remote input **/
2660 if ( f_remote
&& (rbuf_len
< (int)sizeof(rbuf
)) ) {
2661 FD_SET( remote
, &ifds
);
2664 /* FD_SET( local_out, ofds ); */
2665 /* FD_SET( remote, ofds ); */
2667 if ( select( nfds
, &ifds
, &ofds
, (fd_set
*)NULL
, tmo
) == -1 ) {
2669 error( "select() failed, %d\n", socket_errno());
2670 return REASON_ERROR
;
2673 /* fake ifds if local is stdio handle because
2674 select() of Winsock does not accept stdio
2676 if (f_local
&& (local_type
!=LOCAL_SOCKET
) && (0<stdindatalen()))
2677 FD_SET(0,&ifds
); /* data ready */
2680 /* remote => local */
2681 if ( FD_ISSET(remote
, &ifds
) && (rbuf_len
< (int)sizeof(rbuf
)) ) {
2682 len
= recv( remote
, rbuf
+ rbuf_len
, sizeof(rbuf
)-rbuf_len
, 0);
2683 if ( len
== 0 || (len
== -1 && socket_errno() == ECONNRESET
)) {
2684 debug("connection %s by peer\n",
2685 (len
==0)? "closed": "reset");
2686 close_reason
= REASON_CLOSED_BY_REMOTE
;
2687 f_remote
= 0; /* no more read from socket */
2689 } else if ( len
== -1 ) {
2691 fatal("recv() failed, %d\n", socket_errno());
2693 debug("recv %d bytes\n", len
);
2694 if ( 1 < f_debug
) /* more verbose */
2695 report_bytes( "<<<", rbuf
+rbuf_len
, len
);
2700 /* local => remote */
2701 if ( FD_ISSET(local_in
, &ifds
) && (lbuf_len
< (int)sizeof(lbuf
)) ) {
2702 if (local_type
== LOCAL_SOCKET
)
2703 len
= recv(local_in
, lbuf
+ lbuf_len
,
2704 sizeof(lbuf
)-lbuf_len
, 0);
2706 len
= read(local_in
, lbuf
+ lbuf_len
, sizeof(lbuf
)-lbuf_len
);
2709 debug("local input is EOF\n");
2710 if (!f_hold_session
)
2711 shutdown(remote
, 1); /* no-more writing */
2713 close_reason
= REASON_CLOSED_BY_LOCAL
;
2714 } else if ( len
== -1 ) {
2715 /* error on reading from stdin */
2716 if (f_hold_session
) {
2717 debug ("failed to read from local\n");
2719 close_reason
= REASON_CLOSED_BY_LOCAL
;
2721 fatal("recv() failed, errno = %d\n", errno
);
2728 /* flush data in buffer to socket */
2729 if ( 0 < lbuf_len
) {
2730 len
= send(remote
, lbuf
, lbuf_len
, 0);
2732 fatal("send() failed, %d\n", socket_errno());
2733 } else if ( 0 < len
) {
2734 if ( 1 < f_debug
) /* more verbose */
2735 report_bytes( ">>>", lbuf
, len
);
2736 /* move data on to top of buffer */
2737 debug("sent %d bytes\n", len
);
2740 memcpy( lbuf
, lbuf
+len
, lbuf_len
);
2741 assert( 0 <= lbuf_len
);
2745 /* flush data in buffer to local output */
2746 if ( 0 < rbuf_len
) {
2747 if (local_type
== LOCAL_SOCKET
)
2748 len
= send( local_out
, rbuf
, rbuf_len
, 0);
2750 len
= write( local_out
, rbuf
, rbuf_len
);
2752 fatal("output (local) failed, errno=%d\n", errno
);
2755 if ( len
< rbuf_len
)
2756 memcpy( rbuf
, rbuf
+len
, rbuf_len
);
2757 assert( 0 <= rbuf_len
);
2759 if (f_local
== 0 && f_hold_session
) {
2760 debug ("closing local port without disconnecting from remote\n");
2762 shutdown (local_out
, 2);
2768 return close_reason
;
2772 accept_connection (u_short port
)
2774 static int sock
= -1;
2776 struct sockaddr_in name
;
2777 struct sockaddr client
;
2783 /* Create the socket. */
2784 debug("Creating source port to forward.\n");
2785 sock
= socket (PF_INET
, SOCK_STREAM
, 0);
2787 fatal("socket() failed, errno=%d\n", socket_errno());
2789 setsockopt (sock
, SOL_SOCKET
, SO_REUSEADDR
,
2790 (void*)&sockopt
, sizeof(sockopt
));
2792 /* Give the socket a name. */
2793 name
.sin_family
= AF_INET
;
2794 name
.sin_port
= htons (port
);
2795 name
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
2796 if (bind (sock
, (struct sockaddr
*) &name
, sizeof (name
)) < 0)
2797 fatal ("bind() failed, errno=%d\n", socket_errno());
2799 if (listen( sock
, 1) < 0)
2800 fatal ("listen() failed, errno=%d\n", socket_errno());
2802 /* wait for new connection with watching EOF of stdin. */
2803 debug ("waiting new connection at port %d (socket=%d)\n", port
, sock
);
2807 struct timeval
*ptmo
= NULL
;
2811 tmo
.tv_usec
= 100*1000; /* On Windows, 100ms timeout */
2815 FD_SET ((SOCKET
)sock
, &ifds
);
2817 FD_SET (0, &ifds
); /* watch stdin */
2819 n
= select (nfds
, &ifds
, NULL
, NULL
, ptmo
);
2821 fatal ("select() failed, %d\n", socket_errno());
2825 if (0 < stdindatalen()) {
2826 FD_SET (0, &ifds
); /* fake */
2831 if (FD_ISSET(0, &ifds
) && (getchar() <= 0)) {
2833 debug ("Give-up waiting port because stdin is closed.");
2836 if (FD_ISSET(sock
, &ifds
))
2837 break; /* socket is stimulated */
2840 socklen
= sizeof(client
);
2841 connection
= accept( sock
, &client
, &socklen
);
2842 if ( connection
< 0 )
2843 fatal ("accept() failed, errno=%d\n", socket_errno());
2849 /** Main of program **/
2851 main( int argc
, char **argv
)
2854 int remote
; /* socket */
2855 int local_in
; /* Local input */
2856 int local_out
; /* Local output */
2860 WSAStartup( 0x101, &wsadata
);
2863 /* initialization */
2865 getarg( argc
, argv
);
2866 debug("Program is $Revision: 100 $\n");
2868 /* Open local_in and local_out if forwarding a port */
2869 if ( local_type
== LOCAL_SOCKET
) {
2870 /* Relay between local port and destination */
2871 local_in
= local_out
= accept_connection( local_port
);
2873 /* Relay between stdin/stdout and desteination */
2877 _setmode(local_in
, O_BINARY
);
2878 _setmode(local_out
, O_BINARY
);
2884 if (0 < connect_timeout
)
2885 set_timeout (connect_timeout
);
2886 #endif /* not _WIN32 */
2888 if (check_direct(dest_host
))
2889 relay_method
= METHOD_DIRECT
;
2890 /* make connection */
2891 if ( relay_method
== METHOD_DIRECT
) {
2892 remote
= open_connection (dest_host
, dest_port
);
2893 if ( remote
== SOCKET_ERROR
)
2894 fatal( "Unable to connect to destination host, errno=%d\n",
2897 remote
= open_connection (relay_host
, relay_port
);
2898 if ( remote
== SOCKET_ERROR
)
2899 fatal( "Unable to connect to relay host, errno=%d\n",
2903 /** resolve destination host (SOCKS) **/
2904 #if !defined(_WIN32) && !defined(__CYGWIN32__)
2905 if (socks_ns
.sin_addr
.s_addr
!= 0)
2906 switch_ns (&socks_ns
);
2907 #endif /* not _WIN32 && not __CYGWIN32__ */
2908 if (relay_method
== METHOD_SOCKS
&&
2909 socks_resolve
== RESOLVE_LOCAL
&&
2910 local_resolve (dest_host
, &dest_addr
) < 0) {
2911 fatal("Unknown host: %s", dest_host
);
2914 /** relay negociation **/
2915 switch ( relay_method
) {
2917 if ( ((socks_version
== 5) && (begin_socks5_relay(remote
) < 0)) ||
2918 ((socks_version
== 4) && (begin_socks4_relay(remote
) < 0)) )
2919 fatal( "failed to begin relaying via SOCKS.\n");
2923 ret
= begin_http_relay(remote
);
2927 fatal("failed to begin relaying via HTTP.\n");
2931 /* retry with authentication */
2937 if (begin_telnet_relay(remote
) < 0)
2938 fatal("failed to begin relaying via telnet.\n");
2941 debug("connected\n");
2944 if (0 < connect_timeout
)
2946 #endif /* not _WIN32 */
2949 debug ("start relaying.\n");
2951 reason
= do_repeater(local_in
, local_out
, remote
);
2952 debug ("relaying done.\n");
2953 if (local_type
== LOCAL_SOCKET
&&
2954 reason
== REASON_CLOSED_BY_LOCAL
&&
2956 /* re-wait at local port without closing remote session */
2957 debug ("re-waiting at local port %d\n", local_port
);
2958 local_in
= local_out
= accept_connection( local_port
);
2959 debug ("re-start relaying\n");
2962 closesocket(remote
);
2963 if ( local_type
== LOCAL_SOCKET
)
2964 closesocket(local_in
);
2968 debug ("that's all, bye.\n");
2973 /* ------------------------------------------------------------
2975 compile-command: "cc connect.c -o connect"
2980 ------------------------------------------------------------ */
2982 /*** end of connect.c ***/