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