]> git.pld-linux.org Git - packages/connect.git/blame - connect.c
e1c3cbed88f08ea778d90813d48cd428 connect.c
[packages/connect.git] / connect.c
CommitLineData
19fcfc91 1/***********************************************************************
2 * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
3 *
239edf7c 4 * Copyright (c) 2000-2004 Shun-ichi Goto
19fcfc91 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
239edf7c 32 * http://www.taiyo.co.jp/~gotoh/ssh/connect.c
19fcfc91 33 *
34 * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
35 * is available:
239edf7c 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
19fcfc91 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 *
239edf7c 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 *
19fcfc91 62 * How To Use
63 * ==========
64 *
65 * You can specify proxy method in an environment variable or in a
66 * command line option.
67 *
239edf7c 68 * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
19fcfc91 69 * [-H [user@]proxy-server[:port]]
70 * [-S [user@]socks-server[:port]]
239edf7c 71 * [-T proxy-server[:port]]
72 * [-c telnet proxy command]
19fcfc91 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 *
239edf7c 100 * The '-p' option will forward a local TCP port instead of using the
19fcfc91 101 * standard input and output.
102 *
239edf7c 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 *
19fcfc91 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
239edf7c 165 * or system login name. And password is specified from environment
19fcfc91 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 * ===================
239edf7c 174 *
19fcfc91 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>
239edf7c 239#if !defined(_WIN32) && !defined(__CYGWIN32__)
240#define WITH_RESOLVER 1
19fcfc91 241#include <arpa/nameser.h>
242#include <resolv.h>
239edf7c 243#else /* not ( not _WIN32 && not __CYGWIN32__) */
244#undef WITH_RESOLVER
245#endif /* not ( not _WIN32 && not __CYGWIN32__) */
19fcfc91 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
239edf7c 262#define _setmode setmode
19fcfc91 263#endif
264
265/* help message.
239edf7c 266 Win32 environment does not support -R option (vc and cygwin)
267 Win32 native compilers does not support -w option, yet (vc)
19fcfc91 268*/
239edf7c 269static char *usage = "usage: %s [-dnhst45] [-p local-port]"
19fcfc91 270#ifdef _WIN32
19fcfc91 271#ifdef __CYGWIN32__
239edf7c 272"[-w timeout] \n" /* cygwin cannot -R */
19fcfc91 273#else /* not __CYGWIN32__ */
239edf7c 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"
19fcfc91 279#endif /* not _WIN32 */
280" [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
239edf7c 281" [-T proxy-server[:port]]\n"
282" [-c telnet-proxy-command]\n"
19fcfc91 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
239edf7c 291/* set of character for strspn() */
292const char *digits = "0123456789";
293const char *dotdigits = "0123456789.";
294
19fcfc91 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 */
239edf7c 304#define LOCAL_STDIO 0
305#define LOCAL_SOCKET 1
19fcfc91 306char *local_type_names[] = { "stdio", "socket" };
307int local_type = LOCAL_STDIO;
239edf7c 308u_short local_port = 0; /* option 'p' */
309int f_hold_session = 0; /* option 'P' */
310
311char *telnet_command = "telnet %h %p";
19fcfc91 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
239edf7c 324#define METHOD_TELNET 4
325char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
19fcfc91 326
239edf7c 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 */
19fcfc91 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 */
239edf7c 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 */
19fcfc91 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 */
239edf7c 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 */
19fcfc91 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
239edf7c 391int socks_version = 5; /* SOCKS protocol version */
19fcfc91 392int socks_resolve = RESOLVE_UNKNOWN;
393struct sockaddr_in socks_ns;
394char *socks5_auth = NULL;
395
396/* Environment variable names */
239edf7c 397#define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */
19fcfc91 398#define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
399#define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
400
239edf7c 401#define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */
19fcfc91 402#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
403#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
404
239edf7c 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 */
19fcfc91 410
239edf7c 411#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
19fcfc91 412#define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */
413#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
414
239edf7c 415#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
19fcfc91 416
239edf7c 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 */
19fcfc91 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"
239edf7c 427#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
19fcfc91 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
239edf7c 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
19fcfc91 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
19fcfc91 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 ) {
239edf7c 475 va_start( args, fmt );
476 fprintf(stderr, "DEBUG: ");
477 vfprintf( stderr, fmt, args );
478 va_end( args );
19fcfc91 479 }
480}
481
482void
239edf7c 483debug_( const char *fmt, ... ) /* without prefix */
19fcfc91 484{
485 va_list args;
486 if ( f_debug ) {
239edf7c 487 va_start( args, fmt );
488 vfprintf( stderr, fmt, args );
489 va_end( args );
19fcfc91 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
239edf7c 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
19fcfc91 525void
526downcase( char *buf )
527{
528 while ( *buf ) {
239edf7c 529 if ( isupper(*buf) )
530 *buf += 'a'-'A';
531 buf++;
19fcfc91 532 }
533}
534
239edf7c 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
19fcfc91 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 )
239edf7c 598 ret = RESOLVE_BOTH;
19fcfc91 599 else if ( strcmp( buf, "remote" ) == 0 )
239edf7c 600 ret = RESOLVE_REMOTE;
19fcfc91 601 else if ( strcmp( buf, "local" ) == 0 )
239edf7c 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;
19fcfc91 610 }
611 else
239edf7c 612 ret = RESOLVE_UNKNOWN;
19fcfc91 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 )
239edf7c 629 fatal("getpwuid() failed for uid: %d\n", getuid());
19fcfc91 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-- ) {
239edf7c 643 if ( toupper(*str) != toupper(*substr) )
644 return 0; /* not matched */
645 str++, substr++;
19fcfc91 646 }
239edf7c 647 return 1; /* good, matched */
19fcfc91 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 },
239edf7c 666 { ENV_SOCKS5_PASSWD, NULL },
19fcfc91 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*
239edf7c 684find_parameter_item(const char* name)
685{
19fcfc91 686 int i;
687 for( i = 0; parameter_table[i].name != NULL; i++ ){
239edf7c 688 if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
689 return &parameter_table[i];
19fcfc91 690 }
691 return NULL;
692}
693
694void
239edf7c 695read_parameter_file_1(const char* name)
696{
19fcfc91 697 FILE* f;
698 int line;
699 char lbuf[1025];
700 f = fopen(name, "r");
701 if( f ){
239edf7c 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 }
19fcfc91 742 }
743}
744
745void
239edf7c 746read_parameter_file(void)
747{
19fcfc91 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 )
239edf7c 757 fatal("getpwuid() failed for uid: %d\n", getuid());
758 name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
19fcfc91 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*
239edf7c 767getparam(const char* name)
768{
19fcfc91 769 char *value = getenv(name);
770 if ( value == NULL ){
239edf7c 771 PARAMETER_ITEM *item = find_parameter_item(name);
772 if ( item != NULL )
773 value = item->value;
19fcfc91 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-- )
239edf7c 798 *a++ &= *m++;
19fcfc91 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 ) {
239edf7c 807 error("direct address table is full!\n");
808 return -1;
19fcfc91 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,
239edf7c 816 &iaddr, sizeof(iaddr));
19fcfc91 817 memcpy( &direct_addr_list[n_direct_addr_list].mask,
239edf7c 818 mask, sizeof(*mask));
19fcfc91 819 direct_addr_list[n_direct_addr_list].negative = negative;
820 n_direct_addr_list++;
821 return 0;
822}
823
239edf7c 824int
19fcfc91 825parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
826{
239edf7c 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.
19fcfc91 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;
239edf7c 838
19fcfc91 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++ ) {
239edf7c 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;
19fcfc91 859 }
860 /* At this point, *ptr points '/' or EOS ('\0') */
861 if ( *ptr == '\0' )
239edf7c 862 return 0; /* complete as format #3 */
19fcfc91 863 if ( *ptr != '/' )
239edf7c 864 return -1; /* format error */
19fcfc91 865 /* Now parse mask for format #1 or #2 */
866 ptr++;
239edf7c 867 mask->s_addr = 0; /* clear automatic mask */
868
19fcfc91 869 if ( strchr( ptr, '.') ) {
239edf7c 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 */
19fcfc91 884 } else {
239edf7c 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 */
19fcfc91 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 ){
239edf7c 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);
19fcfc91 913 } else if ( relay_method == METHOD_HTTP ){
239edf7c 914 env = getparam(ENV_HTTP_DIRECT);
19fcfc91 915 }
916
917 if ( env == NULL )
239edf7c 918 env = getparam(ENV_CONNECT_DIRECT);
919
19fcfc91 920 if ( env == NULL )
239edf7c 921 return; /* no entry */
922 debug("making direct addr list from: '%s'\n", env);
923 env = strdup( env ); /* reallocate to modify */
19fcfc91 924 beg = next = env;
925 n_entries = 0;
926 do {
239edf7c 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;
19fcfc91 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
239edf7c 960int
961is_direct_address (const struct sockaddr_in *addr)
19fcfc91 962{
963 int i;
964 struct in_addr saddr, iaddr;
965
966 saddr = addr->sin_addr;
239edf7c 967
19fcfc91 968 /* Note: assume IPV4 address !! */
969 for (i=0; i<n_direct_addr_list; i++ ) {
239edf7c 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 }
19fcfc91 986 }
987 debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
239edf7c 988 return 0; /* not direct */
19fcfc91 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{
239edf7c 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 */
19fcfc91 1009 static int disabled = 0;
1010
1011 if ( disabled && enable ) {
239edf7c 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 }
19fcfc91 1025 } else if (!disabled && !enable) {
239edf7c 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 }
19fcfc91 1041 }
239edf7c 1042
19fcfc91 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 ) {
239edf7c 1054 error("Unable to open %s\n", TTY_NAME);
1055 return -1; /* can't open tty */
19fcfc91 1056 }
1057 if ( size <= 0 )
239edf7c 1058 return -1; /* no room */
19fcfc91 1059 write(tty, prompt, strlen(prompt));
1060 buf[0] = '\0';
239edf7c 1061 tty_change_echo(tty, 0); /* disable echo */
19fcfc91 1062 ret = read(tty,buf, size-1);
239edf7c 1063 tty_change_echo(tty, 1); /* restore */
1064 write(tty, "\n", 1); /* new line */
19fcfc91 1065 close(tty);
1066 if ( strchr(buf,'\n') == NULL )
239edf7c 1067 return -1;
19fcfc91 1068 if ( 0 < ret )
239edf7c 1069 buf[ret] = '\0';
19fcfc91 1070 return ret;
1071}
1072
1073#else /* _WIN32 */
1074
1075BOOL __stdcall
1076w32_intr_handler(DWORD dwCtrlType)
1077{
1078 if ( dwCtrlType == CTRL_C_EVENT ) {
239edf7c 1079 intr_flag = 1;
1080 return TRUE;
19fcfc91 1081 } else {
239edf7c 1082 return FALSE;
19fcfc91 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,
239edf7c 1091 0, NULL, OPEN_EXISTING, 0, NULL);
19fcfc91 1092 HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
239edf7c 1093 0, NULL, OPEN_EXISTING, 0, NULL);
19fcfc91 1094 DWORD mode;
239edf7c 1095 DWORD ret, bytes;
19fcfc91 1096
1097 if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
239edf7c 1098 fatal("Cannot open console. (errno=%d)", GetLastError());
19fcfc91 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);
239edf7c 1105 SetConsoleMode(in, mode); /* enable echo */
19fcfc91 1106 SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
1107 if ( intr_flag )
239edf7c 1108 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
19fcfc91 1109 WriteFile(out,"\n", 1, &bytes, 0);
1110 CloseHandle(in);
1111 CloseHandle(out);
1112 return ret;
1113}
1114
1115#endif /* _WIN32 */
1116
239edf7c 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
19fcfc91 1199/*** network operations ***/
1200
239edf7c 1201
19fcfc91 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;
239edf7c 1217
19fcfc91 1218 relay_method = method;
1219
1220 read_parameter_file();
1221 initialize_direct_addr();
1222 if (n_direct_addr_list == 0) {
239edf7c 1223 debug ("(none)\n");
19fcfc91 1224 } else {
239edf7c 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 }
19fcfc91 1236 }
1237
1238 switch ( method ) {
1239 case METHOD_DIRECT:
239edf7c 1240 return -1; /* nothing to do */
1241
19fcfc91 1242 case METHOD_SOCKS:
239edf7c 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
19fcfc91 1280 case METHOD_HTTP:
239edf7c 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 */
19fcfc91 1293 }
239edf7c 1294
19fcfc91 1295 if (expect( spec, HTTP_PROXY_PREFIX)) {
239edf7c 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';
19fcfc91 1300 } else {
239edf7c 1301 /* assume spec is aready "server:port" format */
1302 buf = strdup( spec );
19fcfc91 1303 }
1304 spec = buf;
239edf7c 1305
1306 /* check username in spec */
19fcfc91 1307 sep = strchr( spec, '@' );
1308 if ( sep != NULL ) {
239edf7c 1309 *sep = '\0';
1310 relay_user = strdup( spec );
1311 spec = sep +1;
19fcfc91 1312 }
239edf7c 1313 if (relay_user == NULL)
1314 relay_user = determine_relay_user();
1315
19fcfc91 1316 /* split out hostname and port number from spec */
1317 sep = strchr(spec,':');
1318 if ( sep == NULL ) {
239edf7c 1319 /* hostname only, port is already set as default */
1320 relay_host = strdup( spec );
19fcfc91 1321 } else {
239edf7c 1322 /* hostname and port */
1323 relay_port = atoi(sep+1);
1324 *sep = '\0';
1325 relay_host = strdup( spec );
19fcfc91 1326 }
1327 free(buf);
1328 return 0;
1329}
1330
1331
1332u_short
1333resolve_port( const char *service )
1334{
1335 int port;
239edf7c 1336 if ( service[strspn (service, digits)] == '\0' ) {
1337 /* all digits, port number */
1338 port = atoi(service);
19fcfc91 1339 } else {
239edf7c 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 }
19fcfc91 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) {
239edf7c 1361 revstr = strdup("unknown");
1362 return;
19fcfc91 1363 }
1364 ptr += 2;
239edf7c 1365 len = strspn(ptr, dotdigits);
19fcfc91 1366 if (0 < len) {
239edf7c 1367 revstr = xmalloc(len+1);
1368 memcpy(revstr, ptr, len);
1369 revstr[len] = '\0';
19fcfc91 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;
239edf7c 1379
19fcfc91 1380 progname = *argv;
1381 argc--, argv++;
1382
1383 /* check optinos */
1384 while ( (0 < argc) && (**argv == '-') ) {
239edf7c 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;
19fcfc91 1458
239edf7c 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;
19fcfc91 1469#endif /* not _WIN32 */
1470
239edf7c 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++;
19fcfc91 1514 }
239edf7c 1515
19fcfc91 1516 /* check error */
1517 if ( 0 < err )
239edf7c 1518 goto quit;
1519
19fcfc91 1520 set_relay( method, server );
1521
1522 /* check destination HOST (MUST) */
1523 if ( argc == 0 ) {
239edf7c 1524 fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
1525 fprintf(stderr, usage, progname);
1526 exit(0);
19fcfc91 1527 }
1528 dest_host = argv[0];
1529 /* decide port or service name from programname or argument */
1530 if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
239edf7c 1531 ((ptr=strchr( progname, '\\')) != NULL) )
1532 ptr++;
19fcfc91 1533 else
239edf7c 1534 ptr = progname;
19fcfc91 1535 if ( dest_port == 0 ) {
239edf7c 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 }
19fcfc91 1548 }
1549 /* check port number */
1550 if ( dest_port <= 0 ) {
239edf7c 1551 error( "You must specify the destination port correctly.\n");
1552 err++;
1553 goto quit;
19fcfc91 1554 }
1555 if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
239edf7c 1556 error("Invalid relay port: %d\n", dest_port);
1557 err++;
1558 goto quit;
19fcfc91 1559 }
1560
1561quit:
1562 /* report for debugging */
1563 debug("relay_method = %s (%d)\n",
239edf7c 1564 method_names[relay_method], relay_method);
19fcfc91 1565 if ( relay_method != METHOD_DIRECT ) {
239edf7c 1566 debug("relay_host=%s\n", relay_host);
1567 debug("relay_port=%d\n", relay_port);
1568 debug("relay_user=%s\n", relay_user);
19fcfc91 1569 }
1570 if ( relay_method == METHOD_SOCKS ) {
239edf7c 1571 debug("socks_version=%d\n", socks_version);
1572 debug("socks_resolve=%s (%d)\n",
1573 resolve_names[socks_resolve], socks_resolve);
19fcfc91 1574 }
1575 debug("local_type=%s\n", local_type_names[local_type]);
239edf7c 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 }
19fcfc91 1581 debug("dest_host=%s\n", dest_host);
1582 debug("dest_port=%d\n", dest_port);
1583 if ( 0 < err ) {
239edf7c 1584 fprintf(stderr, usage, progname);
1585 exit(1);
19fcfc91 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
239edf7c 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__ */
19fcfc91 1630
239edf7c 1631/* TODO: IPv6
1632 TODO: fallback if askpass execution failed.
1633 */
19fcfc91 1634
1635int
239edf7c 1636local_resolve (const char *host, struct sockaddr_in *addr)
19fcfc91 1637{
1638 struct hostent *ent;
239edf7c 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);
19fcfc91 1643 } else {
239edf7c 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 */
19fcfc91 1657}
1658
239edf7c 1659int
19fcfc91 1660open_connection( const char *host, u_short port )
1661{
1662 SOCKET s;
1663 struct sockaddr_in saddr;
1664
1665 if ( relay_method == METHOD_DIRECT ) {
239edf7c 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;
19fcfc91 1674 } else {
239edf7c 1675 host = relay_host;
1676 port = relay_port;
19fcfc91 1677 }
1678
239edf7c 1679 if (local_resolve (host, &saddr) < 0) {
1680 error("can't resolve hostname: %s\n", host);
1681 return SOCKET_ERROR;
19fcfc91 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))
239edf7c 1688 == SOCKET_ERROR) {
1689 debug( "connect() failed.\n");
1690 return SOCKET_ERROR;
19fcfc91 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 )
239edf7c 1702 return;
19fcfc91 1703 if ( !f_report )
239edf7c 1704 return; /* don't report */
19fcfc91 1705 debug("%s \"", prefix);
1706 while ( *buf ) {
239edf7c 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);
19fcfc91 1727 }
239edf7c 1728
19fcfc91 1729 debug_("\"\n");
1730}
1731
1732
1733void
1734report_bytes( char *prefix, char *buf, int len )
1735{
1736 if ( ! f_debug )
239edf7c 1737 return;
19fcfc91 1738 debug( "%s", prefix );
1739 while ( 0 < len ) {
239edf7c 1740 fprintf( stderr, " %02x", *(unsigned char *)buf);
1741 buf++;
1742 len--;
19fcfc91 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 ) {
239edf7c 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;
19fcfc91 1763 }
1764 if (!f_report) {
239edf7c 1765 debug("atomic_out() [some bytes]\n");
1766 debug(">>> xx xx xx xx ...\n");
19fcfc91 1767 } else {
239edf7c 1768 debug("atomic_out() [%d bytes]\n", ret);
1769 report_bytes(">>>", buf, ret);
19fcfc91 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 );
239edf7c 1781
19fcfc91 1782 /* do atomic in */
1783 ret = 0;
1784 while ( 0 < size ) {
239edf7c 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;
19fcfc91 1793 }
1794 if (!f_report) {
239edf7c 1795 debug("atomic_in() [some bytes]\n");
1796 debug("<<< xx xx xx xx ...\n");
19fcfc91 1797 } else {
239edf7c 1798 debug("atomic_in() [%d bytes]\n", ret);
1799 report_bytes("<<<", buf, ret);
19fcfc91 1800 }
1801 return ret;
1802}
1803
1804int
1805line_input( SOCKET s, char *buf, int size )
1806{
1807 char *dst = buf;
1808 if ( size == 0 )
239edf7c 1809 return 0; /* no error */
19fcfc91 1810 size--;
1811 while ( 0 < size ) {
239edf7c 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 }
19fcfc91 1830 }
1831 *dst = '\0';
1832 report_text( "<<<", buf);
1833 return 0;
1834}
1835
19fcfc91 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 )
239edf7c 1848 return NULL;
19fcfc91 1849 while ( ptr < end )
239edf7c 1850 *ptr++ = '\0';
19fcfc91 1851 return ptr;
1852}
1853
1854const char *
1855lookup(int num, LOOKUP_ITEM *items)
1856{
1857 int i = 0;
1858 while (0 <= items[i].num) {
239edf7c 1859 if (items[i].num == num)
1860 return items[i].str;
1861 i++;
19fcfc91 1862 }
1863 return "(unknown)";
1864}
1865
1866/* readpass()
239edf7c 1867 password input routine
19fcfc91 1868 Use ssh-askpass (same mechanism to OpenSSH)
1869*/
1870char *
1871readpass( const char* prompt, ...)
1872{
239edf7c 1873 static char buf[1000]; /* XXX, don't be fix length */
19fcfc91 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__)
239edf7c 1881 && getenv("DISPLAY")
19fcfc91 1882#endif /* not _WIN32 && not __CYGWIN32__ */
239edf7c 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);
19fcfc91 1897 } else {
239edf7c 1898 tty_readpass( buf, buf, sizeof(buf));
19fcfc91 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;
239edf7c 1908 char *pass = NULL;
19fcfc91 1909 int len;
239edf7c 1910
19fcfc91 1911 /* do User/Password authentication. */
239edf7c 1912 /* This feature requires username and password from
19fcfc91 1913 command line argument or environment variable,
1914 or terminal. */
239edf7c 1915 if (relay_user == NULL)
1916 fatal("cannot determine user name.\n");
19fcfc91 1917
1918 /* get password from environment variable if exists. */
239edf7c 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);
19fcfc91 1923
1924 /* make authentication packet */
1925 ptr = buf;
239edf7c 1926 PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */
1927 len = strlen( relay_user ); /* ULEN and UNAME */
19fcfc91 1928 PUT_BYTE( ptr++, len );
1929 strcpy( ptr, relay_user );
1930 ptr += len;
239edf7c 1931 len = strlen( pass ); /* PLEN and PASSWD */
19fcfc91 1932 PUT_BYTE( ptr++, strlen(pass));
1933 strcpy( ptr, pass );
1934 ptr += len;
239edf7c 1935 memset (pass, 0, strlen(pass)); /* erase password */
1936
19fcfc91 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 )
239edf7c 1945 return 0; /* success */
19fcfc91 1946 else
239edf7c 1947 return -1; /* fail */
19fcfc91 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++ )
239edf7c 1982 if ( *start != ' ' && *start != '\t') break;
19fcfc91 1983 for ( end--; end >= start; end-- ) {
239edf7c 1984 if ( *end != ' ' && *end != '\t'){
1985 end++;
1986 break;
1987 }
19fcfc91 1988 }
1989 len = end - start;
1990 for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
239edf7c 1991 if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
1992 return socks5_auth_table[i].auth;
1993 }
19fcfc91 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 ) {
239edf7c 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 }
19fcfc91 2011 }
2012 if ( *start && ( i < max_auth ) ){
239edf7c 2013 for( end = start; *end; end++ );
2014 auth_list[i++] = socks5_auth_parse_1(start, end);
19fcfc91 2015 } else {
239edf7c 2016 fatal("Too much auth method.\n");
19fcfc91 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");
239edf7c 2032
19fcfc91 2033 /* request authentication */
2034 ptr = buf;
239edf7c 2035 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
19fcfc91 2036
2037 if ( env == NULL )
239edf7c 2038 env = getparam(ENV_SOCKS5_AUTH);
19fcfc91 2039 if ( env == NULL ) {
239edf7c 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;
19fcfc91 2044 } else {
239edf7c 2045 n_auth = socks5_auth_parse(env, auth_list, 10);
19fcfc91 2046 }
239edf7c 2047 PUT_BYTE( ptr++, n_auth); /* num auth */
19fcfc91 2048 for (i=0; i<n_auth; i++) {
239edf7c 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 */
19fcfc91 2052 }
239edf7c 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;
19fcfc91 2059 }
2060 auth_method = buf[1];
2061
2062 debug("auth method: %s\n", socks5_getauthname(auth_method));
239edf7c 2063
19fcfc91 2064 switch ( auth_method ) {
2065 case SOCKS5_AUTH_REJECT:
239edf7c 2066 error("No acceptable authentication method\n");
2067 return -1; /* fail */
2068
19fcfc91 2069 case SOCKS5_AUTH_NOAUTH:
239edf7c 2070 /* nothing to do */
2071 auth_result = 0;
2072 break;
19fcfc91 2073
2074 case SOCKS5_AUTH_USERPASS:
239edf7c 2075 auth_result = socks5_do_auth_userpass(s);
2076 break;
2077
19fcfc91 2078 default:
239edf7c 2079 error("Unsupported authentication method: %s\n",
2080 socks5_getauthname( auth_method ));
2081 return -1; /* fail */
19fcfc91 2082 }
2083 if ( auth_result != 0 ) {
239edf7c 2084 error("Authentication failed.\n");
2085 return -1;
19fcfc91 2086 }
2087 /* request to connect */
2088 ptr = buf;
239edf7c 2089 PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
2090 PUT_BYTE( ptr++, 1); /* CMD: CONNECT */
2091 PUT_BYTE( ptr++, 0); /* FLG: 0 */
19fcfc91 2092 if ( dest_addr.sin_addr.s_addr == 0 ) {
239edf7c 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;
19fcfc91 2099 } else {
239edf7c 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);
19fcfc91 2104 }
239edf7c 2105 PUT_BYTE( ptr++, dest_port>>8); /* DST.PORT */
19fcfc91 2106 PUT_BYTE( ptr++, dest_port&0xFF);
239edf7c 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;
19fcfc91 2113 }
2114 ptr = buf + 4;
239edf7c 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;
19fcfc91 2126 }
239edf7c 2127
19fcfc91 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.
239edf7c 2151
19fcfc91 2152 1. If server spec has user@hostname:port format then
2153 user part is used for this SOCKS server.
239edf7c 2154
19fcfc91 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");
239edf7c 2165
2166 /* make connect request packet
19fcfc91 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;
239edf7c 2173 PUT_BYTE( ptr++, 4); /* protocol version (4) */
2174 PUT_BYTE( ptr++, 1); /* CONNECT command */
2175 PUT_BYTE( ptr++, dest_port>>8); /* destination Port */
19fcfc91 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 )
239edf7c 2181 *(ptr-1) = 1; /* fake, protocol 4a */
19fcfc91 2182 /* username */
239edf7c 2183 if (relay_user == NULL)
2184 fatal( "Cannot determine user name.\n");
19fcfc91 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)) {
239edf7c 2189 strcpy( ptr, dest_host );
2190 ptr += strlen( dest_host ) +1;
19fcfc91 2191 }
2192 /* send command and get response
2193 response is: VN:1, CD:1, PORT:2, ADDR:4 */
239edf7c 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 */
19fcfc91 2200 }
239edf7c 2201
19fcfc91 2202 /* Conguraturation, connected via SOCKS4 server! */
2203 return 0;
2204}
2205
2206int
2207sendf(SOCKET s, const char *fmt,...)
2208{
239edf7c 2209 static char buf[10240]; /* xxx, enough? */
2210
19fcfc91 2211 va_list args;
2212 va_start( args, fmt );
2213 vsprintf( buf, fmt, args );
2214 va_end( args );
239edf7c 2215
19fcfc91 2216 report_text(">>>", buf);
2217 if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
239edf7c 2218 debug("failed to send http request. errno=%d\n", socket_errno());
2219 return -1;
19fcfc91 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;
239edf7c 2237 buf = xmalloc(dst_len+1);
19fcfc91 2238 bits = data = 0;
2239 src = (unsigned char *)str;
2240 dst = (unsigned char *)buf;
2241 while ( dst_len-- ) {
239edf7c 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;
19fcfc91 2250 }
2251 *dst = '\0';
2252 /* fix-up tail padding */
2253 switch ( src_len%3 ) {
2254 case 1:
239edf7c 2255 *--dst = '=';
19fcfc91 2256 case 2:
239edf7c 2257 *--dst = '=';
19fcfc91 2258 }
2259 return buf;
2260}
2261
2262
2263int
239edf7c 2264basic_auth (SOCKET s)
19fcfc91 2265{
2266 char *userpass;
2267 char *cred;
239edf7c 2268 const char *user = relay_user;
2269 char *pass = NULL;
19fcfc91 2270 int len, ret;
239edf7c 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
19fcfc91 2280 len = strlen(user)+strlen(pass)+1;
239edf7c 2281 userpass = xmalloc(len+1);
19fcfc91 2282 sprintf(userpass,"%s:%s", user, pass);
239edf7c 2283 memset (pass, 0, strlen(pass));
19fcfc91 2284 cred = make_base64_string(userpass);
239edf7c 2285 memset (userpass, 0, len);
19fcfc91 2286
239edf7c 2287 f_report = 0; /* don't report for security */
19fcfc91 2288 ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
2289 f_report = 1;
2290 report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
239edf7c 2291
19fcfc91 2292 memset(cred, 0, strlen(cred));
2293 free(cred);
239edf7c 2294
19fcfc91 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;
19fcfc91 2308 char *auth_what;
2309
2310 debug("begin_http_relay()\n");
2311
19fcfc91 2312 if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
239edf7c 2313 return START_ERROR;
2314 if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
2315 return START_ERROR;
19fcfc91 2316 if (sendf(s,"\r\n") < 0)
239edf7c 2317 return START_ERROR;
2318
19fcfc91 2319 /* get response */
2320 if ( line_input(s, buf, sizeof(buf)) < 0 ) {
239edf7c 2321 debug("failed to read http response.\n");
2322 return START_ERROR;
19fcfc91 2323 }
239edf7c 2324
19fcfc91 2325 /* check status */
239edf7c 2326 if (!strchr(buf, ' ')) {
2327 error ("Unexpected http response: '%s'.\n", buf);
2328 return START_ERROR;
2329 }
19fcfc91 2330 result = atoi(strchr(buf,' '));
239edf7c 2331
19fcfc91 2332 switch ( result ) {
2333 case 200:
239edf7c 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;
19fcfc91 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) */
239edf7c 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
19fcfc91 2393 default:
239edf7c 2394 /* Not allowed */
2395 debug("http proxy is not allowed.\n");
2396 return START_ERROR;
19fcfc91 2397 }
2398 /* skip to end of response header */
2399 do {
239edf7c 2400 if ( line_input(s, buf, sizeof(buf) ) ) {
2401 debug("Can't skip response headers\n");
2402 return START_ERROR;
2403 }
19fcfc91 2404 } while ( strcmp(buf,"\r\n") != 0 );
2405
239edf7c 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;
19fcfc91 2470}
2471
2472
2473#ifdef _WIN32
2474/* ddatalen()
2475 Returns 1 if data is available, otherwise return 0
2476 */
2477int
239edf7c 2478stdindatalen (void)
19fcfc91 2479{
2480 DWORD len = 0;
2481 struct stat st;
2482 fstat( 0, &st );
239edf7c 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 }
19fcfc91 2496 } else if ( st.st_mode & _S_IFREG ) {
239edf7c 2497 /* in case of regular file (redirected) */
2498 len = 1; /* always data ready */
19fcfc91 2499 } else if ( _kbhit() ) {
239edf7c 2500 /* in case of console */
2501 len = 1;
19fcfc91 2502 }
2503 return len;
2504}
2505#endif /* _WIN32 */
2506
239edf7c 2507/* relay byte from stdin to socket and fro socket to stdout.
2508 returns reason of termination */
19fcfc91 2509int
2510do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
2511{
2512 /** vars for local input data **/
239edf7c 2513 char lbuf[1024]; /* local input buffer */
2514 int lbuf_len; /* available data in lbuf */
2515 int f_local; /* read local input more? */
19fcfc91 2516 /** vars for remote input data **/
239edf7c 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 */
19fcfc91 2521 /** other variables **/
2522 int nfds, len;
239edf7c 2523 fd_set ifds, ofds;
19fcfc91 2524 struct timeval *tmo;
2525#ifdef _WIN32
2526 struct timeval win32_tmo;
2527#endif /* _WIN32 */
239edf7c 2528
19fcfc91 2529 /* repeater between stdin/out and socket */
2530 nfds = ((local_in<remote)? remote: local_in) +1;
239edf7c 2531 f_local = 1; /* yes, read from local */
2532 f_remote = 1; /* yes, read from remote */
19fcfc91 2533 lbuf_len = 0;
2534 rbuf_len = 0;
2535
2536 while ( f_local || f_remote ) {
239edf7c 2537 FD_ZERO(&ifds );
2538 FD_ZERO(&ofds );
2539 tmo = NULL;
19fcfc91 2540
239edf7c 2541 /** prepare for reading local input **/
2542 if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
19fcfc91 2543#ifdef _WIN32
239edf7c 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
19fcfc91 2552#endif /* !_WIN32 */
239edf7c 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 }
19fcfc91 2569#ifdef _WIN32
239edf7c 2570 /* fake ifds if local is stdio handle because
19fcfc91 2571 select() of Winsock does not accept stdio
2572 handle. */
239edf7c 2573 if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
2574 FD_SET(0,&ifds); /* data ready */
19fcfc91 2575#endif
2576
239edf7c 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;
19fcfc91 2669}
2670
19fcfc91 2671int
2672accept_connection (u_short port)
2673{
239edf7c 2674 static int sock = -1;
19fcfc91 2675 int connection;
2676 struct sockaddr_in name;
19fcfc91 2677 struct sockaddr client;
2678 int socklen;
239edf7c 2679 fd_set ifds;
2680 int nfds;
2681 int sockopt;
19fcfc91 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)
239edf7c 2687 fatal("socket() failed, errno=%d\n", socket_errno());
2688 sockopt = 1;
2689 setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
2690 (void*)&sockopt, sizeof(sockopt));
19fcfc91 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)
239edf7c 2697 fatal ("bind() failed, errno=%d\n", socket_errno());
19fcfc91 2698
2699 if (listen( sock, 1) < 0)
239edf7c 2700 fatal ("listen() failed, errno=%d\n", socket_errno());
19fcfc91 2701
239edf7c 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);
19fcfc91 2740 socklen = sizeof(client);
2741 connection = accept( sock, &client, &socklen);
2742 if ( connection < 0 )
239edf7c 2743 fatal ("accept() failed, errno=%d\n", socket_errno());
19fcfc91 2744 return connection;
2745}
2746
2747
2748
2749/** Main of program **/
2750int
2751main( int argc, char **argv )
2752{
2753 int ret;
239edf7c 2754 int remote; /* socket */
2755 int local_in; /* Local input */
2756 int local_out; /* Local output */
2757 int reason;
19fcfc91 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 ) {
239edf7c 2770 /* Relay between local port and destination */
19fcfc91 2771 local_in = local_out = accept_connection( local_port );
2772 } else {
239edf7c 2773 /* Relay between stdin/stdout and desteination */
2774 local_in = 0;
2775 local_out = 1;
19fcfc91 2776#ifdef _WIN32
239edf7c 2777 _setmode(local_in, O_BINARY);
2778 _setmode(local_out, O_BINARY);
19fcfc91 2779#endif
2780 }
2781
2782retry:
2783#ifndef _WIN32
2784 if (0 < connect_timeout)
239edf7c 2785 set_timeout (connect_timeout);
19fcfc91 2786#endif /* not _WIN32 */
2787
2788 /* make connection */
2789 if ( relay_method == METHOD_DIRECT ) {
239edf7c 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());
19fcfc91 2794 } else {
239edf7c 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());
19fcfc91 2799 }
2800
2801 /** resolve destination host (SOCKS) **/
239edf7c 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__ */
19fcfc91 2806 if (relay_method == METHOD_SOCKS &&
239edf7c 2807 socks_resolve == RESOLVE_LOCAL &&
2808 local_resolve (dest_host, &dest_addr) < 0) {
2809 fatal("Unknown host: %s", dest_host);
19fcfc91 2810 }
2811
2812 /** relay negociation **/
2813 switch ( relay_method ) {
2814 case METHOD_SOCKS:
239edf7c 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;
19fcfc91 2819
2820 case METHOD_HTTP:
239edf7c 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;
19fcfc91 2838 }
2839 debug("connected\n");
2840
2841#ifndef _WIN32
2842 if (0 < connect_timeout)
239edf7c 2843 set_timeout (0);
19fcfc91 2844#endif /* not _WIN32 */
2845
2846 /* main loop */
2847 debug ("start relaying.\n");
239edf7c 2848do_repeater:
2849 reason = do_repeater(local_in, local_out, remote);
19fcfc91 2850 debug ("relaying done.\n");
239edf7c 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 }
19fcfc91 2860 closesocket(remote);
2861 if ( local_type == LOCAL_SOCKET)
239edf7c 2862 closesocket(local_in);
19fcfc91 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"
239edf7c 2874 tab-width: 8
19fcfc91 2875 fill-column: 74
2876 comment-column: 48
2877 End:
2878 ------------------------------------------------------------ */
2879
2880/*** end of connect.c ***/
This page took 0.381671 seconds and 4 git commands to generate.