Updated bash.exe to MSYS-gca78a7c
[msysgit.git] / src / connect / connect.c
blobf5747117ab394496653c06263b8fb1a39a29d4c8
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 * ---------------------------------------------------------
28 * Getting Source
29 * ==============
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)
35 * is available:
36 * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz
38 * See more detail:
39 * http://www.taiyo.co.jp/~gotoh/ssh/connect.html
41 * How To Compile
42 * ==============
44 * On UNIX environment:
45 * $ gcc connect.c -o connect
47 * On SOLARIS:
48 * $ gcc -o connect -lresolv -lsocket -lnsl connect.c
50 * on Win32 environment:
51 * $ cl connect.c wsock32.lib advapi32.lib
52 * or
53 * $ bcc32 connect.c wsock32.lib advapi32.lib
54 * or
55 * $ gcc connect.c -o connect
57 * on Mac OS X environment:
58 * $ gcc connect.c -o connect -lresolv
59 * or
60 * $ gcc connect.c -o connect -DBIND_8_COMPAT=1
62 * How To Use
63 * ==========
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]
73 * host port
75 * "host" and "port" is for the target hostname and port-number to
76 * connect 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
81 * to use it.
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
106 * kill the program.
108 * The '-w' option specifys timeout seconds for making connection with
109 * TARGET host.
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
123 * or
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
129 * or
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 )
136 * TIPS
137 * ====
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/
192 * HTTP-Proxy --
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 ***********************************************************************/
205 #include <stdio.h>
206 #include <stdlib.h>
207 #include <string.h>
208 #include <ctype.h>
209 #include <memory.h>
210 #include <errno.h>
211 #include <assert.h>
212 #include <sys/types.h>
213 #include <stdarg.h>
214 #include <fcntl.h>
215 #include <signal.h>
217 #ifdef __CYGWIN32__
218 #undef _WIN32
219 #endif
221 #ifdef _WIN32
222 #include <windows.h>
223 #include <winsock.h>
224 #include <sys/stat.h>
225 #include <io.h>
226 #include <conio.h>
227 #else /* !_WIN32 */
228 #include <unistd.h>
229 #include <pwd.h>
230 #include <termios.h>
231 #include <sys/time.h>
232 #ifndef __hpux
233 #include <sys/select.h>
234 #endif /* __hpux */
235 #include <sys/socket.h>
236 #include <netinet/in.h>
237 #include <arpa/inet.h>
238 #include <netdb.h>
239 #if !defined(_WIN32) && !defined(__CYGWIN32__)
240 #define WITH_RESOLVER 1
241 #include <arpa/nameser.h>
242 #include <resolv.h>
243 #else /* not ( not _WIN32 && not __CYGWIN32__) */
244 #undef WITH_RESOLVER
245 #endif /* not ( not _WIN32 && not __CYGWIN32__) */
246 #endif /* !_WIN32 */
248 #ifdef _WIN32
249 #define ECONNRESET WSAECONNRESET
250 #endif /* _WI32 */
254 #ifndef LINT
255 static char *vcid = "$Id: connect.c 100 2007-07-03 10:48:26Z gotoh $";
256 #endif
258 /* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */
259 #ifdef _MSC_VER
260 #define snprintf _snprintf
261 #define vsnprintf _vsnprintf
262 #endif
264 /* consider Borland C */
265 #ifdef __BORLANDC__
266 #define _kbhit kbhit
267 #define _setmode setmode
268 #endif
270 /* help message.
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]"
275 #ifdef _WIN32
276 #ifdef __CYGWIN32__
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"
288 " host port\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 $";
294 char *revstr = NULL;
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.";
302 /* options */
303 int f_debug = 0;
305 /* report flag to hide secure information */
306 int f_report = 1;
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 */
321 typedef struct {
322 int num;
323 const char *str;
324 } LOOKUP_ITEM;
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"},
367 { -1, NULL }
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"},
389 { -1, NULL }
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
451 #define START_OK 0
452 #define START_RETRY 1
454 /* socket related definitions */
455 #ifndef _WIN32
456 #define SOCKET int
457 #endif
458 #ifndef SOCKET_ERROR
459 #define SOCKET_ERROR -1
460 #endif
462 #ifdef _WIN32
463 #define socket_errno() WSAGetLastError()
464 #else /* !_WIN32 */
465 #define closesocket close
466 #define socket_errno() (errno)
467 #endif /* !_WIN32 */
469 #ifdef _WIN32
470 #define popen _popen
471 #endif /* WIN32 */
473 /* packet operation macro */
474 #define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
476 /* debug message output */
477 void
478 debug( const char *fmt, ... )
480 va_list args;
481 if ( f_debug ) {
482 va_start( args, fmt );
483 fprintf(stderr, "DEBUG: ");
484 vfprintf( stderr, fmt, args );
485 va_end( args );
489 void
490 debug_( const char *fmt, ... ) /* without prefix */
492 va_list args;
493 if ( f_debug ) {
494 va_start( args, fmt );
495 vfprintf( stderr, fmt, args );
496 va_end( args );
500 /* error message output */
501 void
502 error( const char *fmt, ... )
504 va_list args;
505 va_start( args, fmt );
506 fprintf(stderr, "ERROR: ");
507 vfprintf( stderr, fmt, args );
508 va_end( args );
511 void
512 fatal( const char *fmt, ... )
514 va_list args;
515 va_start( args, fmt );
516 fprintf(stderr, "FATAL: ");
517 vfprintf( stderr, fmt, args );
518 va_end( args );
519 exit (EXIT_FAILURE);
523 void *
524 xmalloc (size_t size)
526 void *ret = malloc(size);
527 if (ret == NULL)
528 fatal("Cannot allocate memory: %d bytes.\n", size);
529 return ret;
532 char *
533 downcase( char *str )
535 char *buf = str;
536 while ( *buf ) {
537 if ( isupper(*buf) )
538 *buf += 'a'-'A';
539 buf++;
541 return str; /* return converted arg */
544 char *
545 expand_host_and_port (const char *fmt, const char *host, int port)
547 const char *src;
548 char *buf, *dst, *ptr;
549 size_t len = strlen(fmt) + strlen(host) + 20;
550 buf = xmalloc (len);
551 dst = buf;
552 src = fmt;
554 while (*src) {
555 if (*src == '%') {
556 switch (src[1]) {
557 case 'h':
558 strcpy (dst, host);
559 src += 2;
560 break;
561 case 'p':
562 snprintf (dst, len, "%d", port);
563 src += 2;
564 break;
565 default:
566 src ++;
567 break;
569 dst = buf + strlen (buf);
570 } else if (*src == '\\') {
571 switch (src[1]) {
572 case 'r': /* CR */
573 *dst++ = '\r';
574 src += 2;
575 break;
576 case 'n': /* LF */
577 *dst++ = '\n';
578 src += 2;
579 break;
580 case 't': /* TAB */
581 *dst++ = '\t';
582 src += 2;
583 break;
584 default:
585 src ++;
586 break;
588 } else {
589 /* usual */
590 *dst++ = *src++;
592 *dst = '\0';
594 assert (strlen(buf) < len);
595 return buf;
600 lookup_resolve( const char *str )
602 char *buf = strdup( str );
603 int ret;
605 downcase( buf );
606 if ( strcmp( buf, "both" ) == 0 )
607 ret = RESOLVE_BOTH;
608 else if ( strcmp( buf, "remote" ) == 0 )
609 ret = RESOLVE_REMOTE;
610 else if ( strcmp( buf, "local" ) == 0 )
611 ret = RESOLVE_LOCAL;
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;
620 else
621 ret = RESOLVE_UNKNOWN;
622 free(buf);
623 return ret;
626 char *
627 getusername(void)
629 #ifdef _WIN32
630 static char buf[1024];
631 DWORD size = sizeof(buf);
632 buf[0] = '\0';
633 GetUserName( buf, &size);
634 return buf;
635 #else /* not _WIN32 */
636 struct passwd *pw = getpwuid(getuid());
637 if ( pw == NULL )
638 fatal("getpwuid() failed for uid: %d\n", getuid());
639 return pw->pw_name;
640 #endif /* not _WIN32 */
643 /* expect
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 */
654 str++, substr++;
656 return 1; /* good, matched */
660 /** PARAMETER operation **/
661 #define PARAMETER_FILE "/etc/connectrc"
662 #define PARAMETER_DOTFILE ".connectrc"
663 typedef struct {
664 char* name;
665 char* value;
666 } PARAMETER_ITEM;
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 },
689 { NULL, NULL }
692 PARAMETER_ITEM*
693 find_parameter_item(const char* name)
695 int i;
696 for( i = 0; parameter_table[i].name != NULL; i++ ){
697 if ( strcmp(name, parameter_table[i].name) == 0 )
698 return &parameter_table[i];
700 return NULL;
703 void
704 read_parameter_file_1(const char* name)
706 FILE* f;
707 int line;
708 char lbuf[1025];
709 f = fopen(name, "r");
710 if( f ){
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');
715 if ( p == NULL )
716 fatal("%s:%d: buffer overflow\n", name, line);
717 *p = '\0';
718 p = strchr(lbuf, '#');
719 if ( p )
720 *p = '\0';
721 for ( p = lbuf; *p; p++ )
722 if( *p != ' ' && *p != '\t' ) break;
723 if ( *p == '\0' ) continue;
724 param = p;
725 p = strchr(p, '=');
726 if ( p == NULL ) {
727 error("%s:%d: missing equal sign\n", name, line);
728 continue;
730 for ( q = p - 1; q >= lbuf; q-- )
731 if ( *q != ' ' && *q != '\t' ) break;
732 *++q = '\0';
733 for ( ++p; *p; p++ )
734 if ( *p != ' ' && *p != '\t' ) break;
735 value = p;
736 for ( ; *p; p++ );
737 for ( p--; p >= lbuf; p-- )
738 if ( *p != ' ' && *p != '\t' ) break;
739 *++p = '\0';
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);
745 continue;
747 item->value = strdup(value);
748 debug("Parameter `%s' is set to `%s'\n", param, value);
754 void
755 read_parameter_file(void)
757 #if !defined(_WIN32) || defined(cygwin)
758 char *name;
759 struct passwd *pw;
760 #endif
762 read_parameter_file_1(PARAMETER_FILE);
763 #if !defined(_WIN32) || defined(cygwin)
764 pw = getpwuid(getuid());
765 if ( pw == NULL )
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);
771 free(name);
772 #endif /* _WIN32 */
775 char*
776 getparam(const char* name)
778 char *value = getenv(name);
779 if ( value == NULL ){
780 PARAMETER_ITEM *item = find_parameter_item(name);
781 if ( item != NULL )
782 value = item->value;
784 return value;
788 /** DIRECT connection **/
789 #define MAX_DIRECT_ADDR_LIST 256
791 struct ADDRPAIR {
792 struct in_addr addr;
793 struct in_addr mask;
794 char *name;
795 int negative;
798 struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
799 int n_direct_addr_list = 0;
801 void
802 mask_addr (void *addr, void *mask, int addrlen)
804 char *a, *m;
805 a = addr;
806 m = mask;
807 while ( 0 < addrlen-- )
808 *a++ &= *m++;
812 add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
814 struct in_addr iaddr;
815 char *s;
816 if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
817 error("direct address table is full!\n");
818 return -1;
820 iaddr = *addr;
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));
825 free(s);
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++;
833 return 0;
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");
843 return -1;
845 if (*name == '*')
846 name++;
847 if (*name == '.')
848 name++;
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++;
853 return 0;
858 parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
860 /* NOTE: */
861 /* Assume already be splitted by separator
862 and formatted as folowing:
863 1) 12.34.56.789/255.255.255.0
864 2) 12.34.56.789/24
865 3) 12.34.56.
866 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
868 const char *ptr;
869 u_char *dsta, *dstm;
870 int i, n;
872 assert( str != NULL );
873 addr->s_addr = 0;
874 mask->s_addr = 0;
875 ptr = str;
876 dsta = (u_char*)&addr->s_addr;
877 dstm = (u_char*)&mask->s_addr;
878 for (i=0; i<4; i++ ) {
879 if ( *ptr == '\0' )
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 */
886 ptr++;
887 if ( *ptr == '.' )
888 ptr++;
889 else
890 break;
892 /* At this point, *ptr points '/' or EOS ('\0') */
893 if ( *ptr == '\0' )
894 return 0; /* complete as format #3 */
895 if ( *ptr != '/' )
896 return -1; /* format error */
897 /* Now parse mask for format #1 or #2 */
898 ptr++;
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: */
907 *dstm++ = atoi(ptr);
908 while ( isdigit(*ptr) ) /* skip digits */
909 ptr++;
910 if ( *ptr == '.' )
911 ptr++;
912 else
913 break; /* from for loop */
915 /* complete as format #1 */
916 } else {
917 /* case of format #2 */
918 if ( !isdigit(*ptr) )
919 return -1; /* format error: */
920 n = atoi(ptr);
921 if ( n<0 || 32<n)
922 return -1; /* format error */
923 mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
924 /* complete as format #1 */
926 return 0;
929 void
930 initialize_direct_addr (void)
932 int negative;
933 int n_entries;
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;
940 else
941 envkey = ENV_SOCKS4_DIRECT;
942 env = getparam(envkey);
943 if ( env == NULL )
944 env = getparam(ENV_SOCKS_DIRECT);
945 } else if ( relay_method == METHOD_HTTP ){
946 env = getparam(ENV_HTTP_DIRECT);
949 if ( env == NULL )
950 env = getparam(ENV_CONNECT_DIRECT);
952 if ( env == NULL )
953 return; /* no entry */
954 debug("making direct addr list from: '%s'\n", env);
955 env = strdup( env ); /* reallocate to modify */
956 beg = next = env;
957 n_entries = 0;
958 do {
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, ',');
964 if ( next != NULL )
965 *next++ = '\0';
966 addr.s_addr = 0;
967 mask.s_addr = 0;
968 if (*beg == '!') {
969 negative = 1;
970 beg++;
971 } else
972 negative = 0;
973 if ( !parse_addr_pair( beg, &addr, &mask ) ) {
974 add_direct_addr( &addr, &mask, negative );
975 } else {
976 add_direct_host( beg, negative );
978 if ( next != NULL )
979 beg = next;
980 } while ( next != NULL );
982 free( env );
983 return;
987 cmp_addr (void *addr1, void *addr2, int addrlen)
989 return memcmp( addr1, addr2, addrlen );
993 is_direct_address (const struct in_addr addr)
995 int i, neg;
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;
1003 iaddr = addr;
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) {
1008 char *a, *m;
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)": "");
1012 free(a);
1013 free(m);
1014 return !neg? 1: 0;
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.
1025 For example,
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)
1033 int len1, len2;
1034 const char *tail1, *tail2;
1035 len1 = strlen(s1);
1036 len2 = strlen(s2);
1037 if (len1 < len2 || len1 == 0 || len2 == 0)
1038 return 0; /* not match */
1039 tail1 = s1 + len1;
1040 tail2 = s2 + len2;
1041 while (0 < len1 && 0 < len2) {
1042 if (*--tail1 != *--tail2)
1043 break; /* not match */
1044 len1--, len2--;
1046 if (len2 != 0)
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)
1063 int len, i;
1064 const char *tail;
1065 debug("checking %s is for direct?\n", name);
1066 name = downcase(strdup(name));
1067 len = strlen(name);
1068 if (len < 1)
1069 return 0; /* false */
1070 tail = &name[len];
1071 for (i=0; i<n_direct_addr_list; i++ ) {
1072 int dlen, neg;
1073 const char *dname;
1074 const char *n, *d;
1075 dname = direct_addr_list[i].name;
1076 if (dname == NULL)
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)": "");
1081 if (neg) {
1082 return 0; /* not direct */
1083 } else {
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 */
1104 } else {
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 **/
1118 int intr_flag = 0;
1120 #ifndef _WIN32
1121 void
1122 intr_handler(int sig)
1124 intr_flag = 1;
1127 void
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 ) {
1136 /* enable echo */
1137 tcsetattr(fd, TCSANOW, &otio);
1138 disabled = 0;
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);
1145 sigemptyset(&nset);
1146 sigsuspend(&nset);
1147 intr_flag = 0;
1149 } else if (!disabled && !enable) {
1150 /* set SIGINTR handler and break syscall on singal */
1151 sigemptyset(&nset);
1152 sigaddset(&nset, SIGTSTP);
1153 sigprocmask(SIG_BLOCK, &nset, &oset);
1154 intr_flag = 0;
1155 memset(&nsa, 0, sizeof(nsa));
1156 nsa.sa_handler = intr_handler;
1157 sigaction(SIGINT, &nsa, &osa);
1158 /* disable echo */
1159 if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
1160 disabled = 1;
1161 ntio = otio;
1162 ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
1163 (void) tcsetattr(fd, TCSANOW, &ntio);
1167 return;
1170 #define TTY_NAME "/dev/tty"
1172 tty_readpass( const char *prompt, char *buf, size_t size )
1174 int tty, ret = 0;
1176 tty = open(TTY_NAME, O_RDWR);
1177 if ( tty < 0 ) {
1178 error("Unable to open %s\n", TTY_NAME);
1179 return -1; /* can't open tty */
1181 if ( size <= 0 )
1182 return -1; /* no room */
1183 write(tty, prompt, strlen(prompt));
1184 buf[0] = '\0';
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 */
1189 close(tty);
1190 if ( strchr(buf,'\n') == NULL )
1191 return -1;
1192 if ( 0 < ret )
1193 buf[ret] = '\0';
1194 return ret;
1197 #else /* _WIN32 */
1199 BOOL __stdcall
1200 w32_intr_handler(DWORD dwCtrlType)
1202 if ( dwCtrlType == CTRL_C_EVENT ) {
1203 intr_flag = 1;
1204 return TRUE;
1205 } else {
1206 return FALSE;
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);
1218 DWORD mode;
1219 DWORD ret, bytes;
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 */
1231 if ( intr_flag )
1232 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
1233 WriteFile(out,"\n", 1, &bytes, 0);
1234 CloseHandle(in);
1235 CloseHandle(out);
1236 return ret;
1239 #endif /* _WIN32 */
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
1246 following rules:
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 --+-------------+-------------+ |
1259 2 | SOCKS_USER | |
1260 --+---------------------------+-----------------+
1261 3 | CONNECT_USER |
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.
1283 char *
1284 determine_relay_user ()
1286 char *user = NULL;
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);
1293 if (user == NULL)
1294 user = getparam (ENV_SOCKS_USER);
1295 } else if (relay_method == METHOD_HTTP) {
1296 if (user == NULL)
1297 user = getparam (ENV_HTTP_PROXY_USER);
1299 if (user == NULL)
1300 user = getparam (ENV_CONNECT_USER);
1301 /* determine relay user by system call if not yet. */
1302 if (user == NULL)
1303 user = getusername();
1304 return user;
1307 char *
1308 determine_relay_password ()
1310 char *pass = NULL;
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);
1317 if (pass == NULL)
1318 pass = getparam(ENV_CONNECT_PASSWORD);
1319 return pass;
1323 /*** network operations ***/
1326 /* set_relay()
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");
1348 } else {
1349 debug ("%d direct address entries.\n", n_direct_addr_list);
1352 switch ( method ) {
1353 case METHOD_DIRECT:
1354 return -1; /* nothing to do */
1356 case METHOD_SOCKS:
1357 if ( spec == NULL ) {
1358 switch ( socks_version ) {
1359 case 5:
1360 spec = getparam(ENV_SOCKS5_SERVER);
1361 break;
1362 case 4:
1363 spec = getparam(ENV_SOCKS4_SERVER);
1364 break;
1367 if ( spec == NULL )
1368 spec = getparam(ENV_SOCKS_SERVER);
1370 if ( spec == NULL )
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);
1384 } else {
1385 /* default */
1386 if ( socks_version == 5 )
1387 socks_resolve = RESOLVE_REMOTE;
1388 else
1389 socks_resolve = RESOLVE_LOCAL;
1392 break;
1394 case METHOD_HTTP:
1395 if ( spec == NULL )
1396 spec = getparam(ENV_HTTP_PROXY);
1397 if ( spec == NULL )
1398 fatal("You must specify http proxy server\n");
1399 relay_port = 80; /* set default first */
1400 break;
1401 case METHOD_TELNET:
1402 if ( spec == NULL )
1403 spec = getparam(ENV_TELNET_PROXY);
1404 if ( spec == NULL )
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';
1414 } else {
1415 /* assume spec is aready "server:port" format */
1416 buf = strdup( spec );
1418 spec = buf;
1420 /* check username in spec */
1421 sep = strchr( spec, '@' );
1422 if ( sep != NULL ) {
1423 *sep = '\0';
1424 relay_user = strdup( spec );
1425 spec = sep +1;
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 );
1435 } else {
1436 /* hostname and port */
1437 relay_port = atoi(sep+1);
1438 *sep = '\0';
1439 relay_host = strdup( spec );
1441 free(buf);
1442 return 0;
1446 u_short
1447 resolve_port( const char *service )
1449 int port;
1450 if ( service[strspn (service, digits)] == '\0' ) {
1451 /* all digits, port number */
1452 port = atoi(service);
1453 } else {
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);
1459 port = 0;
1460 } else {
1461 port = ntohs(ent->s_port);
1462 debug("service: %s => %d\n", service, port);
1465 return (u_short)port;
1468 void
1469 make_revstr(void)
1471 char *ptr;
1472 size_t len;
1473 ptr = strstr(rcs_revstr, ": ");
1474 if (!ptr) {
1475 revstr = strdup("unknown");
1476 return;
1478 ptr += 2;
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 )
1488 int err = 0;
1489 char *ptr, *server = (char*)NULL;
1490 int method = METHOD_DIRECT;
1492 progname = *argv;
1493 argc--, argv++;
1495 /* check optinos */
1496 while ( (0 < argc) && (**argv == '-') ) {
1497 ptr = *argv + 1;
1498 while ( *ptr ) {
1499 switch ( *ptr ) {
1500 case 's': /* use SOCKS */
1501 method = METHOD_SOCKS;
1502 break;
1504 case 'n': /* no proxy */
1505 method = METHOD_DIRECT;
1506 break;
1508 case 'h': /* use http-proxy */
1509 method = METHOD_HTTP;
1510 break;
1511 case 't':
1512 method = METHOD_TELNET;
1513 break;
1515 case 'S': /* specify SOCKS server */
1516 if ( 1 < argc ) {
1517 argv++, argc--;
1518 method = METHOD_SOCKS;
1519 server = *argv;
1520 } else {
1521 error("option '-%c' needs argument.\n", *ptr);
1522 err++;
1524 break;
1526 case 'H': /* specify http-proxy server */
1527 if ( 1 < argc ) {
1528 argv++, argc--;
1529 method = METHOD_HTTP;
1530 server = *argv;
1531 } else {
1532 error("option '-%c' needs argument.\n", *ptr);
1533 err++;
1535 break;
1536 case 'T': /* specify telnet proxy server */
1537 if ( 1 < argc ) {
1538 argv++, argc--;
1539 method = METHOD_TELNET;
1540 server = *argv;
1541 } else {
1542 error("option '-%c' needs argument.\n", *ptr);
1543 err++;
1545 break;
1547 case 'c':
1548 if (1 < argc) {
1549 argv++, argc--;
1550 telnet_command = *argv;
1551 } else {
1552 error("option '%c' needs argument.\n", *ptr);
1553 err++;
1555 break;
1557 case 'P':
1558 f_hold_session = 1;
1559 /* without break */
1560 case 'p': /* specify port to forward */
1561 if ( 1 < argc ) {
1562 argv++, argc--;
1563 local_type = LOCAL_SOCKET;
1564 local_port = resolve_port(*argv);
1565 } else {
1566 error("option '-%c' needs argument.\n", *ptr);
1567 err++;
1569 break;
1571 #ifndef _WIN32
1572 case 'w':
1573 if ( 1 < argc ) {
1574 argv++, argc--;
1575 connect_timeout = atoi(*argv);
1576 } else {
1577 error("option '-%c' needs argument.\n", *ptr);
1578 err++;
1580 break;
1581 #endif /* not _WIN32 */
1583 case '4':
1584 socks_version = 4;
1585 break;
1587 case '5':
1588 socks_version = 5;
1589 break;
1591 case 'a':
1592 if ( 1 < argc ) {
1593 argv++, argc--;
1594 socks5_auth = *argv;
1595 } else {
1596 error("option '-%c' needs argument.\n", *ptr);
1597 err++;
1599 break;
1601 case 'R': /* specify resolve method */
1602 if ( 1 < argc ) {
1603 argv++, argc--;
1604 socks_resolve = lookup_resolve( *argv );
1605 } else {
1606 error("option '-%c' needs argument.\n", *ptr);
1607 err++;
1609 break;
1611 case 'V': /* print version */
1612 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1613 exit(0);
1615 case 'd': /* debug mode */
1616 f_debug++;
1617 break;
1619 default:
1620 error("unknown option '-%c'\n", *ptr);
1621 err++;
1623 ptr++;
1625 argc--, argv++;
1628 /* check error */
1629 if ( 0 < err )
1630 goto quit;
1632 set_relay( method, server );
1634 /* check destination HOST (MUST) */
1635 if ( argc == 0 ) {
1636 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1637 fprintf(stderr, usage, progname);
1638 exit(0);
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) )
1644 ptr++;
1645 else
1646 ptr = progname;
1647 if ( dest_port == 0 ) {
1648 /* accept only if -P is not specified. */
1649 if ( 1 < argc ) {
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);
1658 free(str);
1661 /* check port number */
1662 if ( dest_port <= 0 ) {
1663 error( "You must specify the destination port correctly.\n");
1664 err++;
1665 goto quit;
1667 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
1668 error("Invalid relay port: %d\n", dest_port);
1669 err++;
1670 goto quit;
1673 quit:
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);
1690 if (f_hold_session)
1691 debug (" with holding remote session.\n");
1693 debug("dest_host=%s\n", dest_host);
1694 debug("dest_port=%d\n", dest_port);
1695 if ( 0 < err ) {
1696 fprintf(stderr, usage, progname);
1697 exit(1);
1699 return 0;
1702 #ifndef _WIN32
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 */
1707 void
1708 sig_timeout(void)
1710 signal( SIGALRM, SIG_IGN );
1711 alarm( 0 );
1712 error( "timed out\n" );
1713 exit(1);
1716 /* set timeout param = seconds, 0 clears */
1717 void
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 );
1724 alarm( 0 );
1725 } else {
1726 debug( "setting timeout: %d seconds\n", timeout );
1727 signal(SIGALRM, (void *)sig_timeout);
1728 alarm( timeout );
1731 #endif
1733 #if !defined(_WIN32) && !defined(__CYGWIN32__)
1734 void
1735 switch_ns (struct sockaddr_in *ns)
1737 res_init();
1738 memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
1739 _res.nscount = 1;
1740 debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
1742 #endif /* !_WIN32 && !__CYGWIN32__ */
1744 /* TODO: IPv6
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);
1756 } else {
1757 debug("resolving host by name: %s\n", host);
1758 ent = gethostbyname (host);
1759 if ( ent ) {
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));
1764 } else {
1765 debug("failed to resolve locally.\n");
1766 return -1; /* failed */
1769 return 0; /* good */
1773 open_connection( const char *host, u_short port )
1775 SOCKET s;
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))
1788 == SOCKET_ERROR) {
1789 debug( "connect() failed.\n");
1790 return SOCKET_ERROR;
1792 return s;
1795 void
1796 report_text( char *prefix, char *buf )
1798 static char work[1024];
1799 char *tmp;
1801 if ( !f_debug )
1802 return;
1803 if ( !f_report )
1804 return; /* don't report */
1805 debug("%s \"", prefix);
1806 while ( *buf ) {
1807 memset( work, 0, sizeof(work));
1808 tmp = work;
1809 while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) {
1810 switch ( *buf ) {
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;
1815 default:
1816 if ( isprint(*buf) ) {
1817 *tmp++ = *buf;
1818 } else {
1819 int consumed = tmp - work;
1820 snprintf( tmp, sizeof(work)-consumed,
1821 "\\x%02X", (unsigned char)*buf);
1822 tmp += strlen(tmp);
1825 buf++;
1826 *tmp = '\0';
1828 debug_("%s", work);
1831 debug_("\"\n");
1835 void
1836 report_bytes( char *prefix, char *buf, int len )
1838 if ( ! f_debug )
1839 return;
1840 debug( "%s", prefix );
1841 while ( 0 < len ) {
1842 fprintf( stderr, " %02x", *(unsigned char *)buf);
1843 buf++;
1844 len--;
1846 fprintf(stderr, "\n");
1847 return;
1851 atomic_out( SOCKET s, char *buf, int size )
1853 int ret, len;
1855 assert( buf != NULL );
1856 assert( 0<=size );
1857 /* do atomic out */
1858 ret = 0;
1859 while ( 0 < size ) {
1860 len = send( s, buf+ret, size, 0 );
1861 if ( len == -1 )
1862 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1863 ret += len;
1864 size -= len;
1866 if (!f_report) {
1867 debug("atomic_out() [some bytes]\n");
1868 debug(">>> xx xx xx xx ...\n");
1869 } else {
1870 debug("atomic_out() [%d bytes]\n", ret);
1871 report_bytes(">>>", buf, ret);
1873 return ret;
1877 atomic_in( SOCKET s, char *buf, int size )
1879 int ret, len;
1881 assert( buf != NULL );
1882 assert( 0<=size );
1884 /* do atomic in */
1885 ret = 0;
1886 while ( 0 < size ) {
1887 len = recv( s, buf+ret, size, 0 );
1888 if ( len == -1 ) {
1889 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1890 } else if ( len == 0 ) {
1891 fatal( "Connection closed by peer.\n");
1893 ret += len;
1894 size -= len;
1896 if (!f_report) {
1897 debug("atomic_in() [some bytes]\n");
1898 debug("<<< xx xx xx xx ...\n");
1899 } else {
1900 debug("atomic_in() [%d bytes]\n", ret);
1901 report_bytes("<<<", buf, ret);
1903 return ret;
1907 line_input( SOCKET s, char *buf, int size )
1909 char *dst = buf;
1910 if ( size == 0 )
1911 return 0; /* no error */
1912 size--;
1913 while ( 0 < size ) {
1914 switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */
1915 case SOCKET_ERROR:
1916 error("recv() error\n");
1917 return -1; /* error */
1918 case 0:
1919 size = 0; /* end of stream */
1920 break;
1921 default:
1922 /* continue reading until last 1 char is EOL? */
1923 if ( *dst == '\n' ) {
1924 /* finished */
1925 size = 0;
1926 } else {
1927 /* more... */
1928 size--;
1930 dst++;
1933 *dst = '\0';
1934 report_text( "<<<", buf);
1935 return 0;
1938 /* cut_token()
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.
1944 char *
1945 cut_token( char *str, char *delim)
1947 char *ptr = str + strcspn(str, delim);
1948 char *end = ptr + strspn(ptr, delim);
1949 if ( ptr == str )
1950 return NULL;
1951 while ( ptr < end )
1952 *ptr++ = '\0';
1953 return ptr;
1956 const char *
1957 lookup(int num, LOOKUP_ITEM *items)
1959 int i = 0;
1960 while (0 <= items[i].num) {
1961 if (items[i].num == num)
1962 return items[i].str;
1963 i++;
1965 return "(unknown)";
1968 /* readpass()
1969 password input routine
1970 Use ssh-askpass (same mechanism to OpenSSH)
1972 char *
1973 readpass( const char* prompt, ...)
1975 static char buf[1000]; /* XXX, don't be fix length */
1976 va_list args;
1977 va_start(args, prompt);
1978 vsnprintf(buf, sizeof(buf), prompt, args);
1979 va_end(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 */
1987 FILE *fp;
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");
1993 free(cmd);
1994 if ( fp == NULL )
1995 return NULL; /* fail */
1996 buf[0] = '\0';
1997 if (fgets(buf, sizeof(buf), fp) == NULL)
1998 return NULL; /* fail */
1999 fclose(fp);
2000 } else {
2001 tty_readpass( buf, buf, sizeof(buf));
2003 buf[strcspn(buf, "\r\n")] = '\0';
2004 return buf;
2007 static int
2008 socks5_do_auth_userpass( int s )
2010 unsigned char buf[1024], *ptr;
2011 char *pass = NULL;
2012 int len;
2014 /* do User/Password authentication. */
2015 /* This feature requires username and password from
2016 command line argument or environment variable,
2017 or terminal. */
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 */
2028 ptr = buf;
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 );
2033 ptr += len;
2034 len = strlen( pass ); /* PLEN and PASSWD */
2035 PUT_BYTE( ptr++, strlen(pass));
2036 strcpy( ptr, pass );
2037 ptr += len;
2038 memset (pass, 0, strlen(pass)); /* erase password */
2040 /* send it and get answer */
2041 f_report = 0;
2042 atomic_out( s, buf, ptr-buf );
2043 f_report = 1;
2044 atomic_in( s, buf, 2 );
2046 /* check status */
2047 if ( buf[1] == 0 )
2048 return 0; /* success */
2049 else
2050 return -1; /* fail */
2053 static const char *
2054 socks5_getauthname( int auth )
2056 switch ( 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)";
2068 typedef struct {
2069 char* name;
2070 unsigned char auth;
2071 } AUTH_METHOD_ITEM;
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 },
2078 { NULL, -1 },
2082 socks5_auth_parse_1(char *start, char *end){
2083 int i, len;
2084 for ( ; *start; start++ )
2085 if ( *start != ' ' && *start != '\t') break;
2086 for ( end--; end >= start; end-- ) {
2087 if ( *end != ' ' && *end != '\t'){
2088 end++;
2089 break;
2092 len = end - start;
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);
2099 return -1;
2103 socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
2104 char *end;
2105 int i = 0;
2106 while ( i < max_auth ) {
2107 end = strchr(start, ',');
2108 if (*start && end) {
2109 auth_list[i++] = socks5_auth_parse_1(start, end);
2110 start = ++end;
2111 } else {
2112 break;
2115 if ( *start && ( i < max_auth ) ){
2116 for( end = start; *end; end++ );
2117 auth_list[i++] = socks5_auth_parse_1(start, end);
2118 } else {
2119 fatal("Too much auth method.\n");
2121 return i;
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 */
2137 ptr = buf;
2138 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2140 if ( env == NULL )
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;
2147 } else {
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");
2161 return -1;
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:
2173 /* nothing to do */
2174 auth_result = 0;
2175 break;
2177 case SOCKS5_AUTH_USERPASS:
2178 auth_result = socks5_do_auth_userpass(s);
2179 break;
2181 default:
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");
2188 return -1;
2190 /* request to connect */
2191 ptr = buf;
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) */
2201 ptr += len;
2202 } else {
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));
2215 return -1;
2217 ptr = buf + 4;
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 */
2221 break;
2222 case 3: /* DOMAINNAME */
2223 atomic_in( s, ptr, 1 ); /* recv name and port */
2224 atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
2225 break;
2226 case 4: /* IP v6 ADDR */
2227 atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */
2228 break;
2231 /* Conguraturation, connected via SOCKS5 server! */
2232 return 0;
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
2241 local machine.
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
2249 then remote.
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
2259 (in this order).
2263 begin_socks4_relay( SOCKET s )
2265 unsigned char buf[256], *ptr;
2267 debug( "begin_socks_relay()\n");
2269 /* make connect request packet
2270 protocol v4:
2271 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
2272 protocol v4a:
2273 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
2275 ptr = buf;
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 */
2285 /* username */
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! */
2306 return 0;
2310 sendf(SOCKET s, const char *fmt,...)
2312 static char buf[10240]; /* xxx, enough? */
2314 va_list args;
2315 va_start( args, fmt );
2316 vsnprintf( buf, sizeof(buf), fmt, args );
2317 va_end( 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());
2322 return -1;
2324 return 0;
2327 const char *base64_table =
2328 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2330 char *
2331 make_base64_string(const char *str)
2333 static char *buf;
2334 unsigned char *src;
2335 char *dst;
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);
2341 bits = data = 0;
2342 src = (unsigned char *)str;
2343 dst = (unsigned char *)buf;
2344 while ( dst_len-- ) {
2345 if ( bits < 6 ) {
2346 data = (data << 8) | *src;
2347 bits += 8;
2348 if ( *src != 0 )
2349 src++;
2351 *dst++ = base64_table[0x3F & (data >> (bits-6))];
2352 bits -= 6;
2354 *dst = '\0';
2355 /* fix-up tail padding */
2356 switch ( src_len%3 ) {
2357 case 1:
2358 *--dst = '=';
2359 case 2:
2360 *--dst = '=';
2362 return buf;
2367 basic_auth (SOCKET s)
2369 char *userpass;
2370 char *cred;
2371 const char *user = relay_user;
2372 char *pass = NULL;
2373 int len, ret;
2375 /* Get username/password for authentication */
2376 if (user == NULL)
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);
2392 f_report = 1;
2393 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
2395 memset(cred, 0, strlen(cred));
2396 free(cred);
2398 return ret;
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
2404 proxy server.
2407 begin_http_relay( SOCKET s )
2409 char buf[1024];
2410 int result;
2411 char *auth_what;
2413 debug("begin_http_relay()\n");
2415 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
2416 return START_ERROR;
2417 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
2418 return START_ERROR;
2419 if (sendf(s,"\r\n") < 0)
2420 return START_ERROR;
2422 /* get response */
2423 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
2424 debug("failed to read http response.\n");
2425 return START_ERROR;
2428 /* check status */
2429 if (!strchr(buf, ' ')) {
2430 error ("Unexpected http response: '%s'.\n", buf);
2431 return START_ERROR;
2433 result = atoi(strchr(buf,' '));
2435 switch ( result ) {
2436 case 200:
2437 /* Conguraturation, connected via http proxy server! */
2438 debug("connected, start user session.\n");
2439 break;
2440 case 302: /* redirect */
2441 do {
2442 if (line_input(s, buf, sizeof(buf)))
2443 break;
2444 downcase(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);
2451 return START_RETRY;
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");
2464 return START_ERROR;
2466 auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2467 do {
2468 if ( line_input(s, buf, sizeof(buf)) ) {
2469 break;
2471 downcase(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;
2484 } else {
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);
2491 return START_ERROR;
2492 } else {
2493 return START_RETRY;
2496 default:
2497 /* Not allowed */
2498 debug("http proxy is not allowed.\n");
2499 return START_ERROR;
2501 /* skip to end of response header */
2502 do {
2503 if ( line_input(s, buf, sizeof(buf) ) ) {
2504 debug("Can't skip response headers\n");
2505 return START_ERROR;
2507 } while ( strcmp(buf,"\r\n") != 0 );
2509 return START_OK;
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 )
2518 char buf[1024];
2519 char *cmd;
2520 char *good_phrase = "connected to";
2521 char *bad_phrase_list[] = {
2522 " failed", " refused", " rejected", " closed"
2524 char sep = ' ';
2525 int i;
2527 debug("begin_telnet_relay()\n");
2529 /* report phrase */
2530 debug("good phrase: '%s'\n", good_phrase);
2531 debug("bad phrases");
2532 sep = ':';
2533 for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
2534 debug_("%c '%s'", sep, bad_phrase_list[i]);
2535 sep = ',';
2537 debug_("\n");
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) {
2545 free(cmd);
2546 return START_ERROR;
2548 free(cmd);
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') {
2556 downcase(buf);
2557 /* first, check good phrase */
2558 if (strstr(buf, good_phrase)) {
2559 debug("good phrase is detected: '%s'\n", good_phrase);
2560 return START_OK;
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]);
2566 return START_ERROR;
2570 debug("error reading from telnet proxy\n");
2572 return START_ERROR;
2576 #ifdef _WIN32
2577 /* ddatalen()
2578 Returns 1 if data is available, otherwise return 0
2581 stdindatalen (void)
2583 DWORD len = 0;
2584 struct stat st;
2585 fstat( 0, &st );
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 */
2593 len = 1;
2594 } else {
2595 fatal("PeekNamedPipe() failed, errno=%d\n",
2596 GetLastError());
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 */
2604 len = 1;
2606 return len;
2608 #endif /* _WIN32 */
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 **/
2625 int nfds, len;
2626 fd_set ifds, ofds;
2627 struct timeval *tmo;
2628 #ifdef _WIN32
2629 struct timeval win32_tmo;
2630 #endif /* _WIN32 */
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 */
2636 lbuf_len = 0;
2637 rbuf_len = 0;
2639 while ( f_local || f_remote ) {
2640 FD_ZERO(&ifds );
2641 FD_ZERO(&ofds );
2642 tmo = NULL;
2644 /** prepare for reading local input **/
2645 if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
2646 #ifdef _WIN32
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 */
2653 tmo = &win32_tmo;
2654 } else
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 ) {
2668 /* some error */
2669 error( "select() failed, %d\n", socket_errno());
2670 return REASON_ERROR;
2672 #ifdef _WIN32
2673 /* fake ifds if local is stdio handle because
2674 select() of Winsock does not accept stdio
2675 handle. */
2676 if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
2677 FD_SET(0,&ifds); /* data ready */
2678 #endif
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 */
2688 f_local = 0;
2689 } else if ( len == -1 ) {
2690 /* error */
2691 fatal("recv() failed, %d\n", socket_errno());
2692 } else {
2693 debug("recv %d bytes\n", len);
2694 if ( 1 < f_debug ) /* more verbose */
2695 report_bytes( "<<<", rbuf+rbuf_len, len);
2696 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);
2705 else
2706 len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
2707 if ( len == 0 ) {
2708 /* stdin is EOF */
2709 debug("local input is EOF\n");
2710 if (!f_hold_session)
2711 shutdown(remote, 1); /* no-more writing */
2712 f_local = 0;
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");
2718 f_local = 0;
2719 close_reason = REASON_CLOSED_BY_LOCAL;
2720 } else
2721 fatal("recv() failed, errno = %d\n", errno);
2722 } else {
2723 /* repeat */
2724 lbuf_len += len;
2728 /* flush data in buffer to socket */
2729 if ( 0 < lbuf_len ) {
2730 len = send(remote, lbuf, lbuf_len, 0);
2731 if ( len == -1 ) {
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);
2738 lbuf_len -= len;
2739 if ( 0 < lbuf_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);
2749 else
2750 len = write( local_out, rbuf, rbuf_len);
2751 if ( len == -1 ) {
2752 fatal("output (local) failed, errno=%d\n", errno);
2754 rbuf_len -= len;
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");
2761 f_remote = 0;
2762 shutdown (local_out, 2);
2763 close (local_out);
2764 break;
2768 return close_reason;
2772 accept_connection (u_short port)
2774 static int sock = -1;
2775 int connection;
2776 struct sockaddr_in name;
2777 struct sockaddr client;
2778 int socklen;
2779 fd_set ifds;
2780 int nfds;
2781 int sockopt;
2783 /* Create the socket. */
2784 debug("Creating source port to forward.\n");
2785 sock = socket (PF_INET, SOCK_STREAM, 0);
2786 if (sock < 0)
2787 fatal("socket() failed, errno=%d\n", socket_errno());
2788 sockopt = 1;
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);
2804 nfds = sock + 1;
2805 do {
2806 int n;
2807 struct timeval *ptmo = NULL;
2808 #ifdef _WIN32
2809 struct timeval tmo;
2810 tmo.tv_sec = 0;
2811 tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */
2812 ptmo = &tmo;
2813 #endif /* _WIN32 */
2814 FD_ZERO (&ifds);
2815 FD_SET ((SOCKET)sock, &ifds);
2816 #ifndef _WIN32
2817 FD_SET (0, &ifds); /* watch stdin */
2818 #endif
2819 n = select (nfds, &ifds, NULL, NULL, ptmo);
2820 if (n == -1) {
2821 fatal ("select() failed, %d\n", socket_errno());
2822 exit (1);
2824 #ifdef _WIN32
2825 if (0 < stdindatalen()) {
2826 FD_SET (0, &ifds); /* fake */
2827 n++;
2829 #endif
2830 if (0 < n) {
2831 if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
2832 /* EOF */
2833 debug ("Give-up waiting port because stdin is closed.");
2834 exit(0);
2836 if (FD_ISSET(sock, &ifds))
2837 break; /* socket is stimulated */
2839 } while (1);
2840 socklen = sizeof(client);
2841 connection = accept( sock, &client, &socklen);
2842 if ( connection < 0 )
2843 fatal ("accept() failed, errno=%d\n", socket_errno());
2844 return connection;
2849 /** Main of program **/
2851 main( int argc, char **argv )
2853 int ret;
2854 int remote; /* socket */
2855 int local_in; /* Local input */
2856 int local_out; /* Local output */
2857 int reason;
2858 #ifdef _WIN32
2859 WSADATA wsadata;
2860 WSAStartup( 0x101, &wsadata);
2861 #endif /* _WIN32 */
2863 /* initialization */
2864 make_revstr();
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 );
2872 } else {
2873 /* Relay between stdin/stdout and desteination */
2874 local_in = 0;
2875 local_out = 1;
2876 #ifdef _WIN32
2877 _setmode(local_in, O_BINARY);
2878 _setmode(local_out, O_BINARY);
2879 #endif
2882 retry:
2883 #ifndef _WIN32
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",
2895 socket_errno());
2896 } else {
2897 remote = open_connection (relay_host, relay_port);
2898 if ( remote == SOCKET_ERROR )
2899 fatal( "Unable to connect to relay host, errno=%d\n",
2900 socket_errno());
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 ) {
2916 case METHOD_SOCKS:
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");
2920 break;
2922 case METHOD_HTTP:
2923 ret = begin_http_relay(remote);
2924 switch (ret) {
2925 case START_ERROR:
2926 close (remote);
2927 fatal("failed to begin relaying via HTTP.\n");
2928 case START_OK:
2929 break;
2930 case START_RETRY:
2931 /* retry with authentication */
2932 close (remote);
2933 goto retry;
2935 break;
2936 case METHOD_TELNET:
2937 if (begin_telnet_relay(remote) < 0)
2938 fatal("failed to begin relaying via telnet.\n");
2939 break;
2941 debug("connected\n");
2943 #ifndef _WIN32
2944 if (0 < connect_timeout)
2945 set_timeout (0);
2946 #endif /* not _WIN32 */
2948 /* main loop */
2949 debug ("start relaying.\n");
2950 do_repeater:
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 &&
2955 f_hold_session) {
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");
2960 goto do_repeater;
2962 closesocket(remote);
2963 if ( local_type == LOCAL_SOCKET)
2964 closesocket(local_in);
2965 #ifdef _WIN32
2966 WSACleanup();
2967 #endif /* _WIN32 */
2968 debug ("that's all, bye.\n");
2970 return 0;
2973 /* ------------------------------------------------------------
2974 Local Variables:
2975 compile-command: "cc connect.c -o connect"
2976 tab-width: 8
2977 fill-column: 74
2978 comment-column: 48
2979 End:
2980 ------------------------------------------------------------ */
2982 /*** end of connect.c ***/