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