]> git.pld-linux.org Git - packages/openssh.git/blame - connect.c
- support for modular xorg
[packages/openssh.git] / connect.c
CommitLineData
f273ee37 1/***********************************************************************
2 * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
3 *
c3adcdf8 4 * Copyright (c) 2000-2004 Shun-ichi Goto
f273ee37 5 * Copyright (c) 2002, J. Grant (English Corrections)
6 *
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.
11 *
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.
16 *
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.
20 *
21 * ---------------------------------------------------------
22 * PROJECT: My Test Program
23 * AUTHOR: Shun-ichi GOTO <gotoh@taiyo.co.jp>
24 * CREATE: Wed Jun 21, 2000
25 * REVISION: $Revision$
26 * ---------------------------------------------------------
27 *
28 * Getting Source
29 * ==============
30 *
31 * Recent version of 'connect.c' is available from
c3adcdf8 32 * http://www.taiyo.co.jp/~gotoh/ssh/connect.c
f273ee37 33 *
34 * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
35 * is available:
c3adcdf8 36 * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz
37 *
38 * See more detail:
39 * http://www.taiyo.co.jp/~gotoh/ssh/connect.html
f273ee37 40 *
41 * How To Compile
42 * ==============
43 *
44 * On UNIX environment:
45 * $ gcc connect.c -o connect
46 *
47 * On SOLARIS:
48 * $ gcc -o connect -lresolv -lsocket -lnsl connect.c
49 *
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
56 *
c3adcdf8 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
61 *
f273ee37 62 * How To Use
63 * ==========
64 *
65 * You can specify proxy method in an environment variable or in a
66 * command line option.
67 *
c3adcdf8 68 * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
f273ee37 69 * [-H [user@]proxy-server[:port]]
70 * [-S [user@]socks-server[:port]]
c3adcdf8 71 * [-T proxy-server[:port]]
72 * [-c telnet proxy command]
f273ee37 73 * host port
74 *
75 * "host" and "port" is for the target hostname and port-number to
76 * connect to.
77 *
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.
82 *
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.
87 *
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)
91 *
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.
99 *
c3adcdf8 100 * The '-p' option will forward a local TCP port instead of using the
f273ee37 101 * standard input and output.
102 *
c3adcdf8 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.
107 *
f273ee37 108 * The '-w' option specifys timeout seconds for making connection with
109 * TARGET host.
110 *
111 * The '-d' option is used for debug. If you fail to connect, use this
112 * and check request to and response from server.
113 *
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).
119 *
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
126 *
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 )
135 *
136 * TIPS
137 * ====
138 *
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:
148 *
149 * ProxyCommand connect -n %h %p
150 *
151 * SOCKS5 authentication
152 * =====================
153 *
154 * Only USER/PASS authentication is supported.
155 *
156 * Proxy authentication
157 * ====================
158 *
159 * Only BASIC scheme is supported.
160 *
161 * Authentication informations
162 * ===========================
163 *
164 * User name for authentication is specifed by an environment variable
c3adcdf8 165 * or system login name. And password is specified from environment
f273ee37 166 * variable or external program (specified in $SSH_ASKPASS) or tty.
167 *
168 * Following environment variable is used for specifying user name.
169 * SOCKS: $SOCKS5_USER, $LOGNAME, $USER
170 * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER
171 *
172 * ssh-askpass support
173 * ===================
c3adcdf8 174 *
f273ee37 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.
180 *
181 * Related Informations
182 * ====================
183 *
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/
191 *
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" ...
198 *
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.
202 *
203 ***********************************************************************/
204
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>
216
217#ifdef __CYGWIN32__
218#undef _WIN32
219#endif
220
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>
c3adcdf8 239#if !defined(_WIN32) && !defined(__CYGWIN32__)
240#define WITH_RESOLVER 1
f273ee37 241#include <arpa/nameser.h>
242#include <resolv.h>
c3adcdf8 243#else /* not ( not _WIN32 && not __CYGWIN32__) */
244#undef WITH_RESOLVER
245#endif /* not ( not _WIN32 && not __CYGWIN32__) */
f273ee37 246#endif /* !_WIN32 */
247
248#ifdef _WIN32
249#define ECONNRESET WSAECONNRESET
250#endif /* _WI32 */
251
252
253
254#ifndef LINT
255static char *vcid = "$Id$";
256#endif
257
258
259/* consider Borland C */
260#ifdef __BORLANDC__
261#define _kbhit kbhit
c3adcdf8 262#define _setmode setmode
f273ee37 263#endif
264
265/* help message.
c3adcdf8 266 Win32 environment does not support -R option (vc and cygwin)
267 Win32 native compilers does not support -w option, yet (vc)
f273ee37 268*/
c3adcdf8 269static char *usage = "usage: %s [-dnhst45] [-p local-port]"
f273ee37 270#ifdef _WIN32
f273ee37 271#ifdef __CYGWIN32__
c3adcdf8 272"[-w timeout] \n" /* cygwin cannot -R */
f273ee37 273#else /* not __CYGWIN32__ */
c3adcdf8 274" \n" /* VC cannot -w nor -R */
275#endif /* not __CYGWIN32__ */
276#else /* not _WIN32 */
277/* help message for UNIX */
278"[-R resolve] [-w timeout] \n"
f273ee37 279#endif /* not _WIN32 */
280" [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
c3adcdf8 281" [-T proxy-server[:port]]\n"
282" [-c telnet-proxy-command]\n"
f273ee37 283" host port\n";
284
285/* name of this program */
286char *progname = NULL;
287char *progdesc = "connect --- simple relaying command via proxy.";
288char *rcs_revstr = "$Revision$";
289char *revstr = NULL;
290
c3adcdf8 291/* set of character for strspn() */
292const char *digits = "0123456789";
293const char *dotdigits = "0123456789.";
294
f273ee37 295/* options */
296int f_debug = 0;
297
298/* report flag to hide secure information */
299int f_report = 1;
300
301int connect_timeout = 0;
302
303/* local input type */
c3adcdf8 304#define LOCAL_STDIO 0
305#define LOCAL_SOCKET 1
f273ee37 306char *local_type_names[] = { "stdio", "socket" };
307int local_type = LOCAL_STDIO;
c3adcdf8 308u_short local_port = 0; /* option 'p' */
309int f_hold_session = 0; /* option 'P' */
310
311char *telnet_command = "telnet %h %p";
f273ee37 312
313/* utiity types, pair holder of number and string */
314typedef struct {
315 int num;
316 const char *str;
317} LOOKUP_ITEM;
318
319/* relay method, server and port */
320#define METHOD_UNDECIDED 0
321#define METHOD_DIRECT 1
322#define METHOD_SOCKS 2
323#define METHOD_HTTP 3
c3adcdf8 324#define METHOD_TELNET 4
325char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
f273ee37 326
c3adcdf8 327int relay_method = METHOD_UNDECIDED; /* relaying method */
328char *relay_host = NULL; /* hostname of relay server */
329u_short relay_port = 0; /* port of relay server */
330char *relay_user = NULL; /* user name for auth */
f273ee37 331
332/* destination target host and port */
333char *dest_host = NULL;
334struct sockaddr_in dest_addr;
335u_short dest_port = 0;
336
337/* informations for SOCKS */
c3adcdf8 338#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
339#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
340#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
341#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
342#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
343#define SOCKS5_REP_REFUSED 0x05 /* connection refused */
344#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
345#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
346#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
347#define SOCKS5_REP_INVADDR 0x09 /* Inalid address */
f273ee37 348
349LOOKUP_ITEM socks5_rep_names[] = {
350 { SOCKS5_REP_SUCCEEDED, "succeeded"},
351 { SOCKS5_REP_FAIL, "general SOCKS server failure"},
352 { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"},
353 { SOCKS5_REP_NUNREACH, "Network unreachable"},
354 { SOCKS5_REP_HUNREACH, "Host unreachable"},
355 { SOCKS5_REP_REFUSED, "connection refused"},
356 { SOCKS5_REP_EXPIRED, "TTL expired"},
357 { SOCKS5_REP_CNOTSUP, "Command not supported"},
358 { SOCKS5_REP_ANOTSUP, "Address not supported"},
359 { SOCKS5_REP_INVADDR, "Invalid address"},
360 { -1, NULL }
361};
362
363/* SOCKS5 authentication methods */
c3adcdf8 364#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
365#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
366#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
367#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
368#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
369#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
370#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
371
372#define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */
373#define SOCKS4_REP_REJECTED 91 /* request rejected or failed */
374#define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */
375#define SOCKS4_REP_USERID 93 /* user id not matched */
f273ee37 376
377LOOKUP_ITEM socks4_rep_names[] = {
378 { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"},
379 { SOCKS4_REP_REJECTED, "request rejected or failed"},
380 { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
381 { SOCKS4_REP_USERID, "user id not matched"},
382 { -1, NULL }
383};
384
385#define RESOLVE_UNKNOWN 0
386#define RESOLVE_LOCAL 1
387#define RESOLVE_REMOTE 2
388#define RESOLVE_BOTH 3
389char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
390
c3adcdf8 391int socks_version = 5; /* SOCKS protocol version */
f273ee37 392int socks_resolve = RESOLVE_UNKNOWN;
393struct sockaddr_in socks_ns;
394char *socks5_auth = NULL;
395
396/* Environment variable names */
c3adcdf8 397#define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
f273ee37 398#define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
399#define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
400
c3adcdf8 401#define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
f273ee37 402#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
403#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
404
c3adcdf8 405#define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
406#define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */
407#define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */
408#define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */
409#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */
f273ee37 410
c3adcdf8 411#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
f273ee37 412#define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
413#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
414
c3adcdf8 415#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
f273ee37 416
c3adcdf8 417#define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */
418#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
419
420#define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
f273ee37 421#define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT"
422#define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT"
423#define ENV_HTTP_DIRECT "HTTP_DIRECT"
424#define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
425
426#define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
c3adcdf8 427#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
f273ee37 428
429/* Prefix string of HTTP_PROXY */
430#define HTTP_PROXY_PREFIX "http://"
431#define PROXY_AUTH_NONE 0
432#define PROXY_AUTH_BASIC 1
433#define PROXY_AUTH_DIGEST 2
434int proxy_auth_type = PROXY_AUTH_NONE;
435
c3adcdf8 436/* reason of end repeating */
437#define REASON_UNK -2
438#define REASON_ERROR -1
439#define REASON_CLOSED_BY_LOCAL 0
440#define REASON_CLOSED_BY_REMOTE 1
f273ee37 441
442/* return value of relay start function. */
443#define START_ERROR -1
444#define START_OK 0
445#define START_RETRY 1
446
447/* socket related definitions */
448#ifndef _WIN32
449#define SOCKET int
450#endif
451#ifndef SOCKET_ERROR
452#define SOCKET_ERROR -1
453#endif
454
f273ee37 455#ifdef _WIN32
456#define socket_errno() WSAGetLastError()
457#else /* !_WIN32 */
458#define closesocket close
459#define socket_errno() (errno)
460#endif /* !_WIN32 */
461
462#ifdef _WIN32
463#define popen _popen
464#endif /* WIN32 */
465
466/* packet operation macro */
467#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
468
469/* debug message output */
470void
471debug( const char *fmt, ... )
472{
473 va_list args;
474 if ( f_debug ) {
c3adcdf8 475 va_start( args, fmt );
476 fprintf(stderr, "DEBUG: ");
477 vfprintf( stderr, fmt, args );
478 va_end( args );
f273ee37 479 }
480}
481
482void
c3adcdf8 483debug_( const char *fmt, ... ) /* without prefix */
f273ee37 484{
485 va_list args;
486 if ( f_debug ) {
c3adcdf8 487 va_start( args, fmt );
488 vfprintf( stderr, fmt, args );
489 va_end( args );
f273ee37 490 }
491}
492
493/* error message output */
494void
495error( const char *fmt, ... )
496{
497 va_list args;
498 va_start( args, fmt );
499 fprintf(stderr, "ERROR: ");
500 vfprintf( stderr, fmt, args );
501 va_end( args );
502}
503
504void
505fatal( const char *fmt, ... )
506{
507 va_list args;
508 va_start( args, fmt );
509 fprintf(stderr, "FATAL: ");
510 vfprintf( stderr, fmt, args );
511 va_end( args );
512 exit (EXIT_FAILURE);
513}
514
c3adcdf8 515
516void *
517xmalloc (size_t size)
518{
519 void *ret = malloc(size);
520 if (ret == NULL)
521 fatal("Cannot allocate memory: %d bytes.\n", size);
522 return ret;
523}
524
f273ee37 525void
526downcase( char *buf )
527{
528 while ( *buf ) {
c3adcdf8 529 if ( isupper(*buf) )
530 *buf += 'a'-'A';
531 buf++;
f273ee37 532 }
533}
534
c3adcdf8 535char *
536expand_host_and_port (const char *fmt, const char *host, int port)
537{
538 const char *src;
539 char *buf, *dst, *ptr;
540 size_t len = strlen(fmt) + strlen(host) + 20;
541 buf = xmalloc (len);
542 dst = buf;
543 src = fmt;
544
545 while (*src) {
546 if (*src == '%') {
547 switch (src[1]) {
548 case 'h':
549 strcpy (dst, host);
550 src += 2;
551 break;
552 case 'p':
553 sprintf (dst, "%d", port);
554 src += 2;
555 break;
556 default:
557 src ++;
558 break;
559 }
560 dst = buf + strlen (buf);
561 } else if (*src == '\\') {
562 switch (src[1]) {
563 case 'r': /* CR */
564 *dst++ = '\r';
565 src += 2;
566 break;
567 case 'n': /* LF */
568 *dst++ = '\n';
569 src += 2;
570 break;
571 case 't': /* TAB */
572 *dst++ = '\t';
573 src += 2;
574 break;
575 default:
576 src ++;
577 break;
578 }
579 } else {
580 /* usual */
581 *dst++ = *src++;
582 }
583 *dst = '\0';
584 }
585 assert (strlen(buf) < len);
586 return buf;
587}
588
589
f273ee37 590int
591lookup_resolve( const char *str )
592{
593 char *buf = strdup( str );
594 int ret;
595
596 downcase( buf );
597 if ( strcmp( buf, "both" ) == 0 )
c3adcdf8 598 ret = RESOLVE_BOTH;
f273ee37 599 else if ( strcmp( buf, "remote" ) == 0 )
c3adcdf8 600 ret = RESOLVE_REMOTE;
f273ee37 601 else if ( strcmp( buf, "local" ) == 0 )
c3adcdf8 602 ret = RESOLVE_LOCAL;
603 else if ( strspn(buf, dotdigits) == strlen(buf) ) {
604#ifndef WITH_RESOLVER
605 fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
606#endif /* not WITH_RESOLVER */
607 ret = RESOLVE_LOCAL; /* this case is also 'local' */
608 socks_ns.sin_addr.s_addr = inet_addr(buf);
609 socks_ns.sin_family = AF_INET;
f273ee37 610 }
611 else
c3adcdf8 612 ret = RESOLVE_UNKNOWN;
f273ee37 613 free(buf);
614 return ret;
615}
616
617char *
618getusername(void)
619{
620#ifdef _WIN32
621 static char buf[1024];
622 DWORD size = sizeof(buf);
623 buf[0] = '\0';
624 GetUserName( buf, &size);
625 return buf;
626#else /* not _WIN32 */
627 struct passwd *pw = getpwuid(getuid());
628 if ( pw == NULL )
c3adcdf8 629 fatal("getpwuid() failed for uid: %d\n", getuid());
f273ee37 630 return pw->pw_name;
631#endif /* not _WIN32 */
632}
633
634/* expect
635 check STR is begin with substr with case-ignored comparison.
636 Return 1 if matched, otherwise 0.
637*/
638int
639expect( char *str, char *substr)
640{
641 int len = strlen(substr);
642 while ( 0 < len-- ) {
c3adcdf8 643 if ( toupper(*str) != toupper(*substr) )
644 return 0; /* not matched */
645 str++, substr++;
f273ee37 646 }
c3adcdf8 647 return 1; /* good, matched */
f273ee37 648}
649
650
651/** PARAMETER operation **/
652#define PARAMETER_FILE "/etc/connectrc"
653#define PARAMETER_DOTFILE ".connectrc"
654typedef struct {
655 char* name;
656 char* value;
657} PARAMETER_ITEM;
658PARAMETER_ITEM parameter_table[] = {
659 { ENV_SOCKS_SERVER, NULL },
660 { ENV_SOCKS5_SERVER, NULL },
661 { ENV_SOCKS4_SERVER, NULL },
662 { ENV_SOCKS_RESOLVE, NULL },
663 { ENV_SOCKS5_RESOLVE, NULL },
664 { ENV_SOCKS4_RESOLVE, NULL },
665 { ENV_SOCKS5_USER, NULL },
c3adcdf8 666 { ENV_SOCKS5_PASSWD, NULL },
f273ee37 667 { ENV_SOCKS5_PASSWORD, NULL },
668 { ENV_HTTP_PROXY, NULL },
669 { ENV_HTTP_PROXY_USER, NULL },
670 { ENV_HTTP_PROXY_PASSWORD, NULL },
671 { ENV_CONNECT_USER, NULL },
672 { ENV_CONNECT_PASSWORD, NULL },
673 { ENV_SSH_ASKPASS, NULL },
674 { ENV_SOCKS5_DIRECT, NULL },
675 { ENV_SOCKS4_DIRECT, NULL },
676 { ENV_SOCKS_DIRECT, NULL },
677 { ENV_HTTP_DIRECT, NULL },
678 { ENV_CONNECT_DIRECT, NULL },
679 { ENV_SOCKS5_AUTH, NULL },
680 { NULL, NULL }
681};
682
683PARAMETER_ITEM*
c3adcdf8 684find_parameter_item(const char* name)
685{
f273ee37 686 int i;
687 for( i = 0; parameter_table[i].name != NULL; i++ ){
c3adcdf8 688 if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
689 return &parameter_table[i];
f273ee37 690 }
691 return NULL;
692}
693
694void
c3adcdf8 695read_parameter_file_1(const char* name)
696{
f273ee37 697 FILE* f;
698 int line;
699 char lbuf[1025];
700 f = fopen(name, "r");
701 if( f ){
c3adcdf8 702 debug("Reading parameter file(%s)\n", name);
703 for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
704 char *p, *q, *param, *value;
705 p = strchr(lbuf, '\n');
706 if ( p == NULL )
707 fatal("%s:%d: buffer overflow\n", name, line);
708 *p = '\0';
709 p = strchr(lbuf, '#');
710 if ( p )
711 *p = '\0';
712 for ( p = lbuf; *p; p++ )
713 if( *p != ' ' && *p != '\t' ) break;
714 if ( *p == '\0' ) continue;
715 param = p;
716 p = strchr(p, '=');
717 if ( p == NULL ) {
718 error("%s:%d: missing equal sign\n", name, line);
719 continue;
720 }
721 for ( q = p - 1; q >= lbuf; q-- )
722 if ( *q != ' ' && *q != '\t' ) break;
723 *++q = '\0';
724 for ( ++p; *p; p++ )
725 if ( *p != ' ' && *p != '\t' ) break;
726 value = p;
727 for ( ; *p; p++ );
728 for ( p--; p >= lbuf; p-- )
729 if ( *p != ' ' && *p != '\t' ) break;
730 *++p = '\0';
731 if ( param && value ) {
732 PARAMETER_ITEM *item;
733 item = find_parameter_item(param);
734 if ( item == NULL ) {
735 error("%s:%d: unknown parameter `%s'\n", name, line, param);
736 continue;
737 }
738 item->value = strdup(value);
739 debug("Parameter `%s' is set to `%s'\n", param, value);
740 }
741 }
f273ee37 742 }
743}
744
745void
c3adcdf8 746read_parameter_file(void)
747{
f273ee37 748#ifndef _WIN32
749 char *name;
750 struct passwd *pw;
751#endif
752
753 read_parameter_file_1(PARAMETER_FILE);
754#ifndef _WIN32
755 pw = getpwuid(getuid());
756 if ( pw == NULL )
c3adcdf8 757 fatal("getpwuid() failed for uid: %d\n", getuid());
758 name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
f273ee37 759 strcpy(name, pw->pw_dir);
760 strcat(name, "/" PARAMETER_DOTFILE);
761 read_parameter_file_1(name);
762 free(name);
763#endif /* _WIN32 */
764}
765
766char*
c3adcdf8 767getparam(const char* name)
768{
f273ee37 769 char *value = getenv(name);
770 if ( value == NULL ){
c3adcdf8 771 PARAMETER_ITEM *item = find_parameter_item(name);
772 if ( item != NULL )
773 value = item->value;
f273ee37 774 }
775 return value;
776}
777
778
779/** DIRECT connection **/
780#define MAX_DIRECT_ADDR_LIST 256
781
782struct ADDRPAIR {
783 struct in_addr addr;
784 struct in_addr mask;
785 int negative;
786};
787
788struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
789int n_direct_addr_list = 0;
790
791void
792mask_addr (void *addr, void *mask, int addrlen)
793{
794 char *a, *m;
795 a = addr;
796 m = mask;
797 while ( 0 < addrlen-- )
c3adcdf8 798 *a++ &= *m++;
f273ee37 799}
800
801int
802add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
803{
804 struct in_addr iaddr;
805 char *s;
806 if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
c3adcdf8 807 error("direct address table is full!\n");
808 return -1;
f273ee37 809 }
810 iaddr = *addr;
811 mask_addr(&iaddr, mask, sizeof(iaddr));
812 s = strdup(inet_ntoa(iaddr));
813 debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
814 free(s);
815 memcpy( &direct_addr_list[n_direct_addr_list].addr,
c3adcdf8 816 &iaddr, sizeof(iaddr));
f273ee37 817 memcpy( &direct_addr_list[n_direct_addr_list].mask,
c3adcdf8 818 mask, sizeof(*mask));
f273ee37 819 direct_addr_list[n_direct_addr_list].negative = negative;
820 n_direct_addr_list++;
821 return 0;
822}
823
c3adcdf8 824int
f273ee37 825parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
826{
c3adcdf8 827 /* NOTE: */
828 /* Assume already be splitted by separator
829 and formatted as folowing:
830 1) 12.34.56.789/255.255.255.0
831 2) 12.34.56.789/24
832 3) 12.34.56.
f273ee37 833 All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
834 */
835 const char *ptr;
836 u_char *dsta, *dstm;
837 int i, n;
c3adcdf8 838
f273ee37 839 assert( str != NULL );
840 debug("parsing address pair: '%s'\n", str);
841 addr->s_addr = 0;
842 mask->s_addr = 0;
843 ptr = str;
844 dsta = (u_char*)&addr->s_addr;
845 dstm = (u_char*)&mask->s_addr;
846 for (i=0; i<4; i++ ) {
c3adcdf8 847 if ( *ptr == '\0' )
848 break; /* case of format #3 */
849 if ( !isdigit(*ptr) )
850 return -1; /* format error: */
851 *dsta++ = atoi( ptr );
852 *dstm++ = 255; /* automatic mask for format #3 */
853 while ( isdigit(*ptr) ) /* skip digits */
854 ptr++;
855 if ( *ptr == '.' )
856 ptr++;
857 else
858 break;
f273ee37 859 }
860 /* At this point, *ptr points '/' or EOS ('\0') */
861 if ( *ptr == '\0' )
c3adcdf8 862 return 0; /* complete as format #3 */
f273ee37 863 if ( *ptr != '/' )
c3adcdf8 864 return -1; /* format error */
f273ee37 865 /* Now parse mask for format #1 or #2 */
866 ptr++;
c3adcdf8 867 mask->s_addr = 0; /* clear automatic mask */
868
f273ee37 869 if ( strchr( ptr, '.') ) {
c3adcdf8 870 /* case of format #1 */
871 dstm = (u_char*)&mask->s_addr;
872 for (i=0; i<4; i++) {
873 if ( !isdigit(*ptr) )
874 return -1; /* format error: */
875 *dstm++ = atoi(ptr);
876 while ( isdigit(*ptr) ) /* skip digits */
877 ptr++;
878 if ( *ptr == '.' )
879 ptr++;
880 else
881 break; /* from for loop */
882 }
883 /* complete as format #1 */
f273ee37 884 } else {
c3adcdf8 885 /* case of format #2 */
886 if ( !isdigit(*ptr) )
887 return -1; /* format error: */
888 n = atoi(ptr);
889 if ( n<0 || 32<n)
890 return -1; /* format error */
891 mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
892 /* complete as format #1 */
f273ee37 893 }
894 return 0;
895}
896
897void
898initialize_direct_addr (void)
899{
900 int negative;
901 int n_entries;
902 char *env = NULL, *beg, *next, *envkey = NULL;
903 struct in_addr addr, mask;
904
905 if ( relay_method == METHOD_SOCKS ){
c3adcdf8 906 if ( socks_version == 5 )
907 envkey = ENV_SOCKS5_DIRECT;
908 else
909 envkey = ENV_SOCKS4_DIRECT;
910 env = getparam(envkey);
911 if ( env == NULL )
912 env = getparam(ENV_SOCKS_DIRECT);
f273ee37 913 } else if ( relay_method == METHOD_HTTP ){
c3adcdf8 914 env = getparam(ENV_HTTP_DIRECT);
f273ee37 915 }
916
917 if ( env == NULL )
c3adcdf8 918 env = getparam(ENV_CONNECT_DIRECT);
919
f273ee37 920 if ( env == NULL )
c3adcdf8 921 return; /* no entry */
922 debug("making direct addr list from: '%s'\n", env);
923 env = strdup( env ); /* reallocate to modify */
f273ee37 924 beg = next = env;
925 n_entries = 0;
926 do {
c3adcdf8 927 if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
928 error("too many entries in %s", envkey);
929 break; /* from do loop */
930 }
931 next = strchr( beg, ',');
932 if ( next != NULL )
933 *next++ = '\0';
934 addr.s_addr = 0;
935 mask.s_addr = 0;
936 if (*beg == '!') {
937 negative = 1;
938 beg++;
939 } else
940 negative = 0;
941 if ( !parse_addr_pair( beg, &addr, &mask ) ) {
942 add_direct_addr( &addr, &mask, negative );
943 } else {
944 error("invalid addr format in %s: %s\n", envkey, beg);
945 }
946 if ( next != NULL )
947 beg = next;
f273ee37 948 } while ( next != NULL );
949
950 free( env );
951 return;
952}
953
954int
955cmp_addr (void *addr1, void *addr2, int addrlen)
956{
957 return memcmp( addr1, addr2, addrlen );
958}
959
c3adcdf8 960int
961is_direct_address (const struct sockaddr_in *addr)
f273ee37 962{
963 int i;
964 struct in_addr saddr, iaddr;
965
966 saddr = addr->sin_addr;
c3adcdf8 967
f273ee37 968 /* Note: assume IPV4 address !! */
969 for (i=0; i<n_direct_addr_list; i++ ) {
c3adcdf8 970 iaddr = saddr;
971 mask_addr( &iaddr, &direct_addr_list[i].mask,
972 sizeof(struct in_addr));
973 if (cmp_addr(&iaddr, &direct_addr_list[i].addr,
974 sizeof(struct in_addr)) == 0) {
975 if (direct_addr_list[i].negative) {
976 debug("negative match, addr to be SOCKSify: %s\n",
977 inet_ntoa(saddr));
978 return 0; /* not direct */
979 }
980 if (!direct_addr_list[i].negative) {
981 debug("positive match, addr to be direct: %s\n",
982 inet_ntoa(saddr));
983 return 1; /* direct*/
984 }
985 }
f273ee37 986 }
987 debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
c3adcdf8 988 return 0; /* not direct */
f273ee37 989}
990
991
992/** TTY operation **/
993
994int intr_flag = 0;
995
996#ifndef _WIN32
997void
998intr_handler(int sig)
999{
1000 intr_flag = 1;
1001}
1002
1003void
1004tty_change_echo(int fd, int enable)
1005{
c3adcdf8 1006 static struct termios ntio, otio; /* new/old termios */
1007 static sigset_t nset, oset; /* new/old sigset */
1008 static struct sigaction nsa, osa; /* new/old sigaction */
f273ee37 1009 static int disabled = 0;
1010
1011 if ( disabled && enable ) {
c3adcdf8 1012 /* enable echo */
1013 tcsetattr(fd, TCSANOW, &otio);
1014 disabled = 0;
1015 /* resotore sigaction */
1016 sigprocmask(SIG_SETMASK, &oset, NULL);
1017 sigaction(SIGINT, &osa, NULL);
1018 if ( intr_flag != 0 ) {
1019 /* re-generate signal */
1020 kill(getpid(), SIGINT);
1021 sigemptyset(&nset);
1022 sigsuspend(&nset);
1023 intr_flag = 0;
1024 }
f273ee37 1025 } else if (!disabled && !enable) {
c3adcdf8 1026 /* set SIGINTR handler and break syscall on singal */
1027 sigemptyset(&nset);
1028 sigaddset(&nset, SIGTSTP);
1029 sigprocmask(SIG_BLOCK, &nset, &oset);
1030 intr_flag = 0;
1031 memset(&nsa, 0, sizeof(nsa));
1032 nsa.sa_handler = intr_handler;
1033 sigaction(SIGINT, &nsa, &osa);
1034 /* disable echo */
1035 if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
1036 disabled = 1;
1037 ntio = otio;
1038 ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
1039 (void) tcsetattr(fd, TCSANOW, &ntio);
1040 }
f273ee37 1041 }
c3adcdf8 1042
f273ee37 1043 return;
1044}
1045
1046#define TTY_NAME "/dev/tty"
1047int
1048tty_readpass( const char *prompt, char *buf, size_t size )
1049{
1050 int tty, ret = 0;
1051
1052 tty = open(TTY_NAME, O_RDWR);
1053 if ( tty < 0 ) {
c3adcdf8 1054 error("Unable to open %s\n", TTY_NAME);
1055 return -1; /* can't open tty */
f273ee37 1056 }
1057 if ( size <= 0 )
c3adcdf8 1058 return -1; /* no room */
f273ee37 1059 write(tty, prompt, strlen(prompt));
1060 buf[0] = '\0';
c3adcdf8 1061 tty_change_echo(tty, 0); /* disable echo */
f273ee37 1062 ret = read(tty,buf, size-1);
c3adcdf8 1063 tty_change_echo(tty, 1); /* restore */
1064 write(tty, "\n", 1); /* new line */
f273ee37 1065 close(tty);
1066 if ( strchr(buf,'\n') == NULL )
c3adcdf8 1067 return -1;
f273ee37 1068 if ( 0 < ret )
c3adcdf8 1069 buf[ret] = '\0';
f273ee37 1070 return ret;
1071}
1072
1073#else /* _WIN32 */
1074
1075BOOL __stdcall
1076w32_intr_handler(DWORD dwCtrlType)
1077{
1078 if ( dwCtrlType == CTRL_C_EVENT ) {
c3adcdf8 1079 intr_flag = 1;
1080 return TRUE;
f273ee37 1081 } else {
c3adcdf8 1082 return FALSE;
f273ee37 1083 }
1084}
1085
1086#define tty_readpass w32_tty_readpass
1087int
1088w32_tty_readpass( const char *prompt, char *buf, size_t size )
1089{
1090 HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
c3adcdf8 1091 0, NULL, OPEN_EXISTING, 0, NULL);
f273ee37 1092 HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
c3adcdf8 1093 0, NULL, OPEN_EXISTING, 0, NULL);
f273ee37 1094 DWORD mode;
c3adcdf8 1095 DWORD ret, bytes;
f273ee37 1096
1097 if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
c3adcdf8 1098 fatal("Cannot open console. (errno=%d)", GetLastError());
f273ee37 1099
1100 WriteFile(out, prompt, strlen(prompt), &bytes, 0);
1101 SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
1102 GetConsoleMode(in, &mode);
1103 SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
1104 ret = ReadFile(in, buf, size, &bytes, 0);
c3adcdf8 1105 SetConsoleMode(in, mode); /* enable echo */
f273ee37 1106 SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
1107 if ( intr_flag )
c3adcdf8 1108 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
f273ee37 1109 WriteFile(out,"\n", 1, &bytes, 0);
1110 CloseHandle(in);
1111 CloseHandle(out);
1112 return ret;
1113}
1114
1115#endif /* _WIN32 */
1116
c3adcdf8 1117/*** User / Password ***/
1118
1119/* SOCKS5 and HTTP Proxy authentication may requires username and
1120 password. We ll give it via environment variable or tty.
1121 Username and password for authentication are decided by
1122 following rules:
1123
1124 Username is taken from
1125 1) server location spec (i.e. user@host:port)
1126 2) environment variables (see tables.1)
1127 3) system account name currently logged in.
1128
1129 Table.1 Order of environment variables for username
1130
1131 | SOCKS v5 | SOCKS v4 | HTTP proxy |
1132 --+-------------+-------------+-----------------+
1133 1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
1134 --+-------------+-------------+ |
1135 2 | SOCKS_USER | |
1136 --+---------------------------+-----------------+
1137 3 | CONNECT_USER |
1138 --+---------------------------------------------+
1139
1140 Password is taken from
1141 1) by environment variables (see table.2)
1142 2) by entering from tty.
1143
1144 Table.2 Order of environment variables for password
1145
1146 | SOCKS v5 | HTTP proxy |
1147 --+-----------------+---------------------+
1148 1 | SOCKS5_PASSWD | |
1149 --+-----------------+ HTTP_PROXY_PASSWORD |
1150 2 | SOCKS5_PASSWORD | |
1151 --+-----------------+---------------------+
1152 3 | CONNECT_PASSWORD |
1153 --+---------------------------------------+
1154
1155 Note: SOCKS5_PASSWD which is added in rev. 1.79
1156 to share value with NEC SOCKS implementation.
1157 */
1158
1159char *
1160determine_relay_user ()
1161{
1162 char *user = NULL;
1163 /* get username from environment variable, or system. */
1164 if (relay_method == METHOD_SOCKS) {
1165 if (user == NULL && socks_version == 5)
1166 user = getparam (ENV_SOCKS5_USER);
1167 if (user == NULL && socks_version == 4)
1168 user = getparam (ENV_SOCKS4_USER);
1169 if (user == NULL)
1170 user = getparam (ENV_SOCKS_USER);
1171 } else if (relay_method == METHOD_HTTP) {
1172 if (user == NULL)
1173 user = getparam (ENV_HTTP_PROXY_USER);
1174 }
1175 if (user == NULL)
1176 user = getparam (ENV_CONNECT_USER);
1177 /* determine relay user by system call if not yet. */
1178 if (user == NULL)
1179 user = getusername();
1180 return user;
1181}
1182
1183char *
1184determine_relay_password ()
1185{
1186 char *pass = NULL;
1187 if (pass == NULL && relay_method == METHOD_HTTP)
1188 pass = getparam(ENV_HTTP_PROXY_PASSWORD);
1189 if (pass == NULL && relay_method == METHOD_SOCKS)
1190 pass = getparam(ENV_SOCKS5_PASSWD);
1191 if (pass == NULL && relay_method == METHOD_SOCKS)
1192 pass = getparam(ENV_SOCKS5_PASSWORD);
1193 if (pass == NULL)
1194 pass = getparam(ENV_CONNECT_PASSWORD);
1195 return pass;
1196}
1197
1198
f273ee37 1199/*** network operations ***/
1200
c3adcdf8 1201
f273ee37 1202/* set_relay()
1203 Determine relay informations:
1204 method, host, port, and username.
1205 1st arg, METHOD should be METHOD_xxx.
1206 2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
1207 hostname is domain name or dot notation.
1208 If port is omitted, use 80 for METHOD_HTTP method,
1209 use 1080 for METHOD_SOCKS method.
1210 Username is also able to given by 3rd. format.
1211 2nd argument SPEC can be NULL. if NULL, use environment variable.
1212 */
1213int
1214set_relay( int method, char *spec )
1215{
1216 char *buf, *sep, *resolve;
c3adcdf8 1217
f273ee37 1218 relay_method = method;
1219
1220 read_parameter_file();
1221 initialize_direct_addr();
1222 if (n_direct_addr_list == 0) {
c3adcdf8 1223 debug ("(none)\n");
f273ee37 1224 } else {
c3adcdf8 1225 int i;
1226 for ( i=0; i<n_direct_addr_list; i++ ) {
1227 char *s1, *s2;
1228 s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
1229 s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
1230 debug(" #%d: %c%s/%s\n", i,
1231 (direct_addr_list[i].negative)? '!': ' ',
1232 s1, s2);
1233 free(s1);
1234 free(s2);
1235 }
f273ee37 1236 }
1237
1238 switch ( method ) {
1239 case METHOD_DIRECT:
c3adcdf8 1240 return -1; /* nothing to do */
1241
f273ee37 1242 case METHOD_SOCKS:
c3adcdf8 1243 if ( spec == NULL ) {
1244 switch ( socks_version ) {
1245 case 5:
1246 spec = getparam(ENV_SOCKS5_SERVER);
1247 break;
1248 case 4:
1249 spec = getparam(ENV_SOCKS4_SERVER);
1250 break;
1251 }
1252 }
1253 if ( spec == NULL )
1254 spec = getparam(ENV_SOCKS_SERVER);
1255
1256 if ( spec == NULL )
1257 fatal("Failed to determine SOCKS server.\n");
1258 relay_port = 1080; /* set default first */
1259
1260 /* determine resolve method */
1261 if ( socks_resolve == RESOLVE_UNKNOWN ) {
1262 if ( ((socks_version == 5) &&
1263 ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
1264 ((socks_version == 4) &&
1265 ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
1266 ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
1267 socks_resolve = lookup_resolve( resolve );
1268 if ( socks_resolve == RESOLVE_UNKNOWN )
1269 fatal("Invalid resolve method: %s\n", resolve);
1270 } else {
1271 /* default */
1272 if ( socks_version == 5 )
1273 socks_resolve = RESOLVE_REMOTE;
1274 else
1275 socks_resolve = RESOLVE_LOCAL;
1276 }
1277 }
1278 break;
1279
f273ee37 1280 case METHOD_HTTP:
c3adcdf8 1281 if ( spec == NULL )
1282 spec = getparam(ENV_HTTP_PROXY);
1283 if ( spec == NULL )
1284 fatal("You must specify http proxy server\n");
1285 relay_port = 80; /* set default first */
1286 break;
1287 case METHOD_TELNET:
1288 if ( spec == NULL )
1289 spec = getparam(ENV_TELNET_PROXY);
1290 if ( spec == NULL )
1291 fatal("You must specify telnet proxy server\n");
1292 relay_port = 23; /* set default first */
f273ee37 1293 }
c3adcdf8 1294
f273ee37 1295 if (expect( spec, HTTP_PROXY_PREFIX)) {
c3adcdf8 1296 /* URL format like: "http://server:port/" */
1297 /* extract server:port part */
1298 buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
1299 buf[strcspn(buf, "/")] = '\0';
f273ee37 1300 } else {
c3adcdf8 1301 /* assume spec is aready "server:port" format */
1302 buf = strdup( spec );
f273ee37 1303 }
1304 spec = buf;
c3adcdf8 1305
1306 /* check username in spec */
f273ee37 1307 sep = strchr( spec, '@' );
1308 if ( sep != NULL ) {
c3adcdf8 1309 *sep = '\0';
1310 relay_user = strdup( spec );
1311 spec = sep +1;
f273ee37 1312 }
c3adcdf8 1313 if (relay_user == NULL)
1314 relay_user = determine_relay_user();
1315
f273ee37 1316 /* split out hostname and port number from spec */
1317 sep = strchr(spec,':');
1318 if ( sep == NULL ) {
c3adcdf8 1319 /* hostname only, port is already set as default */
1320 relay_host = strdup( spec );
f273ee37 1321 } else {
c3adcdf8 1322 /* hostname and port */
1323 relay_port = atoi(sep+1);
1324 *sep = '\0';
1325 relay_host = strdup( spec );
f273ee37 1326 }
1327 free(buf);
1328 return 0;
1329}
1330
1331
1332u_short
1333resolve_port( const char *service )
1334{
1335 int port;
c3adcdf8 1336 if ( service[strspn (service, digits)] == '\0' ) {
1337 /* all digits, port number */
1338 port = atoi(service);
f273ee37 1339 } else {
c3adcdf8 1340 /* treat as service name */
1341 struct servent *ent;
1342 ent = getservbyname( service, NULL );
1343 if ( ent == NULL ) {
1344 debug("Unknown service, '%s'\n", service);
1345 port = 0;
1346 } else {
1347 port = ntohs(ent->s_port);
1348 debug("service: %s => %d\n", service, port);
1349 }
f273ee37 1350 }
1351 return (u_short)port;
1352}
1353
1354void
1355make_revstr(void)
1356{
1357 char *ptr;
1358 size_t len;
1359 ptr = strstr(rcs_revstr, ": ");
1360 if (!ptr) {
c3adcdf8 1361 revstr = strdup("unknown");
1362 return;
f273ee37 1363 }
1364 ptr += 2;
c3adcdf8 1365 len = strspn(ptr, dotdigits);
f273ee37 1366 if (0 < len) {
c3adcdf8 1367 revstr = xmalloc(len+1);
1368 memcpy(revstr, ptr, len);
1369 revstr[len] = '\0';
f273ee37 1370 }
1371}
1372
1373int
1374getarg( int argc, char **argv )
1375{
1376 int err = 0;
1377 char *ptr, *server = (char*)NULL;
1378 int method = METHOD_DIRECT;
c3adcdf8 1379
f273ee37 1380 progname = *argv;
1381 argc--, argv++;
1382
1383 /* check optinos */
1384 while ( (0 < argc) && (**argv == '-') ) {
c3adcdf8 1385 ptr = *argv + 1;
1386 while ( *ptr ) {
1387 switch ( *ptr ) {
1388 case 's': /* use SOCKS */
1389 method = METHOD_SOCKS;
1390 break;
1391
1392 case 'n': /* no proxy */
1393 method = METHOD_DIRECT;
1394 break;
1395
1396 case 'h': /* use http-proxy */
1397 method = METHOD_HTTP;
1398 break;
1399 case 't':
1400 method = METHOD_TELNET;
1401 break;
1402
1403 case 'S': /* specify SOCKS server */
1404 if ( 1 < argc ) {
1405 argv++, argc--;
1406 method = METHOD_SOCKS;
1407 server = *argv;
1408 } else {
1409 error("option '-%c' needs argument.\n", *ptr);
1410 err++;
1411 }
1412 break;
1413
1414 case 'H': /* specify http-proxy server */
1415 if ( 1 < argc ) {
1416 argv++, argc--;
1417 method = METHOD_HTTP;
1418 server = *argv;
1419 } else {
1420 error("option '-%c' needs argument.\n", *ptr);
1421 err++;
1422 }
1423 break;
1424 case 'T': /* specify telnet proxy server */
1425 if ( 1 < argc ) {
1426 argv++, argc--;
1427 method = METHOD_TELNET;
1428 server = *argv;
1429 } else {
1430 error("option '-%c' needs argument.\n", *ptr);
1431 err++;
1432 }
1433 break;
1434
1435 case 'c':
1436 if (1 < argc) {
1437 argv++, argc--;
1438 telnet_command = *argv;
1439 } else {
1440 error("option '%c' needs argument.\n", *ptr);
1441 err++;
1442 }
1443 break;
1444
1445 case 'P':
1446 f_hold_session = 1;
1447 /* without break */
1448 case 'p': /* specify port to forward */
1449 if ( 1 < argc ) {
1450 argv++, argc--;
1451 local_type = LOCAL_SOCKET;
1452 local_port = resolve_port(*argv);
1453 } else {
1454 error("option '-%c' needs argument.\n", *ptr);
1455 err++;
1456 }
1457 break;
f273ee37 1458
c3adcdf8 1459#ifndef _WIN32
1460 case 'w':
1461 if ( 1 < argc ) {
1462 argv++, argc--;
1463 connect_timeout = atoi(*argv);
1464 } else {
1465 error("option '-%c' needs argument.\n", *ptr);
1466 err++;
1467 }
1468 break;
f273ee37 1469#endif /* not _WIN32 */
1470
c3adcdf8 1471 case '4':
1472 socks_version = 4;
1473 break;
1474
1475 case '5':
1476 socks_version = 5;
1477 break;
1478
1479 case 'a':
1480 if ( 1 < argc ) {
1481 argv++, argc--;
1482 socks5_auth = *argv;
1483 } else {
1484 error("option '-%c' needs argument.\n", *ptr);
1485 err++;
1486 }
1487 break;
1488
1489 case 'R': /* specify resolve method */
1490 if ( 1 < argc ) {
1491 argv++, argc--;
1492 socks_resolve = lookup_resolve( *argv );
1493 } else {
1494 error("option '-%c' needs argument.\n", *ptr);
1495 err++;
1496 }
1497 break;
1498
1499 case 'V': /* print version */
1500 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1501 exit(0);
1502
1503 case 'd': /* debug mode */
1504 f_debug++;
1505 break;
1506
1507 default:
1508 error("unknown option '-%c'\n", *ptr);
1509 err++;
1510 }
1511 ptr++;
1512 }
1513 argc--, argv++;
f273ee37 1514 }
c3adcdf8 1515
f273ee37 1516 /* check error */
1517 if ( 0 < err )
c3adcdf8 1518 goto quit;
1519
f273ee37 1520 set_relay( method, server );
1521
1522 /* check destination HOST (MUST) */
1523 if ( argc == 0 ) {
c3adcdf8 1524 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1525 fprintf(stderr, usage, progname);
1526 exit(0);
f273ee37 1527 }
1528 dest_host = argv[0];
1529 /* decide port or service name from programname or argument */
1530 if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
c3adcdf8 1531 ((ptr=strchr( progname, '\\')) != NULL) )
1532 ptr++;
f273ee37 1533 else
c3adcdf8 1534 ptr = progname;
f273ee37 1535 if ( dest_port == 0 ) {
c3adcdf8 1536 /* accept only if -P is not specified. */
1537 if ( 1 < argc ) {
1538 /* get port number from argument (prior to progname) */
1539 /* NOTE: This way is for cvs ext method. */
1540 dest_port = resolve_port(argv[1]);
1541 } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
1542 /* decide port number from program name */
1543 char *str = strdup( ptr+8 );
1544 str[strcspn( str, "." )] = '\0';
1545 dest_port = resolve_port(str);
1546 free(str);
1547 }
f273ee37 1548 }
1549 /* check port number */
1550 if ( dest_port <= 0 ) {
c3adcdf8 1551 error( "You must specify the destination port correctly.\n");
1552 err++;
1553 goto quit;
f273ee37 1554 }
1555 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
c3adcdf8 1556 error("Invalid relay port: %d\n", dest_port);
1557 err++;
1558 goto quit;
f273ee37 1559 }
1560
1561quit:
1562 /* report for debugging */
1563 debug("relay_method = %s (%d)\n",
c3adcdf8 1564 method_names[relay_method], relay_method);
f273ee37 1565 if ( relay_method != METHOD_DIRECT ) {
c3adcdf8 1566 debug("relay_host=%s\n", relay_host);
1567 debug("relay_port=%d\n", relay_port);
1568 debug("relay_user=%s\n", relay_user);
f273ee37 1569 }
1570 if ( relay_method == METHOD_SOCKS ) {
c3adcdf8 1571 debug("socks_version=%d\n", socks_version);
1572 debug("socks_resolve=%s (%d)\n",
1573 resolve_names[socks_resolve], socks_resolve);
f273ee37 1574 }
1575 debug("local_type=%s\n", local_type_names[local_type]);
c3adcdf8 1576 if ( local_type == LOCAL_SOCKET ) {
1577 debug("local_port=%d\n", local_port);
1578 if (f_hold_session)
1579 debug (" with holding remote session.\n");
1580 }
f273ee37 1581 debug("dest_host=%s\n", dest_host);
1582 debug("dest_port=%d\n", dest_port);
1583 if ( 0 < err ) {
c3adcdf8 1584 fprintf(stderr, usage, progname);
1585 exit(1);
f273ee37 1586 }
1587 return 0;
1588}
1589
1590#ifndef _WIN32
1591/* Time-out feature is not allowed for Win32 native compilers. */
1592/* MSVC and Borland C cannot but Cygwin and UNIXes can. */
1593
1594/* timeout signal hander */
1595void
1596sig_timeout(void)
1597{
1598 debug( "timed out\n" );
1599 signal( SIGALRM, SIG_IGN );
1600 alarm( 0 );
1601}
1602
1603/* set timeout param = seconds, 0 clears */
1604void
1605set_timeout(int timeout)
1606{
1607 /* This feature is allowed for UNIX or cygwin environments, currently */
1608 if ( timeout == 0 ) {
1609 debug( "clearing timeout\n" );
1610 signal( SIGALRM, SIG_IGN );
1611 alarm( 0 );
1612 } else {
1613 debug( "setting timeout: %d seconds\n", timeout );
1614 signal(SIGALRM, (void *)sig_timeout);
1615 alarm( timeout );
1616 }
1617}
1618#endif
1619
c3adcdf8 1620#if !defined(_WIN32) && !defined(__CYGWIN32__)
1621void
1622switch_ns (struct sockaddr_in *ns)
1623{
1624 res_init();
1625 memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
1626 _res.nscount = 1;
1627 debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
1628}
1629#endif /* !_WIN32 && !__CYGWIN32__ */
f273ee37 1630
c3adcdf8 1631/* TODO: IPv6
1632 TODO: fallback if askpass execution failed.
1633 */
f273ee37 1634
1635int
c3adcdf8 1636local_resolve (const char *host, struct sockaddr_in *addr)
f273ee37 1637{
1638 struct hostent *ent;
c3adcdf8 1639 if ( strspn(host, dotdigits) == strlen(host) ) {
1640 /* given by IPv4 address */
1641 addr->sin_family = AF_INET;
1642 addr->sin_addr.s_addr = inet_addr(host);
f273ee37 1643 } else {
c3adcdf8 1644 debug("resolving host by name: %s\n", host);
1645 ent = gethostbyname (host);
1646 if ( ent ) {
1647 memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
1648 addr->sin_family = ent->h_addrtype;
1649 debug("resolved: %s (%s)\n",
1650 host, inet_ntoa(addr->sin_addr));
1651 } else {
1652 debug("failed to resolve locally.\n");
1653 return -1; /* failed */
1654 }
1655 }
1656 return 0; /* good */
f273ee37 1657}
1658
c3adcdf8 1659int
f273ee37 1660open_connection( const char *host, u_short port )
1661{
1662 SOCKET s;
1663 struct sockaddr_in saddr;
1664
1665 if ( relay_method == METHOD_DIRECT ) {
c3adcdf8 1666 host = dest_host;
1667 port = dest_port;
1668 } else if ((local_resolve (dest_host, &saddr) >= 0)&&
1669 (is_direct_address(&saddr))) {
1670 debug("%s is connected directly\n", dest_host);
1671 relay_method = METHOD_DIRECT;
1672 host = dest_host;
1673 port = dest_port;
f273ee37 1674 } else {
c3adcdf8 1675 host = relay_host;
1676 port = relay_port;
f273ee37 1677 }
1678
c3adcdf8 1679 if (local_resolve (host, &saddr) < 0) {
1680 error("can't resolve hostname: %s\n", host);
1681 return SOCKET_ERROR;
f273ee37 1682 }
1683 saddr.sin_port = htons(port);
1684
1685 debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
1686 s = socket( AF_INET, SOCK_STREAM, 0 );
1687 if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
c3adcdf8 1688 == SOCKET_ERROR) {
1689 debug( "connect() failed.\n");
1690 return SOCKET_ERROR;
f273ee37 1691 }
1692 return s;
1693}
1694
1695void
1696report_text( char *prefix, char *buf )
1697{
1698 static char work[1024];
1699 char *tmp;
1700
1701 if ( !f_debug )
c3adcdf8 1702 return;
f273ee37 1703 if ( !f_report )
c3adcdf8 1704 return; /* don't report */
f273ee37 1705 debug("%s \"", prefix);
1706 while ( *buf ) {
c3adcdf8 1707 memset( work, 0, sizeof(work));
1708 tmp = work;
1709 while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) {
1710 switch ( *buf ) {
1711 case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
1712 case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
1713 case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
1714 case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
1715 default:
1716 if ( isprint(*buf) ) {
1717 *tmp++ = *buf;
1718 } else {
1719 sprintf( tmp, "\\x%02X", (unsigned char)*buf);
1720 tmp += strlen(tmp);
1721 }
1722 }
1723 buf++;
1724 *tmp = '\0';
1725 }
1726 debug_("%s", work);
f273ee37 1727 }
c3adcdf8 1728
f273ee37 1729 debug_("\"\n");
1730}
1731
1732
1733void
1734report_bytes( char *prefix, char *buf, int len )
1735{
1736 if ( ! f_debug )
c3adcdf8 1737 return;
f273ee37 1738 debug( "%s", prefix );
1739 while ( 0 < len ) {
c3adcdf8 1740 fprintf( stderr, " %02x", *(unsigned char *)buf);
1741 buf++;
1742 len--;
f273ee37 1743 }
1744 fprintf(stderr, "\n");
1745 return;
1746}
1747
1748int
1749atomic_out( SOCKET s, char *buf, int size )
1750{
1751 int ret, len;
1752
1753 assert( buf != NULL );
1754 assert( 0<=size );
1755 /* do atomic out */
1756 ret = 0;
1757 while ( 0 < size ) {
c3adcdf8 1758 len = send( s, buf+ret, size, 0 );
1759 if ( len == -1 )
1760 fatal("atomic_out() failed to send(), %d\n", socket_errno());
1761 ret += len;
1762 size -= len;
f273ee37 1763 }
1764 if (!f_report) {
c3adcdf8 1765 debug("atomic_out() [some bytes]\n");
1766 debug(">>> xx xx xx xx ...\n");
f273ee37 1767 } else {
c3adcdf8 1768 debug("atomic_out() [%d bytes]\n", ret);
1769 report_bytes(">>>", buf, ret);
f273ee37 1770 }
1771 return ret;
1772}
1773
1774int
1775atomic_in( SOCKET s, char *buf, int size )
1776{
1777 int ret, len;
1778
1779 assert( buf != NULL );
1780 assert( 0<=size );
c3adcdf8 1781
f273ee37 1782 /* do atomic in */
1783 ret = 0;
1784 while ( 0 < size ) {
c3adcdf8 1785 len = recv( s, buf+ret, size, 0 );
1786 if ( len == -1 ) {
1787 fatal("atomic_in() failed to recv(), %d\n", socket_errno());
1788 } else if ( len == 0 ) {
1789 fatal( "Connection closed by peer.\n");
1790 }
1791 ret += len;
1792 size -= len;
f273ee37 1793 }
1794 if (!f_report) {
c3adcdf8 1795 debug("atomic_in() [some bytes]\n");
1796 debug("<<< xx xx xx xx ...\n");
f273ee37 1797 } else {
c3adcdf8 1798 debug("atomic_in() [%d bytes]\n", ret);
1799 report_bytes("<<<", buf, ret);
f273ee37 1800 }
1801 return ret;
1802}
1803
1804int
1805line_input( SOCKET s, char *buf, int size )
1806{
1807 char *dst = buf;
1808 if ( size == 0 )
c3adcdf8 1809 return 0; /* no error */
f273ee37 1810 size--;
1811 while ( 0 < size ) {
c3adcdf8 1812 switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */
1813 case SOCKET_ERROR:
1814 error("recv() error\n");
1815 return -1; /* error */
1816 case 0:
1817 size = 0; /* end of stream */
1818 break;
1819 default:
1820 /* continue reading until last 1 char is EOL? */
1821 if ( *dst == '\n' ) {
1822 /* finished */
1823 size = 0;
1824 } else {
1825 /* more... */
1826 size--;
1827 }
1828 dst++;
1829 }
f273ee37 1830 }
1831 *dst = '\0';
1832 report_text( "<<<", buf);
1833 return 0;
1834}
1835
f273ee37 1836/* cut_token()
1837 Span token in given string STR until char in DELIM is appeared.
1838 Then replace contiguous DELIMS with '\0' for string termination
1839 and returns next pointer.
1840 If no next token, return NULL.
1841*/
1842char *
1843cut_token( char *str, char *delim)
1844{
1845 char *ptr = str + strcspn(str, delim);
1846 char *end = ptr + strspn(ptr, delim);
1847 if ( ptr == str )
c3adcdf8 1848 return NULL;
f273ee37 1849 while ( ptr < end )
c3adcdf8 1850 *ptr++ = '\0';
f273ee37 1851 return ptr;
1852}
1853
1854const char *
1855lookup(int num, LOOKUP_ITEM *items)
1856{
1857 int i = 0;
1858 while (0 <= items[i].num) {
c3adcdf8 1859 if (items[i].num == num)
1860 return items[i].str;
1861 i++;
f273ee37 1862 }
1863 return "(unknown)";
1864}
1865
1866/* readpass()
c3adcdf8 1867 password input routine
f273ee37 1868 Use ssh-askpass (same mechanism to OpenSSH)
1869*/
1870char *
1871readpass( const char* prompt, ...)
1872{
c3adcdf8 1873 static char buf[1000]; /* XXX, don't be fix length */
f273ee37 1874 va_list args;
1875 va_start(args, prompt);
1876 vsprintf(buf, prompt, args);
1877 va_end(args);
1878
1879 if ( getparam(ENV_SSH_ASKPASS)
1880#if !defined(_WIN32) && !defined(__CYGWIN32__)
c3adcdf8 1881 && getenv("DISPLAY")
f273ee37 1882#endif /* not _WIN32 && not __CYGWIN32__ */
c3adcdf8 1883 ) {
1884 /* use ssh-askpass to get password */
1885 FILE *fp;
1886 char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
1887 cmd = xmalloc(strlen(askpass) +1 +1 +strlen(buf) +1);
1888 sprintf(cmd, "%s \"%s\"", askpass, buf);
1889 fp = popen(cmd, "r");
1890 free(cmd);
1891 if ( fp == NULL )
1892 return NULL; /* fail */
1893 buf[0] = '\0';
1894 if (fgets(buf, sizeof(buf), fp) == NULL)
1895 return NULL; /* fail */
1896 fclose(fp);
f273ee37 1897 } else {
c3adcdf8 1898 tty_readpass( buf, buf, sizeof(buf));
f273ee37 1899 }
1900 buf[strcspn(buf, "\r\n")] = '\0';
1901 return buf;
1902}
1903
1904static int
1905socks5_do_auth_userpass( int s )
1906{
1907 unsigned char buf[1024], *ptr;
c3adcdf8 1908 char *pass = NULL;
f273ee37 1909 int len;
c3adcdf8 1910
f273ee37 1911 /* do User/Password authentication. */
c3adcdf8 1912 /* This feature requires username and password from
f273ee37 1913 command line argument or environment variable,
1914 or terminal. */
c3adcdf8 1915 if (relay_user == NULL)
1916 fatal("cannot determine user name.\n");
f273ee37 1917
1918 /* get password from environment variable if exists. */
c3adcdf8 1919 if ((pass=determine_relay_password()) == NULL &&
1920 (pass=readpass("Enter SOCKS5 password for %s@%s: ",
1921 relay_user, relay_host)) == NULL)
1922 fatal("Cannot get password for user: %s\n", relay_user);
f273ee37 1923
1924 /* make authentication packet */
1925 ptr = buf;
c3adcdf8 1926 PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */
1927 len = strlen( relay_user ); /* ULEN and UNAME */
f273ee37 1928 PUT_BYTE( ptr++, len );
1929 strcpy( ptr, relay_user );
1930 ptr += len;
c3adcdf8 1931 len = strlen( pass ); /* PLEN and PASSWD */
f273ee37 1932 PUT_BYTE( ptr++, strlen(pass));
1933 strcpy( ptr, pass );
1934 ptr += len;
c3adcdf8 1935 memset (pass, 0, strlen(pass)); /* erase password */
1936
f273ee37 1937 /* send it and get answer */
1938 f_report = 0;
1939 atomic_out( s, buf, ptr-buf );
1940 f_report = 1;
1941 atomic_in( s, buf, 2 );
1942
1943 /* check status */
1944 if ( buf[1] == 0 )
c3adcdf8 1945 return 0; /* success */
f273ee37 1946 else
c3adcdf8 1947 return -1; /* fail */
f273ee37 1948}
1949
1950static const char *
1951socks5_getauthname( int auth )
1952{
1953 switch ( auth ) {
1954 case SOCKS5_AUTH_REJECT: return "REJECTED";
1955 case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
1956 case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
1957 case SOCKS5_AUTH_USERPASS: return "USERPASS";
1958 case SOCKS5_AUTH_CHAP: return "CHAP";
1959 case SOCKS5_AUTH_EAP: return "EAP";
1960 case SOCKS5_AUTH_MAF: return "MAF";
1961 default: return "(unknown)";
1962 }
1963}
1964
1965typedef struct {
1966 char* name;
1967 unsigned char auth;
1968} AUTH_METHOD_ITEM;
1969
1970AUTH_METHOD_ITEM socks5_auth_table[] = {
1971 { "none", SOCKS5_AUTH_NOAUTH },
1972 { "gssapi", SOCKS5_AUTH_GSSAPI },
1973 { "userpass", SOCKS5_AUTH_USERPASS },
1974 { "chap", SOCKS5_AUTH_CHAP },
1975 { NULL, -1 },
1976};
1977
1978int
1979socks5_auth_parse_1(char *start, char *end){
1980 int i, len;
1981 for ( ; *start; start++ )
c3adcdf8 1982 if ( *start != ' ' && *start != '\t') break;
f273ee37 1983 for ( end--; end >= start; end-- ) {
c3adcdf8 1984 if ( *end != ' ' && *end != '\t'){
1985 end++;
1986 break;
1987 }
f273ee37 1988 }
1989 len = end - start;
1990 for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
c3adcdf8 1991 if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
1992 return socks5_auth_table[i].auth;
1993 }
f273ee37 1994 }
1995 fatal("Unknown auth method: %s\n", start);
1996 return -1;
1997}
1998
1999int
2000socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
2001 char *end;
2002 int i = 0;
2003 while ( i < max_auth ) {
c3adcdf8 2004 end = strchr(start, ',');
2005 if (*start && end) {
2006 auth_list[i++] = socks5_auth_parse_1(start, end);
2007 start = ++end;
2008 } else {
2009 break;
2010 }
f273ee37 2011 }
2012 if ( *start && ( i < max_auth ) ){
c3adcdf8 2013 for( end = start; *end; end++ );
2014 auth_list[i++] = socks5_auth_parse_1(start, end);
f273ee37 2015 } else {
c3adcdf8 2016 fatal("Too much auth method.\n");
f273ee37 2017 }
2018 return i;
2019}
2020
2021/* begin SOCKS5 relaying
2022 And no authentication is supported.
2023 */
2024int
2025begin_socks5_relay( SOCKET s )
2026{
2027 unsigned char buf[256], *ptr, *env = socks5_auth;
2028 unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
2029 int len, auth_result, i;
2030
2031 debug( "begin_socks_relay()\n");
c3adcdf8 2032
f273ee37 2033 /* request authentication */
2034 ptr = buf;
c3adcdf8 2035 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
f273ee37 2036
2037 if ( env == NULL )
c3adcdf8 2038 env = getparam(ENV_SOCKS5_AUTH);
f273ee37 2039 if ( env == NULL ) {
c3adcdf8 2040 /* add no-auth authentication */
2041 auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
2042 /* add user/pass authentication */
2043 auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
f273ee37 2044 } else {
c3adcdf8 2045 n_auth = socks5_auth_parse(env, auth_list, 10);
f273ee37 2046 }
c3adcdf8 2047 PUT_BYTE( ptr++, n_auth); /* num auth */
f273ee37 2048 for (i=0; i<n_auth; i++) {
c3adcdf8 2049 debug("available auth method[%d] = %s (0x%02x)\n",
2050 i, socks5_getauthname(auth_list[i]), auth_list[i]);
2051 PUT_BYTE( ptr++, auth_list[i]); /* authentications */
f273ee37 2052 }
c3adcdf8 2053 atomic_out( s, buf, ptr-buf ); /* send requst */
2054 atomic_in( s, buf, 2 ); /* recv response */
2055 if ( (buf[0] != 5) || /* ver5 response */
2056 (buf[1] == 0xFF) ) { /* check auth method */
2057 error("No auth method accepted.\n");
2058 return -1;
f273ee37 2059 }
2060 auth_method = buf[1];
2061
2062 debug("auth method: %s\n", socks5_getauthname(auth_method));
c3adcdf8 2063
f273ee37 2064 switch ( auth_method ) {
2065 case SOCKS5_AUTH_REJECT:
c3adcdf8 2066 error("No acceptable authentication method\n");
2067 return -1; /* fail */
2068
f273ee37 2069 case SOCKS5_AUTH_NOAUTH:
c3adcdf8 2070 /* nothing to do */
2071 auth_result = 0;
2072 break;
f273ee37 2073
2074 case SOCKS5_AUTH_USERPASS:
c3adcdf8 2075 auth_result = socks5_do_auth_userpass(s);
2076 break;
2077
f273ee37 2078 default:
c3adcdf8 2079 error("Unsupported authentication method: %s\n",
2080 socks5_getauthname( auth_method ));
2081 return -1; /* fail */
f273ee37 2082 }
2083 if ( auth_result != 0 ) {
c3adcdf8 2084 error("Authentication failed.\n");
2085 return -1;
f273ee37 2086 }
2087 /* request to connect */
2088 ptr = buf;
c3adcdf8 2089 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2090 PUT_BYTE( ptr++, 1); /* CMD: CONNECT */
2091 PUT_BYTE( ptr++, 0); /* FLG: 0 */
f273ee37 2092 if ( dest_addr.sin_addr.s_addr == 0 ) {
c3adcdf8 2093 /* resolved by SOCKS server */
2094 PUT_BYTE( ptr++, 3); /* ATYP: DOMAINNAME */
2095 len = strlen(dest_host);
2096 PUT_BYTE( ptr++, len); /* DST.ADDR (len) */
2097 memcpy( ptr, dest_host, len ); /* (hostname) */
2098 ptr += len;
f273ee37 2099 } else {
c3adcdf8 2100 /* resolved localy */
2101 PUT_BYTE( ptr++, 1 ); /* ATYP: IPv4 */
2102 memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
2103 ptr += sizeof(dest_addr.sin_addr);
f273ee37 2104 }
c3adcdf8 2105 PUT_BYTE( ptr++, dest_port>>8); /* DST.PORT */
f273ee37 2106 PUT_BYTE( ptr++, dest_port&0xFF);
c3adcdf8 2107 atomic_out( s, buf, ptr-buf); /* send request */
2108 atomic_in( s, buf, 4 ); /* recv response */
2109 if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */
2110 error("Got error response from SOCKS server: %d (%s).\n",
2111 buf[1], lookup(buf[1], socks5_rep_names));
2112 return -1;
f273ee37 2113 }
2114 ptr = buf + 4;
c3adcdf8 2115 switch ( buf[3] ) { /* case by ATYP */
2116 case 1: /* IP v4 ADDR*/
2117 atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */
2118 break;
2119 case 3: /* DOMAINNAME */
2120 atomic_in( s, ptr, 1 ); /* recv name and port */
2121 atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
2122 break;
2123 case 4: /* IP v6 ADDR */
2124 atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */
2125 break;
f273ee37 2126 }
c3adcdf8 2127
f273ee37 2128 /* Conguraturation, connected via SOCKS5 server! */
2129 return 0;
2130}
2131
2132/* begin SOCKS protocol 4 relaying
2133 And no authentication is supported.
2134
2135 There's SOCKS protocol version 4 and 4a. Protocol version
2136 4a has capability to resolve hostname by SOCKS server, so
2137 we don't need resolving IP address of destination host on
2138 local machine.
2139
2140 Environment variable SOCKS_RESOLVE directs how to resolve
2141 IP addess. There's 3 keywords allowed; "local", "remote"
2142 and "both" (case insensitive). Keyword "local" means taht
2143 target host name is resolved by localhost resolver
2144 (usualy with gethostbyname()), "remote" means by remote
2145 SOCKS server, "both" means to try resolving by localhost
2146 then remote.
2147
2148 SOCKS4 protocol and authentication of SOCKS5 protocol
2149 requires user name on connect request.
2150 User name is determined by following method.
c3adcdf8 2151
f273ee37 2152 1. If server spec has user@hostname:port format then
2153 user part is used for this SOCKS server.
c3adcdf8 2154
f273ee37 2155 2. Get user name from environment variable LOGNAME, USER
2156 (in this order).
2157
2158*/
2159int
2160begin_socks4_relay( SOCKET s )
2161{
2162 unsigned char buf[256], *ptr;
2163
2164 debug( "begin_socks_relay()\n");
c3adcdf8 2165
2166 /* make connect request packet
f273ee37 2167 protocol v4:
2168 VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
2169 protocol v4a:
2170 VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
2171 */
2172 ptr = buf;
c3adcdf8 2173 PUT_BYTE( ptr++, 4); /* protocol version (4) */
2174 PUT_BYTE( ptr++, 1); /* CONNECT command */
2175 PUT_BYTE( ptr++, dest_port>>8); /* destination Port */
f273ee37 2176 PUT_BYTE( ptr++, dest_port&0xFF);
2177 /* destination IP */
2178 memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
2179 ptr += sizeof(dest_addr.sin_addr);
2180 if ( dest_addr.sin_addr.s_addr == 0 )
c3adcdf8 2181 *(ptr-1) = 1; /* fake, protocol 4a */
f273ee37 2182 /* username */
c3adcdf8 2183 if (relay_user == NULL)
2184 fatal( "Cannot determine user name.\n");
f273ee37 2185 strcpy( ptr, relay_user );
2186 ptr += strlen( relay_user ) +1;
2187 /* destination host name (for protocol 4a) */
2188 if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
c3adcdf8 2189 strcpy( ptr, dest_host );
2190 ptr += strlen( dest_host ) +1;
f273ee37 2191 }
2192 /* send command and get response
2193 response is: VN:1, CD:1, PORT:2, ADDR:4 */
c3adcdf8 2194 atomic_out( s, buf, ptr-buf); /* send request */
2195 atomic_in( s, buf, 8 ); /* recv response */
2196 if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */
2197 error("Got error response: %d: '%s'.\n",
2198 buf[1], lookup(buf[1], socks4_rep_names));
2199 return -1; /* failed */
f273ee37 2200 }
c3adcdf8 2201
f273ee37 2202 /* Conguraturation, connected via SOCKS4 server! */
2203 return 0;
2204}
2205
2206int
2207sendf(SOCKET s, const char *fmt,...)
2208{
c3adcdf8 2209 static char buf[10240]; /* xxx, enough? */
2210
f273ee37 2211 va_list args;
2212 va_start( args, fmt );
2213 vsprintf( buf, fmt, args );
2214 va_end( args );
c3adcdf8 2215
f273ee37 2216 report_text(">>>", buf);
2217 if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
c3adcdf8 2218 debug("failed to send http request. errno=%d\n", socket_errno());
2219 return -1;
f273ee37 2220 }
2221 return 0;
2222}
2223
2224const char *base64_table =
2225"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2226
2227char *
2228make_base64_string(const char *str)
2229{
2230 static char *buf;
2231 unsigned char *src;
2232 char *dst;
2233 int bits, data, src_len, dst_len;
2234 /* make base64 string */
2235 src_len = strlen(str);
2236 dst_len = (src_len+2)/3*4;
c3adcdf8 2237 buf = xmalloc(dst_len+1);
f273ee37 2238 bits = data = 0;
2239 src = (unsigned char *)str;
2240 dst = (unsigned char *)buf;
2241 while ( dst_len-- ) {
c3adcdf8 2242 if ( bits < 6 ) {
2243 data = (data << 8) | *src;
2244 bits += 8;
2245 if ( *src != 0 )
2246 src++;
2247 }
2248 *dst++ = base64_table[0x3F & (data >> (bits-6))];
2249 bits -= 6;
f273ee37 2250 }
2251 *dst = '\0';
2252 /* fix-up tail padding */
2253 switch ( src_len%3 ) {
2254 case 1:
c3adcdf8 2255 *--dst = '=';
f273ee37 2256 case 2:
c3adcdf8 2257 *--dst = '=';
f273ee37 2258 }
2259 return buf;
2260}
2261
2262
2263int
c3adcdf8 2264basic_auth (SOCKET s)
f273ee37 2265{
2266 char *userpass;
2267 char *cred;
c3adcdf8 2268 const char *user = relay_user;
2269 char *pass = NULL;
f273ee37 2270 int len, ret;
c3adcdf8 2271
2272 /* Get username/password for authentication */
2273 if (user == NULL)
2274 fatal("Cannot decide username for proxy authentication.");
2275 if ((pass = determine_relay_password ()) == NULL &&
2276 (pass = readpass("Enter proxy authentication password for %s@%s: ",
2277 relay_user, relay_host)) == NULL)
2278 fatal("Cannot decide password for proxy authentication.");
2279
f273ee37 2280 len = strlen(user)+strlen(pass)+1;
c3adcdf8 2281 userpass = xmalloc(len+1);
f273ee37 2282 sprintf(userpass,"%s:%s", user, pass);
c3adcdf8 2283 memset (pass, 0, strlen(pass));
f273ee37 2284 cred = make_base64_string(userpass);
c3adcdf8 2285 memset (userpass, 0, len);
f273ee37 2286
c3adcdf8 2287 f_report = 0; /* don't report for security */
f273ee37 2288 ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
2289 f_report = 1;
2290 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
c3adcdf8 2291
f273ee37 2292 memset(cred, 0, strlen(cred));
2293 free(cred);
c3adcdf8 2294
f273ee37 2295 return ret;
2296}
2297
2298/* begin relaying via HTTP proxy
2299 Directs CONNECT method to proxy server to connect to
2300 destination host (and port). It may not be allowed on your
2301 proxy server.
2302 */
2303int
2304begin_http_relay( SOCKET s )
2305{
2306 char buf[1024];
2307 int result;
f273ee37 2308 char *auth_what;
2309
2310 debug("begin_http_relay()\n");
2311
f273ee37 2312 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
c3adcdf8 2313 return START_ERROR;
2314 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
2315 return START_ERROR;
f273ee37 2316 if (sendf(s,"\r\n") < 0)
c3adcdf8 2317 return START_ERROR;
2318
f273ee37 2319 /* get response */
2320 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
c3adcdf8 2321 debug("failed to read http response.\n");
2322 return START_ERROR;
f273ee37 2323 }
c3adcdf8 2324
f273ee37 2325 /* check status */
c3adcdf8 2326 if (!strchr(buf, ' ')) {
2327 error ("Unexpected http response: '%s'.\n", buf);
2328 return START_ERROR;
2329 }
f273ee37 2330 result = atoi(strchr(buf,' '));
c3adcdf8 2331
f273ee37 2332 switch ( result ) {
2333 case 200:
c3adcdf8 2334 /* Conguraturation, connected via http proxy server! */
2335 debug("connected, start user session.\n");
2336 break;
2337 case 302: /* redirect */
2338 do {
2339 if (line_input(s, buf, sizeof(buf)))
2340 break;
2341 downcase(buf);
2342 if (expect(buf, "Location: ")) {
2343 relay_host = cut_token(buf, "//");
2344 cut_token(buf, "/");
2345 relay_port = atoi(cut_token(buf, ":"));
2346 }
2347 } while (strcmp(buf,"\r\n") != 0);
2348 return START_RETRY;
f273ee37 2349
2350 /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
2351 * not strictly the correct response, but some proxies do send this (e.g.
2352 * Symantec's Raptor firewall) */
c3adcdf8 2353 case 401: /* WWW-Auth required */
2354 case 407: /* Proxy-Auth required */
2355 /** NOTE: As easy implementation, we support only BASIC scheme
2356 and ignore realm. */
2357 /* If proxy_auth_type is PROXY_AUTH_BASIC and get
2358 this result code, authentication was failed. */
2359 if (proxy_auth_type != PROXY_AUTH_NONE) {
2360 error("Authentication failed.\n");
2361 return START_ERROR;
2362 }
2363 auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
2364 do {
2365 if ( line_input(s, buf, sizeof(buf)) ) {
2366 break;
2367 }
2368 downcase(buf);
2369 if (expect(buf, auth_what)) {
2370 /* parse type and realm */
2371 char *scheme, *realm;
2372 scheme = cut_token(buf, " ");
2373 realm = cut_token(scheme, " ");
2374 if ( scheme == NULL || realm == NULL ) {
2375 debug("Invalid format of %s field.", auth_what);
2376 return START_ERROR; /* fail */
2377 }
2378 /* check supported auth type */
2379 if (expect(scheme, "basic")) {
2380 proxy_auth_type = PROXY_AUTH_BASIC;
2381 } else {
2382 debug("Unsupported authentication type: %s", scheme);
2383 }
2384 }
2385 } while (strcmp(buf,"\r\n") != 0);
2386 if ( proxy_auth_type == PROXY_AUTH_NONE ) {
2387 debug("Can't find %s in response header.", auth_what);
2388 return START_ERROR;
2389 } else {
2390 return START_RETRY;
2391 }
2392
f273ee37 2393 default:
c3adcdf8 2394 /* Not allowed */
2395 debug("http proxy is not allowed.\n");
2396 return START_ERROR;
f273ee37 2397 }
2398 /* skip to end of response header */
2399 do {
c3adcdf8 2400 if ( line_input(s, buf, sizeof(buf) ) ) {
2401 debug("Can't skip response headers\n");
2402 return START_ERROR;
2403 }
f273ee37 2404 } while ( strcmp(buf,"\r\n") != 0 );
2405
c3adcdf8 2406 return START_OK;
2407}
2408
2409/* begin relaying via TELNET proxy.
2410 Sends string specified by telnet_command (-c option) with
2411 replacing host name and port number to the socket. */
2412int
2413begin_telnet_relay( SOCKET s )
2414{
2415 char buf[1024];
2416 char *cmd;
2417 char *good_phrase = "connected to";
2418 char *bad_phrase_list[] = {
2419 " failed", " refused", " rejected", " closed"
2420 };
2421 char sep = ' ';
2422 int i;
2423
2424 debug("begin_telnet_relay()\n");
2425
2426 /* report phrase */
2427 debug("good phrase: '%s'\n", good_phrase);
2428 debug("bad phrases");
2429 sep = ':';
2430 for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
2431 debug_("%c '%s'", sep, bad_phrase_list[i]);
2432 sep = ',';
2433 }
2434 debug_("\n");
2435
2436 /* make request string with replacing %h by destination hostname
2437 and %p by port number, etc. */
2438 cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
2439
2440 /* Sorry, we send request string now without waiting a prompt. */
2441 if (sendf(s, "%s\r\n", cmd) < 0) {
2442 free(cmd);
2443 return START_ERROR;
2444 }
2445 free(cmd);
2446
2447 /* Process answer from proxy until good or bad phrase is detected. We
2448 assume that the good phrase should be appeared only in the final
2449 line of proxy responses. Bad keywods in the line causes operation
2450 fail. First checks a good phrase, then checks bad phrases.
2451 If no match, continue reading line from proxy. */
2452 while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
2453 downcase(buf);
2454 /* first, check good phrase */
2455 if (strstr(buf, good_phrase)) {
2456 debug("good phrase is detected: '%s'\n", good_phrase);
2457 return START_OK;
2458 }
2459 /* then, check bad phrase */
2460 for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) {
2461 if (strstr(buf, bad_phrase_list[i]) != NULL) {
2462 debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]);
2463 return START_ERROR;
2464 }
2465 }
2466 }
2467 debug("error reading from telnet proxy\n");
2468
2469 return START_ERROR;
f273ee37 2470}
2471
2472
2473#ifdef _WIN32
2474/* ddatalen()
2475 Returns 1 if data is available, otherwise return 0
2476 */
2477int
c3adcdf8 2478stdindatalen (void)
f273ee37 2479{
2480 DWORD len = 0;
2481 struct stat st;
2482 fstat( 0, &st );
c3adcdf8 2483 if ( st.st_mode & _S_IFIFO ) {
2484 /* in case of PIPE */
2485 if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
2486 NULL, 0, NULL, &len, NULL) ) {
2487 if ( GetLastError() == ERROR_BROKEN_PIPE ) {
2488 /* PIPE source is closed */
2489 /* read() will detects EOF */
2490 len = 1;
2491 } else {
2492 fatal("PeekNamedPipe() failed, errno=%d\n",
2493 GetLastError());
2494 }
2495 }
f273ee37 2496 } else if ( st.st_mode & _S_IFREG ) {
c3adcdf8 2497 /* in case of regular file (redirected) */
2498 len = 1; /* always data ready */
f273ee37 2499 } else if ( _kbhit() ) {
c3adcdf8 2500 /* in case of console */
2501 len = 1;
f273ee37 2502 }
2503 return len;
2504}
2505#endif /* _WIN32 */
2506
c3adcdf8 2507/* relay byte from stdin to socket and fro socket to stdout.
2508 returns reason of termination */
f273ee37 2509int
2510do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
2511{
2512 /** vars for local input data **/
c3adcdf8 2513 char lbuf[1024]; /* local input buffer */
2514 int lbuf_len; /* available data in lbuf */
2515 int f_local; /* read local input more? */
f273ee37 2516 /** vars for remote input data **/
c3adcdf8 2517 char rbuf[1024]; /* remote input buffer */
2518 int rbuf_len; /* available data in rbuf */
2519 int f_remote; /* read remote input more? */
2520 int close_reason = REASON_UNK; /* reason of end repeating */
f273ee37 2521 /** other variables **/
2522 int nfds, len;
c3adcdf8 2523 fd_set ifds, ofds;
f273ee37 2524 struct timeval *tmo;
2525#ifdef _WIN32
2526 struct timeval win32_tmo;
2527#endif /* _WIN32 */
c3adcdf8 2528
f273ee37 2529 /* repeater between stdin/out and socket */
2530 nfds = ((local_in<remote)? remote: local_in) +1;
c3adcdf8 2531 f_local = 1; /* yes, read from local */
2532 f_remote = 1; /* yes, read from remote */
f273ee37 2533 lbuf_len = 0;
2534 rbuf_len = 0;
2535
2536 while ( f_local || f_remote ) {
c3adcdf8 2537 FD_ZERO(&ifds );
2538 FD_ZERO(&ofds );
2539 tmo = NULL;
f273ee37 2540
c3adcdf8 2541 /** prepare for reading local input **/
2542 if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
f273ee37 2543#ifdef _WIN32
c3adcdf8 2544 if ( local_type != LOCAL_SOCKET ) {
2545 /* select() on Winsock is not accept standard handle.
2546 So use select() with short timeout and checking data
2547 in stdin by another method. */
2548 win32_tmo.tv_sec = 0;
2549 win32_tmo.tv_usec = 10*1000; /* 10 ms */
2550 tmo = &win32_tmo;
2551 } else
f273ee37 2552#endif /* !_WIN32 */
c3adcdf8 2553 FD_SET( local_in, &ifds );
2554 }
2555
2556 /** prepare for reading remote input **/
2557 if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
2558 FD_SET( remote, &ifds );
2559 }
2560
2561 /* FD_SET( local_out, ofds ); */
2562 /* FD_SET( remote, ofds ); */
2563
2564 if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
2565 /* some error */
2566 error( "select() failed, %d\n", socket_errno());
2567 return REASON_ERROR;
2568 }
f273ee37 2569#ifdef _WIN32
c3adcdf8 2570 /* fake ifds if local is stdio handle because
f273ee37 2571 select() of Winsock does not accept stdio
2572 handle. */
c3adcdf8 2573 if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
2574 FD_SET(0,&ifds); /* data ready */
f273ee37 2575#endif
2576
c3adcdf8 2577 /* remote => local */
2578 if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
2579 len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
2580 if ( len == 0 ) {
2581 debug("connection closed by peer\n");
2582 close_reason = REASON_CLOSED_BY_REMOTE;
2583 f_remote = 0; /* no more read from socket */
2584 f_local = 0;
2585 } else if ( len == -1 ) {
2586 if (socket_errno() != ECONNRESET) {
2587 /* error */
2588 fatal("recv() faield, %d\n", socket_errno());
2589 } else {
2590 debug("ECONNRESET detected\n");
2591 }
2592 } else {
2593 debug("recv %d bytes\n", len);
2594 if ( 1 < f_debug ) /* more verbose */
2595 report_bytes( "<<<", rbuf, rbuf_len);
2596 rbuf_len += len;
2597 }
2598 }
2599
2600 /* local => remote */
2601 if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) {
2602 if (local_type == LOCAL_SOCKET)
2603 len = recv(local_in, lbuf + lbuf_len,
2604 sizeof(lbuf)-lbuf_len, 0);
2605 else
2606 len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
2607 if ( len == 0 ) {
2608 /* stdin is EOF */
2609 debug("local input is EOF\n");
2610 if (!f_hold_session)
2611 shutdown(remote, 1); /* no-more writing */
2612 f_local = 0;
2613 close_reason = REASON_CLOSED_BY_LOCAL;
2614 } else if ( len == -1 ) {
2615 /* error on reading from stdin */
2616 if (f_hold_session) {
2617 debug ("failed to read from local\n");
2618 f_local = 0;
2619 close_reason = REASON_CLOSED_BY_LOCAL;
2620 } else
2621 fatal("recv() failed, errno = %d\n", errno);
2622 } else {
2623 /* repeat */
2624 lbuf_len += len;
2625 }
2626 }
2627
2628 /* flush data in buffer to socket */
2629 if ( 0 < lbuf_len ) {
2630 len = send(remote, lbuf, lbuf_len, 0);
2631 if ( 1 < f_debug ) /* more verbose */
2632 report_bytes( ">>>", lbuf, lbuf_len);
2633 if ( len == -1 ) {
2634 fatal("send() failed, %d\n", socket_errno());
2635 } else if ( 0 < len ) {
2636 /* move data on to top of buffer */
2637 debug("send %d bytes\n", len);
2638 lbuf_len -= len;
2639 if ( 0 < lbuf_len )
2640 memcpy( lbuf, lbuf+len, lbuf_len );
2641 assert( 0 <= lbuf_len );
2642 }
2643 }
2644
2645 /* flush data in buffer to local output */
2646 if ( 0 < rbuf_len ) {
2647 if (local_type == LOCAL_SOCKET)
2648 len = send( local_out, rbuf, rbuf_len, 0);
2649 else
2650 len = write( local_out, rbuf, rbuf_len);
2651 if ( len == -1 ) {
2652 fatal("output (local) failed, errno=%d\n", errno);
2653 }
2654 rbuf_len -= len;
2655 if ( len < rbuf_len )
2656 memcpy( rbuf, rbuf+len, rbuf_len );
2657 assert( 0 <= rbuf_len );
2658 }
2659 if (f_local == 0 && f_hold_session) {
2660 debug ("closing local port without disconnecting from remote\n");
2661 f_remote = 0;
2662 shutdown (local_out, 2);
2663 close (local_out);
2664 break;
2665 }
2666 }
2667
2668 return close_reason;
f273ee37 2669}
2670
f273ee37 2671int
2672accept_connection (u_short port)
2673{
c3adcdf8 2674 static int sock = -1;
f273ee37 2675 int connection;
2676 struct sockaddr_in name;
f273ee37 2677 struct sockaddr client;
2678 int socklen;
c3adcdf8 2679 fd_set ifds;
2680 int nfds;
2681 int sockopt;
f273ee37 2682
2683 /* Create the socket. */
2684 debug("Creating source port to forward.\n");
2685 sock = socket (PF_INET, SOCK_STREAM, 0);
2686 if (sock < 0)
c3adcdf8 2687 fatal("socket() failed, errno=%d\n", socket_errno());
2688 sockopt = 1;
2689 setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
2690 (void*)&sockopt, sizeof(sockopt));
f273ee37 2691
2692 /* Give the socket a name. */
2693 name.sin_family = AF_INET;
2694 name.sin_port = htons (port);
2695 name.sin_addr.s_addr = htonl (INADDR_ANY);
2696 if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
c3adcdf8 2697 fatal ("bind() failed, errno=%d\n", socket_errno());
f273ee37 2698
2699 if (listen( sock, 1) < 0)
c3adcdf8 2700 fatal ("listen() failed, errno=%d\n", socket_errno());
f273ee37 2701
c3adcdf8 2702 /* wait for new connection with watching EOF of stdin. */
2703 debug ("waiting new connection at port %d (socket=%d)\n", port, sock);
2704 nfds = sock + 1;
2705 do {
2706 int n;
2707 struct timeval *ptmo = NULL;
2708#ifdef _WIN32
2709 struct timeval tmo;
2710 tmo.tv_sec = 0;
2711 tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */
2712 ptmo = &tmo;
2713#endif /* _WIN32 */
2714 FD_ZERO (&ifds);
2715 FD_SET ((SOCKET)sock, &ifds);
2716#ifndef _WIN32
2717 FD_SET (0, &ifds); /* watch stdin */
2718#endif
2719 n = select (nfds, &ifds, NULL, NULL, ptmo);
2720 if (n == -1) {
2721 fatal ("select() failed, %d\n", socket_errno());
2722 exit (1);
2723 }
2724#ifdef _WIN32
2725 if (0 < stdindatalen()) {
2726 FD_SET (0, &ifds); /* fake */
2727 n++;
2728 }
2729#endif
2730 if (0 < n) {
2731 if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
2732 /* EOF */
2733 debug ("Give-up waiting port because stdin is closed.");
2734 exit(0);
2735 }
2736 if (FD_ISSET(sock, &ifds))
2737 break; /* socket is stimulated */
2738 }
2739 } while (1);
f273ee37 2740 socklen = sizeof(client);
2741 connection = accept( sock, &client, &socklen);
2742 if ( connection < 0 )
c3adcdf8 2743 fatal ("accept() failed, errno=%d\n", socket_errno());
f273ee37 2744 return connection;
2745}
2746
2747
2748
2749/** Main of program **/
2750int
2751main( int argc, char **argv )
2752{
2753 int ret;
c3adcdf8 2754 int remote; /* socket */
2755 int local_in; /* Local input */
2756 int local_out; /* Local output */
2757 int reason;
f273ee37 2758#ifdef _WIN32
2759 WSADATA wsadata;
2760 WSAStartup( 0x101, &wsadata);
2761#endif /* _WIN32 */
2762
2763 /* initialization */
2764 make_revstr();
2765 getarg( argc, argv );
2766 debug("Program is $Revision$\n");
2767
2768 /* Open local_in and local_out if forwarding a port */
2769 if ( local_type == LOCAL_SOCKET ) {
c3adcdf8 2770 /* Relay between local port and destination */
f273ee37 2771 local_in = local_out = accept_connection( local_port );
2772 } else {
c3adcdf8 2773 /* Relay between stdin/stdout and desteination */
2774 local_in = 0;
2775 local_out = 1;
f273ee37 2776#ifdef _WIN32
c3adcdf8 2777 _setmode(local_in, O_BINARY);
2778 _setmode(local_out, O_BINARY);
f273ee37 2779#endif
2780 }
2781
2782retry:
2783#ifndef _WIN32
2784 if (0 < connect_timeout)
c3adcdf8 2785 set_timeout (connect_timeout);
f273ee37 2786#endif /* not _WIN32 */
2787
2788 /* make connection */
2789 if ( relay_method == METHOD_DIRECT ) {
c3adcdf8 2790 remote = open_connection (dest_host, dest_port);
2791 if ( remote == SOCKET_ERROR )
2792 fatal( "Unable to connect to destination host, errno=%d\n",
2793 socket_errno());
f273ee37 2794 } else {
c3adcdf8 2795 remote = open_connection (relay_host, relay_port);
2796 if ( remote == SOCKET_ERROR )
2797 fatal( "Unable to connect to relay host, errno=%d\n",
2798 socket_errno());
f273ee37 2799 }
2800
2801 /** resolve destination host (SOCKS) **/
c3adcdf8 2802#if !defined(_WIN32) && !defined(__CYGWIN32__)
2803 if (socks_ns.sin_addr.s_addr != 0)
2804 switch_ns (&socks_ns);
2805#endif /* not _WIN32 && not __CYGWIN32__ */
f273ee37 2806 if (relay_method == METHOD_SOCKS &&
c3adcdf8 2807 socks_resolve == RESOLVE_LOCAL &&
2808 local_resolve (dest_host, &dest_addr) < 0) {
2809 fatal("Unknown host: %s", dest_host);
f273ee37 2810 }
2811
2812 /** relay negociation **/
2813 switch ( relay_method ) {
2814 case METHOD_SOCKS:
c3adcdf8 2815 if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
2816 ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
2817 fatal( "failed to begin relaying via SOCKS.\n");
2818 break;
f273ee37 2819
2820 case METHOD_HTTP:
c3adcdf8 2821 ret = begin_http_relay(remote);
2822 switch (ret) {
2823 case START_ERROR:
2824 close (remote);
2825 fatal("failed to begin relaying via HTTP.\n");
2826 case START_OK:
2827 break;
2828 case START_RETRY:
2829 /* retry with authentication */
2830 close (remote);
2831 goto retry;
2832 }
2833 break;
2834 case METHOD_TELNET:
2835 if (begin_telnet_relay(remote) < 0)
2836 fatal("failed to begin relaying via telnet.\n");
2837 break;
f273ee37 2838 }
2839 debug("connected\n");
2840
2841#ifndef _WIN32
2842 if (0 < connect_timeout)
c3adcdf8 2843 set_timeout (0);
f273ee37 2844#endif /* not _WIN32 */
2845
2846 /* main loop */
2847 debug ("start relaying.\n");
c3adcdf8 2848do_repeater:
2849 reason = do_repeater(local_in, local_out, remote);
f273ee37 2850 debug ("relaying done.\n");
c3adcdf8 2851 if (local_type == LOCAL_SOCKET &&
2852 reason == REASON_CLOSED_BY_LOCAL &&
2853 f_hold_session) {
2854 /* re-wait at local port without closing remote session */
2855 debug ("re-waiting at local port %d\n", local_port);
2856 local_in = local_out = accept_connection( local_port );
2857 debug ("re-start relaying\n");
2858 goto do_repeater;
2859 }
f273ee37 2860 closesocket(remote);
2861 if ( local_type == LOCAL_SOCKET)
c3adcdf8 2862 closesocket(local_in);
f273ee37 2863#ifdef _WIN32
2864 WSACleanup();
2865#endif /* _WIN32 */
2866 debug ("that's all, bye.\n");
2867
2868 return 0;
2869}
2870
2871/* ------------------------------------------------------------
2872 Local Variables:
2873 compile-command: "cc connect.c -o connect"
c3adcdf8 2874 tab-width: 8
f273ee37 2875 fill-column: 74
2876 comment-column: 48
2877 End:
2878 ------------------------------------------------------------ */
2879
2880/*** end of connect.c ***/
This page took 0.409736 seconds and 4 git commands to generate.