]> git.pld-linux.org Git - packages/connect.git/commitdiff
c78de727e1208799072be78c05d64398 connect.c RA-DEVEL auto/ac/openssh-3_7_1p2-1 auto/ac/openssh-3_7_1p2-2 auto/ac/openssh-3_7_1p2-3 auto/ac/openssh-3_8_1p1-1 auto/ac/openssh-3_8p1-0_2 auto/ac/openssh-3_8p1-0_3 auto/ac/openssh-3_8p1-3 auto/ac/openssh-3_9p1-2 auto/ac/openssh-3_9p1-3 auto/ac/openssh-3_9p1-5 openssh-3_9p1-4
authorankry <ankry@pld-linux.org>
Wed, 27 Aug 2003 10:40:21 +0000 (10:40 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    connect.c -> 1.1

connect.c [new file with mode: 0644]

diff --git a/connect.c b/connect.c
new file mode 100644 (file)
index 0000000..6a74031
--- /dev/null
+++ b/connect.c
@@ -0,0 +1,2545 @@
+/***********************************************************************
+ * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
+ *
+ * Copyright (c) 2000, 2001 Shun-ichi Goto
+ * Copyright (c) 2002, J. Grant (English Corrections)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * ---------------------------------------------------------
+ * PROJECT:  My Test Program
+ * AUTHOR:   Shun-ichi GOTO <gotoh@taiyo.co.jp>
+ * CREATE:   Wed Jun 21, 2000
+ * REVISION: $Revision$
+ * ---------------------------------------------------------
+ *
+ * Getting Source
+ * ==============
+ *
+ *   Recent version of 'connect.c' is available from
+ *     http://www.imasy.or.jp/~gotoh/ssh/connect.c
+ *
+ *   Pre-compiled biniary for Win32 is also available:
+ *     http://www.imasy.or.jp/~gotoh/ssh/connect.exe.gz
+ *
+ *   Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
+ *   is available:
+ *     http://www.imasy.or.jp/~gotoh/ssh/ssh-askpass.exe.gz
+ *
+ * How To Compile
+ * ==============
+ *
+ *  On UNIX environment:
+ *      $ gcc connect.c -o connect
+ *
+ *  On SOLARIS:
+ *      $ gcc -o connect -lresolv -lsocket -lnsl connect.c
+ *
+ *  on Win32 environment:
+ *      $ cl connect.c wsock32.lib advapi32.lib
+ *    or
+ *      $ bcc32 connect.c wsock32.lib advapi32.lib
+ *    or
+ *      $ gcc connect.c -o connect
+ *
+ * How To Use
+ * ==========
+ *
+ *   You can specify proxy method in an environment variable or in a
+ *   command line option.
+ *
+ *   usage:  connect [-dnhs45] [-R resolve] [-p local-port] [-w sec]
+ *                   [-H [user@]proxy-server[:port]]
+ *                   [-S [user@]socks-server[:port]]
+ *                   host port
+ *
+ *   "host" and "port" is for the target hostname and port-number to
+ *   connect to.
+ *
+ *   The -H option specifys a hostname and port number of the http proxy
+ *   server to relay. If port is omitted, 80 is used. You can specify this
+ *   value in the environment variable HTTP_PROXY and pass the -h option
+ *   to use it.
+ *
+ *   The -S option specifys the hostname and port number of the SOCKS
+ *   server to relay.  Like -H, port number can be omitted and the default
+ *   is 1080. You can also specify this value pair in the environment
+ *   variable SOCKS5_SERVER and give the -s option to use it.
+ *
+ *   The '-4' and the '-5' options are for specifying SOCKS relaying and
+ *   indicates protocol version to use. It is valid only when used with
+ *   '-s' or '-S'. Default is '-5' (protocol version 5)
+ *
+ *   The '-R' option is for specifying method to resolve the
+ *   hostname. Three keywords ("local", "remote", "both") or dot-notation
+ *   IP address are acceptable.  The keyword "both" means, "Try local
+ *   first, then remote". If a dot-notation IP address is specified, use
+ *   this host as nameserver. The default is "remote" for SOCKS5 or
+ *   "local" for others. On SOCKS4 protocol, remote resolving method
+ *   ("remote" and "both") requires protocol 4a supported server.
+ *
+ *   The '**-p**' option will forward a local TCP port instead of using the
+ *   standard input and output.
+ *
+ *   The '-w' option specifys timeout seconds for making connection with
+ *   TARGET host.
+ *
+ *   The '-d' option is used for debug. If you fail to connect, use this
+ *   and check request to and response from server.
+ *
+ *   You can omit the "port" argument when program name is special format
+ *   containing port number itself. For example,
+ *     $ ln -s connect connect-25
+ *   means this connect-25 command is spcifying port number 25 already
+ *   so you need not 2nd argument (and ignored if specified).
+ *
+ *   To use proxy, this example is for SOCKS5 connection to connect to
+ *   'host' at port 25 via SOCKS5 server on 'firewall' host.
+ *     $ connect -S firewall  host 25
+ *   or
+ *     $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER
+ *     $ connect -s host 25
+ *
+ *   For a HTTP-PROXY connection:
+ *     $ connect -H proxy-server:8080  host 25
+ *   or
+ *     $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY
+ *     $ connect -h host 25
+ *   To forward a local port, for example to use ssh:
+ *     $ connect -p 5550 -H proxy-server:8080  host 22
+ *    ($ ssh -l user -p 5550 localhost )
+ *
+ * TIPS
+ * ====
+ *
+ *   Connect.c doesn't have any configuration to specify the SOCKS server.
+ *   If you are a mobile user, this limitation might bother you.  However,
+ *   You can compile connect.c and link with other standard SOCKS library
+ *   like the NEC SOCKS5 library or Dante. This means connect.c is
+ *   socksified and uses a configration file like to other SOCKSified
+ *   network commands and you can switch configuration file any time
+ *   (ex. when ppp startup) that brings you switching of SOCKS server for
+ *   connect.c in same way with other commands. For this case, you can
+ *   write ~/.ssh/config like this:
+ *
+ *     ProxyCommand connect -n %h %p
+ *
+ * SOCKS5 authentication
+ * =====================
+ *
+ *   Only USER/PASS authentication is supported.
+ *
+ * Proxy authentication
+ * ====================
+ *
+ *   Only BASIC scheme is supported.
+ *
+ * Authentication informations
+ * ===========================
+ *
+ *   User name for authentication is specifed by an environment variable
+ *   or system login name.  And Password is specified from environment
+ *   variable or external program (specified in $SSH_ASKPASS) or tty.
+ *
+ *   Following environment variable is used for specifying user name.
+ *     SOCKS: $SOCKS5_USER, $LOGNAME, $USER
+ *     HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER
+ *
+ * ssh-askpass support
+ * ===================
+ *
+ *   You can use ssh-askpass (came from OpenSSH or else) to specify
+ *   password on graphical environment (X-Window or MS Windows). To use
+ *   this, set program name to environment variable SSH_ASKPASS. On UNIX,
+ *   X-Window must be required, so $DISPLAY environment variable is also
+ *   needed.  On Win32 environment, $DISPLAY is not mentioned.
+ *
+ * Related Informations
+ * ====================
+ *
+ *   SOCKS5 -- RFC 1928, RFC 1929, RFC 1961
+ *             NEC SOCKS Reference Implementation is available from:
+ *               http://www.socks.nec.com
+ *             DeleGate version 5 or earlier can be SOCKS4 server,
+ *             and version 6 can be SOCKS5 and SOCKS4 server.
+ *             and version 7.7.0 or later can be SOCKS5 and SOCKS4a server.
+ *               http://www.delegate.org/delegate/
+ *
+ *   HTTP-Proxy --
+ *             Many http proxy servers supports this, but https should
+ *             be allowed as configuration on your host.
+ *             For example on DeleGate, you should add "https" to the
+ *             "REMITTABLE" parameter to allow HTTP-Proxy like this:
+ *               delegated -Pxxxx ...... REMITTABLE="+,https" ...
+ *
+ *  Hypertext Transfer Protocol -- HTTP/1.1  -- RFC 2616
+ *  HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617
+ *             For proxy authentication, refer these documents.
+ *
+ ***********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef __CYGWIN32__
+#undef _WIN32
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <conio.h>
+#else /* !_WIN32 */
+#include <unistd.h>
+#include <pwd.h>
+#include <termios.h>
+#include <sys/time.h>
+#ifndef __hpux
+#include <sys/select.h>
+#endif /* __hpux */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifndef __CYGWIN32__
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif /* !__CYGWIN32__ */
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define ECONNRESET WSAECONNRESET
+#endif /* _WI32 */
+
+
+
+#ifndef LINT
+static char *vcid = "$Id$";
+#endif
+
+
+/* consider Borland C */
+#ifdef __BORLANDC__
+#define _kbhit kbhit
+#endif
+
+/* help message.
+   Win32 environment does not support -R option. 
+   Win32 native compilers does not support -w option, yet.
+*/
+static char *usage =
+#ifdef _WIN32
+"usage: %s [-dnhs45] \n"
+#else  /* not _WIN32 */
+#ifdef __CYGWIN32__
+/* help message for UNIX */
+"usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
+#else  /* not __CYGWIN32__ */
+"usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
+#endif /* __CYGWIN32__ */
+#endif /* not _WIN32 */
+"          [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
+"          host port\n";
+
+/* name of this program */
+char *progname = NULL;
+char *progdesc = "connect --- simple relaying command via proxy.";
+char *rcs_revstr = "$Revision$";
+char *revstr = NULL;
+
+/* options */
+int f_debug = 0;
+
+/* report flag to hide secure information */
+int f_report = 1;
+
+int connect_timeout = 0;
+
+/* local input type */
+#define LOCAL_STDIO    0
+#define LOCAL_SOCKET   1
+char *local_type_names[] = { "stdio", "socket" };
+int   local_type = LOCAL_STDIO;
+u_short local_port = 0;
+
+/* utiity types, pair holder of number and string */
+typedef struct {
+    int num;
+    const char *str;
+} LOOKUP_ITEM;
+
+/* relay method, server and port */
+#define METHOD_UNDECIDED 0
+#define METHOD_DIRECT    1
+#define METHOD_SOCKS     2
+#define METHOD_HTTP      3
+char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP" };
+
+int   relay_method = METHOD_UNDECIDED;         /* relaying method */
+char *relay_host = NULL;                       /* hostname of relay server */
+u_short relay_port = 0;                                /* port of relay server */
+char *relay_user = NULL;                       /* user name for auth */
+
+/* destination target host and port */
+char *dest_host = NULL;
+struct sockaddr_in dest_addr;
+u_short dest_port = 0;
+
+/* informations for SOCKS */
+#define SOCKS5_REP_SUCCEEDED   0x00    /* succeeded */
+#define SOCKS5_REP_FAIL                0x01    /* general SOCKS serer failure */
+#define SOCKS5_REP_NALLOWED    0x02    /* connection not allowed by ruleset */
+#define SOCKS5_REP_NUNREACH    0x03    /* Network unreachable */
+#define SOCKS5_REP_HUNREACH    0x04    /* Host unreachable */
+#define SOCKS5_REP_REFUSED     0x05    /* connection refused */
+#define SOCKS5_REP_EXPIRED     0x06    /* TTL expired */
+#define SOCKS5_REP_CNOTSUP     0x07    /* Command not supported */
+#define SOCKS5_REP_ANOTSUP     0x08    /* Address not supported */
+#define SOCKS5_REP_INVADDR     0x09    /* Inalid address */
+
+LOOKUP_ITEM socks5_rep_names[] = {
+    { SOCKS5_REP_SUCCEEDED, "succeeded"},
+    { SOCKS5_REP_FAIL,      "general SOCKS server failure"},
+    { SOCKS5_REP_NALLOWED,  "connection not allowed by ruleset"},
+    { SOCKS5_REP_NUNREACH,  "Network unreachable"},
+    { SOCKS5_REP_HUNREACH,  "Host unreachable"},
+    { SOCKS5_REP_REFUSED,   "connection refused"},
+    { SOCKS5_REP_EXPIRED,   "TTL expired"},
+    { SOCKS5_REP_CNOTSUP,   "Command not supported"},
+    { SOCKS5_REP_ANOTSUP,   "Address not supported"},
+    { SOCKS5_REP_INVADDR,   "Invalid address"},
+    { -1, NULL }
+};
+
+/* SOCKS5 authentication methods */
+#define SOCKS5_AUTH_REJECT     0xFF    /* No acceptable auth method */
+#define SOCKS5_AUTH_NOAUTH     0x00    /* without authentication */
+#define SOCKS5_AUTH_GSSAPI     0x01    /* GSSAPI */
+#define SOCKS5_AUTH_USERPASS   0x02    /* User/Password */
+#define SOCKS5_AUTH_CHAP       0x03    /* Challenge-Handshake Auth Proto. */
+#define SOCKS5_AUTH_EAP                0x05    /* Extensible Authentication Proto. */
+#define SOCKS5_AUTH_MAF                0x08    /* Multi-Authentication Framework */
+
+#define SOCKS4_REP_SUCCEEDED   90      /* rquest granted (succeeded) */
+#define SOCKS4_REP_REJECTED    91      /* request rejected or failed */
+#define SOCKS4_REP_IDENT_FAIL  92      /* cannot connect identd */
+#define SOCKS4_REP_USERID      93      /* user id not matched */
+
+LOOKUP_ITEM socks4_rep_names[] = {
+    { SOCKS4_REP_SUCCEEDED,  "request granted (succeeded)"},
+    { SOCKS4_REP_REJECTED,   "request rejected or failed"},
+    { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
+    { SOCKS4_REP_USERID,     "user id not matched"},
+    { -1, NULL }
+};
+
+#define RESOLVE_UNKNOWN 0
+#define RESOLVE_LOCAL   1
+#define RESOLVE_REMOTE  2
+#define RESOLVE_BOTH    3
+char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
+
+int socks_version = 5;                         /* SOCKS protocol version */
+int socks_resolve = RESOLVE_UNKNOWN;
+struct sockaddr_in socks_ns;
+char *socks5_auth = NULL;
+
+/* Environment variable names */
+#define ENV_SOCKS_SERVER  "SOCKS_SERVER"       /* SOCKS server */
+#define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
+#define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
+
+#define ENV_SOCKS_RESOLVE  "SOCKS_RESOLVE"     /* resolve method */
+#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
+#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
+
+#define ENV_SOCKS5_USER            "SOCKS5_USER"       /* auth user for SOCKS5 */
+#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD"  /* auth password for SOCKS5 */
+
+#define ENV_HTTP_PROXY          "HTTP_PROXY"   /* common env var */
+#define ENV_HTTP_PROXY_USER     "HTTP_PROXY_USER" /* auth user */
+#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
+
+#define ENV_CONNECT_USER     "CONNECT_USER"    /* default auth user name */
+#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD"        /* default auth password */
+
+#define ENV_SOCKS_DIRECT   "SOCKS_DIRECT"      /* addr-list for non-proxy */
+#define ENV_SOCKS5_DIRECT  "SOCKS5_DIRECT"
+#define ENV_SOCKS4_DIRECT  "SOCKS4_DIRECT"
+#define ENV_HTTP_DIRECT    "HTTP_DIRECT"
+#define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
+
+#define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
+#define ENV_SSH_ASKPASS "SSH_ASKPASS"          /* askpass program */
+
+/* Prefix string of HTTP_PROXY */
+#define HTTP_PROXY_PREFIX "http://"
+#define PROXY_AUTH_NONE 0
+#define PROXY_AUTH_BASIC 1
+#define PROXY_AUTH_DIGEST 2
+int proxy_auth_type = PROXY_AUTH_NONE;
+
+
+/* return value of relay start function. */
+#define START_ERROR -1
+#define START_OK     0
+#define START_RETRY  1
+
+/* socket related definitions */
+#ifndef _WIN32
+#define SOCKET int
+#endif
+#ifndef SOCKET_ERROR
+#define SOCKET_ERROR -1
+#endif
+
+#ifndef FD_ALLOC
+#define FD_ALLOC(nfds) ((fd_set*)malloc((nfds+7)/8))
+#endif /* !FD_ALLOC */
+
+#ifdef _WIN32
+#define socket_errno() WSAGetLastError()
+#else /* !_WIN32 */
+#define closesocket close
+#define socket_errno() (errno)
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define popen _popen
+#endif /* WIN32 */
+
+/* packet operation macro */
+#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
+
+/* debug message output */
+void
+debug( const char *fmt, ... )
+{
+    va_list args;
+    if ( f_debug ) {
+       va_start( args, fmt );
+       fprintf(stderr, "DEBUG: ");
+       vfprintf( stderr, fmt, args );
+       va_end( args );
+    }
+}
+
+void
+debug_( const char *fmt, ... )                 /* without prefix */
+{
+    va_list args;
+    if ( f_debug ) {
+       va_start( args, fmt );
+       vfprintf( stderr, fmt, args );
+       va_end( args );
+    }
+}
+
+/* error message output */
+void
+error( const char *fmt, ... )
+{
+    va_list args;
+    va_start( args, fmt );
+    fprintf(stderr, "ERROR: ");
+    vfprintf( stderr, fmt, args );
+    va_end( args );
+}
+
+void
+fatal( const char *fmt, ... )
+{
+    va_list args;
+    va_start( args, fmt );
+    fprintf(stderr, "FATAL: ");
+    vfprintf( stderr, fmt, args );
+    va_end( args );
+    exit (EXIT_FAILURE);
+}
+
+void
+downcase( char *buf )
+{
+    while ( *buf ) {
+       if ( isupper(*buf) )
+           *buf += 'a'-'A';
+       buf++;
+    }
+}
+
+int
+lookup_resolve( const char *str )
+{
+    char *buf = strdup( str );
+    int ret;
+
+    downcase( buf );
+    if ( strcmp( buf, "both" ) == 0 )
+       ret = RESOLVE_BOTH;
+    else if ( strcmp( buf, "remote" ) == 0 )
+       ret = RESOLVE_REMOTE;
+    else if ( strcmp( buf, "local" ) == 0 )
+       ret = RESOLVE_LOCAL;
+    else if ( strspn(buf, "0123456789.") == strlen(buf) ) {
+#if defined(_WIN32) || defined(__CYGWIN32__)
+       fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
+#endif /* _WIN32 || __CYGWIN32__ */
+       ret = RESOLVE_LOCAL;                    /* this case is also 'local' */
+       socks_ns.sin_addr.s_addr = inet_addr(buf);
+       socks_ns.sin_family = AF_INET;
+    }
+    else
+       ret = RESOLVE_UNKNOWN;
+    free(buf);
+    return ret;
+}
+
+char *
+getusername(void)
+{
+#ifdef _WIN32
+    static char buf[1024];
+    DWORD size = sizeof(buf);
+    buf[0] = '\0';
+    GetUserName( buf, &size);
+    return buf;
+#else  /* not _WIN32 */
+    struct passwd *pw = getpwuid(getuid());
+    if ( pw == NULL )
+       fatal("getpwuid() failed for uid: %d\n", getuid());
+    return pw->pw_name;
+#endif /* not _WIN32 */
+}
+
+/* expect
+   check STR is begin with substr with case-ignored comparison.
+   Return 1 if matched, otherwise 0.
+*/
+int
+expect( char *str, char *substr)
+{
+    int len = strlen(substr);
+    while ( 0 < len-- ) {
+       if ( toupper(*str) != toupper(*substr) )
+           return 0;                           /* not matched */
+       str++, substr++;
+    }
+    return 1;                  /* good, matched */
+}
+
+
+/** PARAMETER operation **/
+#define PARAMETER_FILE "/etc/connectrc"
+#define PARAMETER_DOTFILE ".connectrc"
+typedef struct {
+    char* name;
+    char* value;
+} PARAMETER_ITEM;
+PARAMETER_ITEM parameter_table[] = {
+    { ENV_SOCKS_SERVER, NULL },
+    { ENV_SOCKS5_SERVER, NULL },
+    { ENV_SOCKS4_SERVER, NULL },
+    { ENV_SOCKS_RESOLVE, NULL },
+    { ENV_SOCKS5_RESOLVE, NULL },
+    { ENV_SOCKS4_RESOLVE, NULL },
+    { ENV_SOCKS5_USER, NULL },
+    { ENV_SOCKS5_PASSWORD, NULL },
+    { ENV_HTTP_PROXY, NULL },
+    { ENV_HTTP_PROXY_USER, NULL },
+    { ENV_HTTP_PROXY_PASSWORD, NULL },
+    { ENV_CONNECT_USER, NULL },
+    { ENV_CONNECT_PASSWORD, NULL },
+    { ENV_SSH_ASKPASS, NULL },
+    { ENV_SOCKS5_DIRECT, NULL },
+    { ENV_SOCKS4_DIRECT, NULL },
+    { ENV_SOCKS_DIRECT, NULL },
+    { ENV_HTTP_DIRECT, NULL },
+    { ENV_CONNECT_DIRECT, NULL },
+    { ENV_SOCKS5_AUTH, NULL },
+    { NULL, NULL }
+};
+
+PARAMETER_ITEM*
+find_parameter_item(const char* name){
+    int i;
+    for( i = 0; parameter_table[i].name != NULL; i++ ){
+       if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
+           return &parameter_table[i];
+    }
+    return NULL;
+}
+
+void
+read_parameter_file_1(const char* name){
+    FILE* f;
+    int line;
+    char lbuf[1025];
+    f = fopen(name, "r");
+    if( f ){
+       debug("Reading parameter file(%s)\n", name);
+       for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
+           char *p, *q, *param = NULL, *value = NULL;
+           p = strchr(lbuf, '\n');
+           if ( p == NULL )
+               fatal("%s:%d: buffer overflow\n", name, line);
+           *p = '\0';
+           p = strchr(lbuf, '#');
+           if ( p )
+               *p = '\0';
+           for ( p = lbuf; *p; p++ )
+               if( *p != ' ' && *p != '\t' ) break;
+           if ( *p == '\0' ) continue;
+           param = p;
+           p = strchr(p, '=');
+           if ( p == NULL ) {
+               error("%s:%d: missing equal sign\n", name, line);
+               continue;
+           }
+           for ( q = p - 1; q >= lbuf; q-- )
+               if ( *q != ' ' && *q != '\t' ) break;
+           *++q = '\0';
+           for ( ++p; *p; p++ )
+               if ( *p != ' ' && *p != '\t' ) break;
+           value = p;
+           for ( ; *p; p++ );
+           for ( p--; p >= lbuf; p-- )
+               if ( *p != ' ' && *p != '\t' ) break;
+           *++p = '\0';
+           if ( param && value ) {
+               PARAMETER_ITEM *item;
+               item = find_parameter_item(param);
+               if ( item == NULL ) {
+                   error("%s:%d: unknown parameter `%s'\n", name, line, param);
+                   continue;
+               }
+               item->value = strdup(value);
+               debug("Parameter `%s' is set to `%s'\n", param, value);
+           }
+       }
+    }
+}
+
+void
+read_parameter_file(void){
+#ifndef _WIN32
+    char *name;
+    struct passwd *pw;
+#endif
+
+    read_parameter_file_1(PARAMETER_FILE);
+#ifndef _WIN32
+    pw = getpwuid(getuid());
+    if ( pw == NULL )
+       fatal("getpwuid() failed for uid: %d\n", getuid());
+    name = malloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
+    if ( name == NULL )
+       fatal("Can't allocate memory\n");
+    strcpy(name, pw->pw_dir);
+    strcat(name, "/" PARAMETER_DOTFILE);
+    read_parameter_file_1(name);
+    free(name);
+#endif /* _WIN32 */
+}
+
+char*
+getparam(const char* name){
+    char *value = getenv(name);
+    if ( value == NULL ){
+       PARAMETER_ITEM *item = find_parameter_item(name);
+       if ( item != NULL )
+           value = item->value;
+    }
+    return value;
+}
+
+
+/** DIRECT connection **/
+#define MAX_DIRECT_ADDR_LIST 256
+
+struct ADDRPAIR {
+    struct in_addr addr;
+    struct in_addr mask;
+    int negative;
+};
+
+struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
+int n_direct_addr_list = 0;
+
+void
+mask_addr (void *addr, void *mask, int addrlen)
+{
+    char *a, *m;
+    a = addr;
+    m = mask;
+    while ( 0 < addrlen-- )
+       *a++ &= *m++;
+}
+
+int
+add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
+{
+    struct in_addr iaddr;
+    char *s;
+    if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
+       error("direct address table is full!\n");
+       return -1;
+    }
+    iaddr = *addr;
+    mask_addr(&iaddr, mask, sizeof(iaddr));
+    s = strdup(inet_ntoa(iaddr));
+    debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
+    free(s);
+    memcpy( &direct_addr_list[n_direct_addr_list].addr,
+           &iaddr, sizeof(iaddr));
+    memcpy( &direct_addr_list[n_direct_addr_list].mask,
+           mask, sizeof(*mask));
+    direct_addr_list[n_direct_addr_list].negative = negative;
+    n_direct_addr_list++;
+    return 0;
+}
+
+int 
+parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
+{
+    /* NOTE: */ 
+    /* Assme already be splitted by separator 
+       and formatted as folowing: 
+       1)  12.34.56.789/255.255.255.0 
+       2)  12.34.56.789/24 
+       3)  12.34.56. 
+       All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
+    */
+    const char *ptr;
+    u_char *dsta, *dstm;
+    int i, n;
+    
+    assert( str != NULL );
+    debug("parsing address pair: '%s'\n", str);
+    addr->s_addr = 0;
+    mask->s_addr = 0;
+    ptr = str;
+    dsta = (u_char*)&addr->s_addr;
+    dstm = (u_char*)&mask->s_addr;
+    for (i=0; i<4; i++ ) {
+       if ( *ptr == '\0' )
+           break;              /* case of format #3 */
+       if ( !isdigit(*ptr) )
+           return -1;          /* format error: */
+       *dsta++ = atoi( ptr );
+       *dstm++ = 255;          /* automatic mask for format #3 */
+       while ( isdigit(*ptr) ) /* skip digits */
+           ptr++;
+       if ( *ptr == '.' ) 
+           ptr++;
+       else
+           break;
+    }
+    /* At this point, *ptr points '/' or EOS ('\0') */
+    if ( *ptr == '\0' )
+       return 0;                       /* complete as format #3 */
+    if ( *ptr != '/' )
+       return -1;                      /* format error */
+    /* Now parse mask for format #1 or #2 */
+    ptr++;
+    mask->s_addr = 0;                  /* clear automatic mask */
+    
+    if ( strchr( ptr, '.') ) {
+       /* case of format #1 */
+       dstm = (u_char*)&mask->s_addr;
+       for (i=0; i<4; i++) {
+           if ( !isdigit(*ptr) )
+               return -1;              /* format error: */
+           *dstm++ = atoi(ptr);
+           while ( isdigit(*ptr) )     /* skip digits */
+               ptr++;
+           if ( *ptr == '.' )
+               ptr++;
+           else
+               break;                  /* from for loop */
+       }
+       /* complete as format #1 */
+    } else {
+       /* case of format #2 */
+       if ( !isdigit(*ptr) )
+           return -1;                  /* format error: */
+       n = atoi(ptr);
+       if ( n<0 || 32<n)
+           return -1;                  /* format error */
+       mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
+       /* complete as format #1 */
+    }
+    return 0;
+}
+
+void
+initialize_direct_addr (void)
+{
+    int negative;
+    int n_entries;
+    char *env = NULL, *beg, *next, *envkey = NULL;
+    struct in_addr addr, mask;
+
+    if ( relay_method == METHOD_SOCKS ){
+       if ( socks_version == 5 )
+           envkey = ENV_SOCKS5_DIRECT;
+       else
+           envkey = ENV_SOCKS4_DIRECT;
+       env = getparam(envkey);
+       if ( env == NULL )
+           env = getparam(ENV_SOCKS_DIRECT);
+    } else if ( relay_method == METHOD_HTTP ){
+       env = getparam(ENV_HTTP_DIRECT);
+    }
+
+    if ( env == NULL )
+       env = getparam(ENV_CONNECT_DIRECT);
+    
+    if ( env == NULL )
+       return;                 /* no entry */
+    debug("making direect addr list from: '%s'\n", env);
+    env = strdup( env );       /* reallocate to modify */
+    beg = next = env;
+    n_entries = 0;
+    do {
+       if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
+           error("too many entries in %s", envkey);
+           break;              /* from do loop */
+       }
+       next = strchr( beg, ',');
+       if ( next != NULL )
+           *next++ = '\0';
+       addr.s_addr = 0;
+       mask.s_addr = 0;
+       if (*beg == '!') {
+           negative = 1;
+           beg++;
+       } else
+           negative = 0;
+       if ( !parse_addr_pair( beg, &addr, &mask ) ) {
+           add_direct_addr( &addr, &mask, negative );
+       } else {
+           error("invalid addr format in %s: %s\n", envkey, beg);
+       }
+       if ( next != NULL )
+           beg = next;
+    } while ( next != NULL );
+
+    free( env );
+    return;
+}
+
+int
+cmp_addr (void *addr1, void *addr2, int addrlen)
+{
+    return memcmp( addr1, addr2, addrlen );
+}
+
+int 
+is_direct_address (const struct sockaddr_in *addr, int addrlen)
+{
+    int i;
+    struct in_addr saddr, iaddr;
+
+    saddr = addr->sin_addr;
+    
+    /* Note: assume IPV4 address !! */
+    for (i=0; i<n_direct_addr_list; i++ ) {
+       iaddr = saddr;
+       mask_addr( &iaddr, &direct_addr_list[i].mask, 
+                  sizeof(struct in_addr));
+       if (cmp_addr(&iaddr, &direct_addr_list[i].addr, 
+                    sizeof(struct in_addr)) == 0) {
+           if (direct_addr_list[i].negative) {
+               debug("negative match, addr to be SOCKSify: %s\n",
+                     inet_ntoa(saddr));
+               return 0;       /* not direct */
+           }
+           if (!direct_addr_list[i].negative) {
+               debug("positive match, addr to be direct: %s\n",
+                     inet_ntoa(saddr));
+               return 1;       /* direct*/
+           }
+       } 
+    }
+    debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
+    return 0;                  /* not direct */
+}
+
+
+/** TTY operation **/
+
+int intr_flag = 0;
+
+#ifndef _WIN32
+void
+intr_handler(int sig)
+{
+    intr_flag = 1;
+}
+
+void
+tty_change_echo(int fd, int enable)
+{
+    static struct termios ntio, otio;          /* new/old termios */
+    static sigset_t nset, oset;                        /* new/old sigset */
+    static struct sigaction nsa, osa;          /* new/old sigaction */
+    static int disabled = 0;
+
+    if ( disabled && enable ) {
+       /* enable echo */
+       tcsetattr(fd, TCSANOW, &otio);
+       disabled = 0;
+       /* resotore sigaction */
+       sigprocmask(SIG_SETMASK, &oset, NULL);
+       sigaction(SIGINT, &osa, NULL);
+       if ( intr_flag != 0 ) {
+           /* re-generate signal  */
+           kill(getpid(), SIGINT);
+           sigemptyset(&nset);
+           sigsuspend(&nset);
+           intr_flag = 0;
+       }
+    } else if (!disabled && !enable) {
+       /* set SIGINTR handler and break syscall on singal */
+       sigemptyset(&nset);
+       sigaddset(&nset, SIGTSTP);
+       sigprocmask(SIG_BLOCK, &nset, &oset);
+       intr_flag = 0;
+       memset(&nsa, 0, sizeof(nsa));
+       nsa.sa_handler = intr_handler;
+       sigaction(SIGINT, &nsa, &osa);
+       /* disable echo */
+       if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
+           disabled = 1;
+           ntio = otio;
+           ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+           (void) tcsetattr(fd, TCSANOW, &ntio);
+       }
+    }
+       
+    return;
+}
+
+#define TTY_NAME "/dev/tty"
+int
+tty_readpass( const char *prompt, char *buf, size_t size )
+{
+    int tty, ret = 0;
+
+    tty = open(TTY_NAME, O_RDWR);
+    if ( tty < 0 ) {
+       error("Unable to open %s\n", TTY_NAME);
+       return -1;                              /* can't open tty */
+    }
+    if ( size <= 0 )
+       return -1;                              /* no room */
+    write(tty, prompt, strlen(prompt));
+    buf[0] = '\0';
+    tty_change_echo(tty, 0);                   /* disable echo */
+    ret = read(tty,buf, size-1);
+    tty_change_echo(tty, 1);                   /* restore */
+    write(tty, "\n", 1);                       /* new line */
+    close(tty);
+    if ( strchr(buf,'\n') == NULL  )
+       return -1;
+    if ( 0 < ret )
+       buf[ret] = '\0';
+    return ret;
+}
+
+#else  /* _WIN32 */
+
+BOOL __stdcall
+w32_intr_handler(DWORD dwCtrlType)
+{
+    if ( dwCtrlType == CTRL_C_EVENT ) {
+       intr_flag = 1;
+       return TRUE;
+    } else {
+       return FALSE;
+    }
+}
+
+#define tty_readpass w32_tty_readpass
+int
+w32_tty_readpass( const char *prompt, char *buf, size_t size )
+{
+    HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
+                          0, NULL, OPEN_EXISTING, 0, NULL);
+    HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
+                           0, NULL, OPEN_EXISTING, 0, NULL);
+    DWORD mode;
+    int ret, bytes;
+
+    if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
+       fatal("Cannot open console. (errno=%d)", GetLastError());
+
+    WriteFile(out, prompt, strlen(prompt), &bytes, 0);
+    SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
+    GetConsoleMode(in, &mode);
+    SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
+    ret = ReadFile(in, buf, size, &bytes, 0);
+    SetConsoleMode(in, mode);                  /* enable echo */
+    SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
+    if ( intr_flag )
+       GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
+    WriteFile(out,"\n", 1, &bytes, 0);
+    CloseHandle(in);
+    CloseHandle(out);
+    return ret;
+}
+
+#endif /* _WIN32 */
+
+/*** network operations ***/
+
+/* set_relay()
+   Determine relay informations:
+   method, host, port, and username.
+   1st arg, METHOD should be METHOD_xxx.
+   2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
+   hostname is domain name or dot notation.
+   If port is omitted, use 80 for METHOD_HTTP method,
+   use 1080 for METHOD_SOCKS method.
+   Username is also able to given by 3rd. format.
+   2nd argument SPEC can be NULL. if NULL, use environment variable.
+ */
+int
+set_relay( int method, char *spec )
+{
+    char *buf, *sep, *resolve;
+    
+    relay_method = method;
+
+    read_parameter_file();
+    initialize_direct_addr();
+    if (n_direct_addr_list == 0) {
+       debug ("(none)\n");
+    } else {
+       int i;
+       for ( i=0; i<n_direct_addr_list; i++ ) {
+           char *s1, *s2;
+           s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
+           s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
+           debug(" #%d: %c%s/%s\n", i,
+                 (direct_addr_list[i].negative)? '!': ' ',
+                 s1, s2);
+           free(s1);
+           free(s2);
+       }
+    }
+
+    switch ( method ) {
+    case METHOD_DIRECT:
+       return -1;                              /* nothing to do */
+       
+    case METHOD_SOCKS:
+       if ( spec == NULL ) {
+           switch ( socks_version ) {
+           case 5:
+               spec = getparam(ENV_SOCKS5_SERVER);
+               break;
+           case 4:
+               spec = getparam(ENV_SOCKS4_SERVER);
+               break;
+           }
+       }
+       if ( spec == NULL )
+           spec = getparam(ENV_SOCKS_SERVER);
+       
+       if ( spec == NULL )
+           fatal("Failed to determine SOCKS server.\n");
+       relay_port = 1080;                      /* set default first */
+
+       /* determine resolve method */
+       if ( socks_resolve == RESOLVE_UNKNOWN ) {
+           if ( ((socks_version == 5) &&
+                 ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
+                ((socks_version == 4) &&
+                 ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
+                ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
+               socks_resolve = lookup_resolve( resolve );
+               if ( socks_resolve == RESOLVE_UNKNOWN )
+                   fatal("Invalid resolve method: %s\n", resolve);
+           } else {
+               /* default */
+               if ( socks_version == 5 )
+                   socks_resolve = RESOLVE_REMOTE;
+               else
+                   socks_resolve = RESOLVE_LOCAL;
+           }
+       }
+       break;
+       
+    case METHOD_HTTP:
+       if ( spec == NULL )
+           spec = getparam(ENV_HTTP_PROXY);
+       if ( spec == NULL )
+           fatal("You must specify http proxy server\n");
+       relay_port = 80;                        /* set default first */
+       break;
+    }
+    
+    if (expect( spec, HTTP_PROXY_PREFIX)) {
+       /* URL format like: "http://server:port/" */
+       /* extract server:port part */
+       buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
+       buf[strcspn(buf, "/")] = '\0';
+    } else {
+       /* assume spec is aready "server:port" format */
+       buf = strdup( spec );
+    }
+    spec = buf;
+    
+    /* check username in spec */ 
+    sep = strchr( spec, '@' );
+    if ( sep != NULL ) {
+       *sep = '\0';
+       relay_user = strdup( spec );
+       spec = sep +1;
+    }
+    if ( (relay_user == NULL) &&
+        ((relay_user = getenv("LOGNAME")) == NULL) &&
+        ((relay_user = getenv("USER")) == NULL) &&
+        ((relay_user = getusername()) == NULL)) {
+       /* get username from system */
+       debug("Cannot determine your username.\n");
+    }
+    
+    /* split out hostname and port number from spec */
+    sep = strchr(spec,':');
+    if ( sep == NULL ) {
+       /* hostname only, port is already set as default */
+       relay_host = strdup( spec );
+    } else {
+       /* hostname and port */
+       relay_port = atoi(sep+1);
+       *sep = '\0';
+       relay_host = strdup( spec );
+    }
+    free(buf);
+    return 0;
+}
+
+
+u_short
+resolve_port( const char *service )
+{
+    int port;
+    if ( service[strspn (service, "0123456789")] == '\0'  ) {
+       /* all digits, port number */
+       port = atoi(service);
+    } else {
+       /* treat as service name */
+       struct servent *ent;
+       ent = getservbyname( service, NULL );
+       if ( ent == NULL ) {
+           debug("Unknown service, '%s'\n", service);
+           port = 0;
+       } else {
+           port = ntohs(ent->s_port);
+           debug("service: %s => %d\n", service, port);
+       }
+    }
+    return (u_short)port;
+}
+
+void
+make_revstr(void)
+{
+    char *ptr;
+    size_t len;
+    ptr = strstr(rcs_revstr, ": ");
+    if (!ptr) {
+       revstr = strdup("unknown");
+       return;
+    }
+    ptr += 2;
+    len = strspn(ptr, "0123456789.");
+    if (0 < len) {
+       revstr = malloc(len+1);
+       memcpy(revstr, ptr, len);
+       revstr[len] = '\0';
+    }
+}
+
+int
+getarg( int argc, char **argv )
+{
+    int err = 0;
+    char *ptr, *server = (char*)NULL;
+    int method = METHOD_DIRECT;
+    
+    progname = *argv;
+    argc--, argv++;
+
+    /* check optinos */
+    while ( (0 < argc) && (**argv == '-') ) {
+       ptr = *argv + 1;
+       while ( *ptr ) {
+           switch ( *ptr ) {
+           case 's':                           /* use SOCKS */
+               method = METHOD_SOCKS;
+               break;
+
+           case 'n':                           /* no proxy */
+               method = METHOD_DIRECT;
+               break;
+               
+           case 'h':                           /* use http-proxy */
+               method = METHOD_HTTP;
+               break;
+
+           case 'S':                           /* specify SOCKS server */
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   method = METHOD_SOCKS;
+                   server = *argv;
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+           
+           case 'H':                           /* specify http-proxy server */
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   method = METHOD_HTTP;
+                   server = *argv;
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+               
+           case 'P':
+               /* destination port number */
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   dest_port = resolve_port(*argv);
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+
+           case 'p':                          /* specify port to forward */
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   local_type = LOCAL_SOCKET;
+                   local_port = resolve_port(*argv);
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+
+#ifndef _WIN32         
+           case 'w':
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   connect_timeout = atoi(*argv);
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+#endif /* not _WIN32 */
+
+           case '4':
+               socks_version = 4;
+               break;
+               
+           case '5':
+               socks_version = 5;
+               break;
+
+           case 'a':
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   socks5_auth = *argv;
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+
+           case 'R':                           /* specify resolve method */
+               if ( 0 < argc ) {
+                   argv++, argc--;
+                   socks_resolve = lookup_resolve( *argv );
+               } else {
+                   error("option '-%c' needs argument.\n", *ptr);
+                   err++;
+               }
+               break;
+
+           case 'V':                           /* print version */
+               fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
+               exit(0);
+               
+           case 'd':                           /* debug mode */
+               f_debug = 1;
+               break;
+               
+           default:
+               error("unknown option '-%c'\n", *ptr);
+               err++;
+           }
+           ptr++;
+       }
+       argc--, argv++;
+    }
+    
+    /* check error */
+    if ( 0 < err )
+       goto quit;
+       
+    set_relay( method, server );
+
+    /* check destination HOST (MUST) */
+    if ( argc == 0  ) {
+       fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
+       fprintf(stderr, usage, progname);
+       exit(0);
+    }
+    dest_host = argv[0];
+    /* decide port or service name from programname or argument */
+    if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
+        ((ptr=strchr( progname, '\\')) != NULL) )
+       ptr++;
+    else
+       ptr = progname;
+    if ( dest_port == 0 ) {
+       /* accept only if -P is not specified. */
+       if ( 1 < argc ) {
+           /* get port number from argument (prior to progname) */
+           /* NOTE: This way is for cvs ext method. */
+           dest_port = resolve_port(argv[1]);
+       } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
+           /* decide port number from program name */
+           char *str = strdup( ptr+8 );
+           str[strcspn( str, "." )] = '\0';
+           dest_port = resolve_port(str);
+           free(str);
+       }
+    }
+    /* check port number */
+    if ( dest_port <= 0 ) {
+       error( "You must specify the destination port correctly.\n");
+       err++;
+       goto quit;
+    }
+    if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
+       error("Invalid relay port: %d\n", dest_port);
+       err++;
+       goto quit;
+    }
+
+quit:
+    /* report for debugging */
+    debug("relay_method = %s (%d)\n",
+         method_names[relay_method], relay_method);
+    if ( relay_method != METHOD_DIRECT ) {
+       debug("relay_host=%s\n", relay_host);
+       debug("relay_port=%d\n", relay_port);
+       debug("relay_user=%s\n", relay_user);
+    }
+    if ( relay_method == METHOD_SOCKS ) {
+       debug("socks_version=%d\n", socks_version);
+       debug("socks_resolve=%s (%d)\n",
+             resolve_names[socks_resolve], socks_resolve);
+    }
+    debug("local_type=%s\n", local_type_names[local_type]);
+    if ( local_type == LOCAL_SOCKET )
+       debug("local_port=%d\n", local_port);
+    debug("dest_host=%s\n", dest_host);
+    debug("dest_port=%d\n", dest_port);
+    if ( 0 < err ) {
+       fprintf(stderr, usage, progname);
+       exit(1);
+    }
+    return 0;
+}
+
+#ifndef _WIN32
+/* Time-out feature is not allowed for Win32 native compilers. */
+/* MSVC and Borland C cannot but Cygwin and UNIXes can. */
+
+/* timeout signal hander */
+void
+sig_timeout(void)
+{
+    debug( "timed out\n" );
+    signal( SIGALRM, SIG_IGN );
+    alarm( 0 );
+}
+
+/* set timeout param = seconds, 0 clears */
+void
+set_timeout(int timeout)
+{
+    /* This feature is allowed for UNIX or cygwin environments, currently */
+    if ( timeout == 0 ) {
+        debug( "clearing timeout\n" );
+        signal( SIGALRM, SIG_IGN );
+        alarm( 0 );
+    } else {
+        debug( "setting timeout: %d seconds\n", timeout );
+        signal(SIGALRM, (void *)sig_timeout);
+        alarm( timeout );
+    }
+}
+#endif
+
+
+/* TODO: IPv6 */
+
+int
+local_resolve (const char *host, struct sockaddr_in *addr,
+              struct sockaddr_in *ns)
+{
+    struct hostent *ent;
+    if ( strspn(host, "0123456789.") == strlen(host) ) {
+       /* given by IPv4 address */
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr(host);
+    } else {
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+       if (ns != 0 && ns->sin_addr.s_addr != 0) {
+           res_init();
+           memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
+           _res.nscount = 1;
+           debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
+       }
+#endif /* !_WIN32 && !__CYGWIN32__ */
+       debug("resolving host by name: %s\n", host);
+       ent = gethostbyname (host);
+       if ( ent ) {
+           memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
+           addr->sin_family = ent->h_addrtype;
+           debug("resolved: %s (%s)\n",
+                 host, inet_ntoa(addr->sin_addr));
+       } else {
+           debug("failed to resolve locally.\n");
+           return -1;                          /* failed */
+       }
+    }
+    return 0;                                  /* good */
+}
+
+SOCKET
+open_connection( const char *host, u_short port )
+{
+    SOCKET s;
+    struct sockaddr_in saddr;
+
+    if ( relay_method == METHOD_DIRECT ) {
+       host = dest_host;
+       port = dest_port;
+    } else if ((local_resolve (dest_host, &saddr, NULL) >= 0)&&
+              (is_direct_address(&saddr, sizeof(saddr)))) {
+       debug("%s is connected directly\n", dest_host);
+       relay_method = METHOD_DIRECT;
+       host = dest_host;
+       port = dest_port;
+    } else {
+       host = relay_host;
+       port = relay_port;
+    }
+
+    if (local_resolve (host, &saddr, NULL) < 0) {
+       error("can't resolve hostname: %s\n", host);
+       return SOCKET_ERROR;
+    }
+    saddr.sin_port = htons(port);
+
+    debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
+    s = socket( AF_INET, SOCK_STREAM, 0 );
+    if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
+        == SOCKET_ERROR) {
+       debug( "connect() failed.\n");
+       return SOCKET_ERROR;
+    }
+    return s;
+}
+
+void
+report_text( char *prefix, char *buf )
+{
+    static char work[1024];
+    char *tmp;
+
+    if ( !f_debug )
+       return;
+    if ( !f_report )
+       return;                                 /* don't report */
+    debug("%s \"", prefix);
+    while ( *buf ) {
+       memset( work, 0, sizeof(work));
+       tmp = work;
+       while ( *buf && ((tmp-work) < sizeof(work)-5) ) {
+           switch ( *buf ) {
+           case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
+           case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
+           case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
+           case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
+           default:
+               if ( isprint(*buf) ) {
+                   *tmp++ = *buf;
+               } else {
+                   sprintf( tmp, "\\x%02X", (unsigned char)*buf);
+                   tmp += strlen(tmp);
+               }
+           }
+           buf++;
+           *tmp = '\0';
+       }
+       debug_("%s", work);
+    }
+    
+    debug_("\"\n");
+}
+
+
+void
+report_bytes( char *prefix, char *buf, int len )
+{
+    if ( ! f_debug )
+       return;
+    debug( "%s", prefix );
+    while ( 0 < len ) {
+       fprintf( stderr, " %02x", *(unsigned char *)buf);
+       buf++;
+       len--;
+    }
+    fprintf(stderr, "\n");
+    return;
+}
+
+int
+atomic_out( SOCKET s, char *buf, int size )
+{
+    int ret, len;
+
+    assert( buf != NULL );
+    assert( 0<=size );
+    /* do atomic out */
+    ret = 0;
+    while ( 0 < size ) {
+       len = send( s, buf+ret, size, 0 );
+       if ( len == -1 )
+           fatal("atomic_out() failed to send(), %d\n", socket_errno());
+       ret += len;
+       size -= len;
+    }
+    if (!f_report) {
+       debug("atomic_out()  [some bytes]\n");
+       debug(">>> xx xx xx xx ...\n");
+    } else {
+       debug("atomic_out()  [%d bytes]\n", ret);
+       report_bytes(">>>", buf, ret);
+    }
+    return ret;
+}
+
+int
+atomic_in( SOCKET s, char *buf, int size )
+{
+    int ret, len;
+
+    assert( buf != NULL );
+    assert( 0<=size );
+    
+    /* do atomic in */
+    ret = 0;
+    while ( 0 < size ) {
+       len = recv( s, buf+ret, size, 0 );
+       if ( len == -1 ) {
+           fatal("atomic_in() failed to recv(), %d\n", socket_errno());
+       } else if ( len == 0 ) {
+           fatal( "Connection closed by peer.\n");
+       }
+       ret += len;
+       size -= len;
+    }
+    if (!f_report) {
+       debug("atomic_in()  [some bytes]\n");
+       debug("<<< xx xx xx xx ...\n");
+    } else {
+       debug("atomic_in() [%d bytes]\n", ret);
+       report_bytes("<<<", buf, ret);
+    }
+    return ret;
+}
+
+int
+line_input( SOCKET s, char *buf, int size )
+{
+    char *dst = buf;
+    if ( size == 0 )
+       return 0;                               /* no error */
+    size--;
+    while ( 0 < size ) {
+       switch ( recv( s, dst, 1, 0) ) {        /* recv one-by-one */
+       case SOCKET_ERROR:
+           error("recv() error\n");
+           return -1;                          /* error */
+       case 0:
+           size = 0;                           /* end of stream */
+           break;
+       default:
+           /* continue reading until last 1 char is EOL? */
+           if ( *dst == '\n' ) {
+               /* finished */
+               size = 0;
+           } else {
+               /* more... */
+               size--;
+           }
+           dst++;
+       }
+    }
+    *dst = '\0';
+    report_text( "<<<", buf);
+    return 0;
+}
+
+void
+skip_all_lines (SOCKET s)
+{
+    char buf[1024];
+    while (0 < recv (s, buf, sizeof(buf), 0))
+       ;
+}
+
+/* cut_token()
+   Span token in given string STR until char in DELIM is appeared.
+   Then replace contiguous DELIMS with '\0' for string termination
+   and returns next pointer.
+   If no next token, return NULL.
+*/
+char *
+cut_token( char *str, char *delim)
+{
+    char *ptr = str + strcspn(str, delim);
+    char *end = ptr + strspn(ptr, delim);
+    if ( ptr == str )
+       return NULL;
+    while ( ptr < end )
+       *ptr++ = '\0';
+    return ptr;
+}
+
+const char *
+lookup(int num, LOOKUP_ITEM *items)
+{
+    int i = 0;
+    while (0 <= items[i].num) {
+       if (items[i].num == num)
+           return items[i].str;
+       i++;
+    }
+    return "(unknown)";
+}
+
+/* readpass()
+   password input routine 
+   Use ssh-askpass (same mechanism to OpenSSH)
+*/
+char *
+readpass( const char* prompt, ...)
+{
+    static char buf[1000];                     /* XXX, don't be fix length */
+    va_list args;
+    va_start(args, prompt);
+    vsprintf(buf, prompt, args);
+    va_end(args);
+
+    if ( getparam(ENV_SSH_ASKPASS)
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+        && getenv("DISPLAY")
+#endif /* not _WIN32 && not __CYGWIN32__ */
+       ) {
+       /* use ssh-askpass to get password */
+       FILE *fp;
+       char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
+       cmd = malloc(strlen(askpass) +1 +1 +strlen(buf) +1);
+       sprintf(cmd, "%s \"%s\"", askpass, buf);
+       fp = popen(cmd, "r");
+       free(cmd);
+       if ( fp == NULL )
+           return NULL;                        /* fail */
+       buf[0] = '\0';
+       if (fgets(buf, sizeof(buf), fp) == NULL)
+           return NULL;                        /* fail */
+       fclose(fp);
+    } else {
+       tty_readpass( buf, buf, sizeof(buf));
+    }
+    buf[strcspn(buf, "\r\n")] = '\0';
+    return buf;
+}
+
+static int
+socks5_do_auth_userpass( int s )
+{
+    unsigned char buf[1024], *ptr;
+    char *pass;
+    int len;
+    
+    /* do User/Password authentication. */
+    /* This feature requires username and password from 
+       command line argument or environment variable,
+       or terminal. */
+    if ( relay_user == NULL )
+       fatal("User name cannot be decided.\n");
+
+    /* get password from environment variable if exists. */
+    if ((pass=getparam(ENV_SOCKS5_PASSWORD)) == NULL &&
+       (pass=readpass("Enter SOCKS5 password for %s@%s: ",
+                      relay_user, relay_host)) == NULL )
+       fatal("Cannot get password for user: %s\n", relay_user);
+
+    /* make authentication packet */
+    ptr = buf;
+    PUT_BYTE( ptr++, 1 );                      /* subnegotiation ver.: 1 */
+    len = strlen( relay_user );                        /* ULEN and UNAME */
+    PUT_BYTE( ptr++, len );
+    strcpy( ptr, relay_user );
+    ptr += len;
+    len = strlen( pass );                      /* PLEN and PASSWD */
+    PUT_BYTE( ptr++, strlen(pass));
+    strcpy( ptr, pass );
+    ptr += len;
+    
+    /* send it and get answer */
+    f_report = 0;
+    atomic_out( s, buf, ptr-buf );
+    f_report = 1;
+    atomic_in( s, buf, 2 );
+
+    /* check status */
+    if ( buf[1] == 0 )
+       return 0;                               /* success */
+    else
+       return -1;                              /* fail */
+}
+
+static const char *
+socks5_getauthname( int auth )
+{
+    switch ( auth ) {
+    case SOCKS5_AUTH_REJECT: return "REJECTED";
+    case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
+    case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
+    case SOCKS5_AUTH_USERPASS: return "USERPASS";
+    case SOCKS5_AUTH_CHAP: return "CHAP";
+    case SOCKS5_AUTH_EAP: return "EAP";
+    case SOCKS5_AUTH_MAF: return "MAF";
+    default: return "(unknown)";
+    }
+}
+
+typedef struct {
+    char* name;
+    unsigned char auth;
+} AUTH_METHOD_ITEM;
+
+AUTH_METHOD_ITEM socks5_auth_table[] = {
+    { "none", SOCKS5_AUTH_NOAUTH },
+    { "gssapi", SOCKS5_AUTH_GSSAPI },
+    { "userpass", SOCKS5_AUTH_USERPASS },
+    { "chap", SOCKS5_AUTH_CHAP },
+    { NULL, -1 },
+};
+
+int
+socks5_auth_parse_1(char *start, char *end){
+    int i, len;
+    for ( ; *start; start++ )
+       if ( *start != ' ' && *start != '\t') break;
+    for ( end--; end >= start; end-- ) {
+       if ( *end != ' ' && *end != '\t'){
+           end++;
+           break;
+       }
+    }
+    len = end - start;
+    for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
+       if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
+           return socks5_auth_table[i].auth;
+       }
+    }
+    fatal("Unknown auth method: %s\n", start);
+    return -1;
+}
+
+int
+socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
+    char *end;
+    int i = 0;
+    while ( i < max_auth ) {
+       if ( *start && ( end = strchr(start, ',') ) ){
+           auth_list[i++] = socks5_auth_parse_1(start, end);
+           start = ++end;
+       } else {
+           break;
+       }
+    }
+    if ( *start && ( i < max_auth ) ){
+       for( end = start; *end; end++ );
+       auth_list[i++] = socks5_auth_parse_1(start, end);
+    } else {
+       fatal("Too much auth method.\n");
+    }
+    return i;
+}
+
+/* begin SOCKS5 relaying
+   And no authentication is supported.
+ */
+int
+begin_socks5_relay( SOCKET s )
+{
+    unsigned char buf[256], *ptr, *env = socks5_auth;
+    unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
+    int len, auth_result, i;
+
+    debug( "begin_socks_relay()\n");
+    
+    /* request authentication */
+    ptr = buf;
+    PUT_BYTE( ptr++, 5);                       /* SOCKS version (5) */
+
+    if ( env == NULL )
+       env = getparam(ENV_SOCKS5_AUTH);
+    if ( env == NULL ) {
+       /* add no-auth authentication */
+       auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
+       /* add user/pass authentication */
+       auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
+    } else {
+       n_auth = socks5_auth_parse(env, auth_list, 10);
+    }
+    PUT_BYTE( ptr++, n_auth);                  /* num auth */
+    for (i=0; i<n_auth; i++) {
+       debug("available auth method[%d] = %s (0x%02x)\n",
+             i, socks5_getauthname(auth_list[i]), auth_list[i]);
+       PUT_BYTE( ptr++, auth_list[i]);         /* authentications */
+    }
+    atomic_out( s, buf, ptr-buf );             /* send requst */
+    atomic_in( s, buf, 2 );                    /* recv response */
+    if ( (buf[0] != 5) ||                      /* ver5 response */
+        (buf[1] == 0xFF) ) {                   /* check auth method */
+       error("No auth method accepted.\n");
+       return -1;
+    }
+    auth_method = buf[1];
+
+    debug("auth method: %s\n", socks5_getauthname(auth_method));
+    auth_result = -1;
+    switch ( auth_method ) {
+    case SOCKS5_AUTH_REJECT:
+       error("No acceptable authentication method\n");
+       return -1;                              /* fail */
+       
+    case SOCKS5_AUTH_NOAUTH:
+       /* nothing to do */
+       auth_result = 0;
+       break;
+
+    case SOCKS5_AUTH_USERPASS:
+       auth_result = socks5_do_auth_userpass(s);
+       break;
+       
+    default:
+       error("Unsupported authentication method: %s\n",
+             socks5_getauthname( auth_method ));
+       return -1;                              /* fail */
+    }
+    if ( auth_result != 0 ) {
+       error("Authentication failed.\n");
+       return -1;
+    }
+    /* request to connect */
+    ptr = buf;
+    PUT_BYTE( ptr++, 5);                       /* SOCKS version (5) */
+    PUT_BYTE( ptr++, 1);                       /* CMD: CONNECT */
+    PUT_BYTE( ptr++, 0);                       /* FLG: 0 */
+    if ( dest_addr.sin_addr.s_addr == 0 ) {
+       /* resolved by SOCKS server */
+       PUT_BYTE( ptr++, 3);                    /* ATYP: DOMAINNAME */
+       len = strlen(dest_host);
+       PUT_BYTE( ptr++, len);                  /* DST.ADDR (len) */
+       memcpy( ptr, dest_host, len );          /* (hostname) */
+       ptr += len;
+    } else {
+       /* resolved localy */
+       PUT_BYTE( ptr++, 1 );                   /* ATYP: IPv4 */
+       memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
+       ptr += sizeof(dest_addr.sin_addr);
+    }
+    PUT_BYTE( ptr++, dest_port>>8);    /* DST.PORT */
+    PUT_BYTE( ptr++, dest_port&0xFF);
+    atomic_out( s, buf, ptr-buf);              /* send request */
+    atomic_in( s, buf, 4 );                    /* recv response */
+    if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) {  /* check reply code */
+       error("Got error response from SOCKS server: %d (%s).\n",
+             buf[1], lookup(buf[1], socks5_rep_names));
+       return -1;
+    }
+    ptr = buf + 4;
+    switch ( buf[3] ) {                                /* case by ATYP */
+    case 1:                                    /* IP v4 ADDR*/
+       atomic_in( s, ptr, 4+2 );               /* recv IPv4 addr and port */
+       break;
+    case 3:                                    /* DOMAINNAME */
+       atomic_in( s, ptr, 1 );                 /* recv name and port */
+       atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
+       break;
+    case 4:                                    /* IP v6 ADDR */
+       atomic_in( s, ptr, 16+2 );              /* recv IPv6 addr and port */
+       break;
+    }
+    
+    /* Conguraturation, connected via SOCKS5 server! */
+    return 0;
+}
+
+/* begin SOCKS protocol 4 relaying
+   And no authentication is supported.
+
+   There's SOCKS protocol version 4 and 4a. Protocol version
+   4a has capability to resolve hostname by SOCKS server, so
+   we don't need resolving IP address of destination host on
+   local machine.
+
+   Environment variable SOCKS_RESOLVE directs how to resolve
+   IP addess. There's 3 keywords allowed; "local", "remote"
+   and "both" (case insensitive). Keyword "local" means taht
+   target host name is resolved by localhost resolver
+   (usualy with gethostbyname()), "remote" means by remote
+   SOCKS server, "both" means to try resolving by localhost
+   then remote.
+
+   SOCKS4 protocol and authentication of SOCKS5 protocol
+   requires user name on connect request.
+   User name is determined by following method.
+   
+   1. If server spec has user@hostname:port format then
+      user part is used for this SOCKS server.
+      
+   2. Get user name from environment variable LOGNAME, USER
+      (in this order).
+
+*/
+int
+begin_socks4_relay( SOCKET s )
+{
+    unsigned char buf[256], *ptr;
+
+    debug( "begin_socks_relay()\n");
+    
+    /* make connect request packet 
+       protocol v4:
+         VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
+       protocol v4a:
+         VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
+    */
+    ptr = buf;
+    PUT_BYTE( ptr++, 4);                       /* protocol version (4) */
+    PUT_BYTE( ptr++, 1);                       /* CONNECT command */
+    PUT_BYTE( ptr++, dest_port>>8);    /* destination Port */
+    PUT_BYTE( ptr++, dest_port&0xFF);
+    /* destination IP */
+    memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
+    ptr += sizeof(dest_addr.sin_addr);
+    if ( dest_addr.sin_addr.s_addr == 0 )
+       *(ptr-1) = 1;                           /* fake, protocol 4a */
+    /* username */
+    strcpy( ptr, relay_user );
+    ptr += strlen( relay_user ) +1;
+    /* destination host name (for protocol 4a) */
+    if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
+       strcpy( ptr, dest_host );
+       ptr += strlen( dest_host ) +1;
+    }
+    /* send command and get response
+       response is: VN:1, CD:1, PORT:2, ADDR:4 */
+    atomic_out( s, buf, ptr-buf);              /* send request */
+    atomic_in( s, buf, 8 );                    /* recv response */
+    if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) {  /* check reply code */
+       error("Got error response: %d: '%s'.\n",
+             buf[1], lookup(buf[1], socks4_rep_names));
+       return -1;                              /* failed */
+    }
+    
+    /* Conguraturation, connected via SOCKS4 server! */
+    return 0;
+}
+
+int
+sendf(SOCKET s, const char *fmt,...)
+{
+    static char buf[10240];                    /* xxx, enough? */
+    
+    va_list args;
+    va_start( args, fmt );
+    vsprintf( buf, fmt, args );
+    va_end( args );
+    
+    report_text(">>>", buf);
+    if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
+       debug("failed to send http request. errno=%d\n", socket_errno());
+       return -1;
+    }
+    return 0;
+}
+
+const char *base64_table =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *
+make_base64_string(const char *str)
+{
+    static char *buf;
+    unsigned char *src;
+    char *dst;
+    int bits, data, src_len, dst_len;
+    /* make base64 string */
+    src_len = strlen(str);
+    dst_len = (src_len+2)/3*4;
+    buf = malloc(dst_len+1);
+    bits = data = 0;
+    src = (unsigned char *)str;
+    dst = (unsigned char *)buf;
+    while ( dst_len-- ) {
+       if ( bits < 6 ) {
+           data = (data << 8) | *src;
+           bits += 8;
+           if ( *src != 0 )
+               src++;
+       }
+       *dst++ = base64_table[0x3F & (data >> (bits-6))];
+       bits -= 6;
+    }
+    *dst = '\0';
+    /* fix-up tail padding */
+    switch ( src_len%3 ) {
+    case 1:
+       *--dst = '=';
+    case 2:
+       *--dst = '=';
+    }
+    return buf;
+}
+
+
+int
+basic_auth( SOCKET s, const char *user, const char *pass )
+{
+    char *userpass;
+    char *cred;
+    int len, ret;
+    
+    len = strlen(user)+strlen(pass)+1;
+    userpass = malloc(len+1);
+    sprintf(userpass,"%s:%s", user, pass);
+    cred = make_base64_string(userpass);
+    memset( userpass, 0, len );
+
+    f_report = 0;                              /* don't report for security */
+    ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
+    f_report = 1;
+    report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
+    
+    memset(cred, 0, strlen(cred));
+    free(cred);
+    
+    return ret;
+}
+
+/* begin relaying via HTTP proxy
+   Directs CONNECT method to proxy server to connect to
+   destination host (and port). It may not be allowed on your
+   proxy server.
+ */
+int
+begin_http_relay( SOCKET s )
+{
+    char buf[1024];
+    int result;
+    char *user = NULL, *pass = NULL;
+    char *auth_what;
+
+    debug("begin_http_relay()\n");
+
+    if (proxy_auth_type != PROXY_AUTH_NONE) {
+       /* Get username/password for authentication */
+       if ((user = relay_user) == NULL &&
+           (user = getparam(ENV_HTTP_PROXY_USER)) == NULL &&
+           (user = getparam(ENV_CONNECT_USER)) == NULL &&
+           (user = getusername()) == NULL )
+           fatal("Cannot decide username for proxy authentication.");
+       if ((pass = getparam(ENV_HTTP_PROXY_PASSWORD)) == NULL &&
+           (pass = getparam(ENV_CONNECT_PASSWORD)) == NULL &&
+           (pass = readpass("Enter proxy authentication password for %s@%s: ",
+                            user, relay_host)) == NULL)
+           fatal("Cannot decide password for proxy authentication.");
+       user = strdup(user);
+       pass = strdup(pass);
+    }
+    if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
+       return -1;
+    if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth(s, user, pass) < 0)
+       return -1;
+    if (sendf(s,"\r\n") < 0)
+       return -1;
+    if ( user ) { memset(user, 0, strlen(user)); free(user); }
+    if ( pass ) { memset(pass, 0, strlen(pass)); free(pass); }
+    
+    /* get response */
+    if ( line_input(s, buf, sizeof(buf)) < 0 ) {
+       debug("failed to read http response.\n");
+       return -1;
+    }
+    /* check status */
+    result = atoi(strchr(buf,' '));
+    switch ( result ) {
+    case 200:
+       /* Conguraturation, connected via http proxy server! */
+       debug("connected, start user session.\n");
+       break;
+    case 302:                                  /* redirect */
+       do {
+           if (line_input(s, buf, sizeof(buf)))
+               break;
+           downcase(buf);
+           if (expect(buf, "Location: ")) {
+               relay_host = cut_token(buf, "//");
+               cut_token(buf, "/");
+               relay_port = atoi(cut_token(buf, ":"));
+           }
+       } while (strcmp(buf,"\r\n") != 0);
+       skip_all_lines(s);
+       return START_RETRY;
+
+    /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
+     * not strictly the correct response, but some proxies do send this (e.g.
+     * Symantec's Raptor firewall) */
+    case 401:                                  /* WWW-Auth required */
+    case 407:                                  /* Proxy-Auth required */
+       /** NOTE: As easy implementation, we support only BASIC scheme
+           and ignore realm. */
+       /* If proxy_auth_type is PROXY_AUTH_BASIC and get
+        this result code, authentication was failed. */
+       if (proxy_auth_type != PROXY_AUTH_NONE) {
+           error("Authentication failed.\n");
+           return -1;
+       }
+       auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
+       do {
+           if ( line_input(s, buf, sizeof(buf)) ) {
+               break;
+           }
+           downcase(buf);
+           if (expect(buf, auth_what)) {
+               /* parse type and realm */
+               char *scheme, *realm;
+               scheme = cut_token(buf, " ");
+               realm = cut_token(scheme, " ");
+               if ( scheme == NULL || realm == NULL ) {
+                   debug("Invalid format of %s field.", auth_what);
+                   return -1;                  /* fail */
+               }
+               /* check type */
+               if (expect(scheme, "basic")) {
+                   proxy_auth_type = PROXY_AUTH_BASIC;
+               } else {
+                   debug("Unsupported authentication type: %s", scheme);
+                   return -1;
+               }
+           }
+       } while (strcmp(buf,"\r\n") != 0);
+       skip_all_lines(s);
+       if ( proxy_auth_type == PROXY_AUTH_NONE ) {
+           debug("Can't find %s in response header.", auth_what);
+           return -1;
+       } else {
+           return START_RETRY;
+       }
+       
+    default:
+       /* Not allowed */
+       debug("http proxy is not allowed.\n");
+       return -1;
+    }
+    /* skip to end of response header */
+    do {
+       if ( line_input(s, buf, sizeof(buf) ) ) {
+           debug("Can't skip response headers\n");
+           return -1;
+       }
+    } while ( strcmp(buf,"\r\n") != 0 );
+
+    return 0;
+}
+
+
+#ifdef _WIN32
+/* ddatalen()
+   Returns 1 if data is available, otherwise return 0
+ */
+int
+fddatalen( SOCKET fd )
+{
+    DWORD len = 0;
+    struct stat st;
+    fstat( 0, &st );
+    if ( st.st_mode & _S_IFIFO ) { 
+       /* in case of PIPE */
+       if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
+                            NULL, 0, NULL, &len, NULL) ) {
+           if ( GetLastError() == ERROR_BROKEN_PIPE ) {
+               /* PIPE source is closed */
+               /* read() will detects EOF */
+               len = 1;
+           } else {
+               fatal("PeekNamedPipe() failed, errno=%d\n",
+                     GetLastError());
+           }
+       }
+    } else if ( st.st_mode & _S_IFREG ) {
+       /* in case of regular file (redirected) */
+       len = 1;                        /* always data ready */
+    } else if ( _kbhit() ) {
+       /* in case of console */
+       len = 1;
+    }
+    return len;
+}
+#endif /* _WIN32 */
+
+/* relay byte from stdin to socket and fro socket to stdout */
+int
+do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
+{
+    /** vars for local input data **/
+    char lbuf[1024];                           /* local input buffer */
+    int lbuf_len;                              /* available data in lbuf */
+    int f_local;                               /* read local input more? */
+    /** vars for remote input data **/
+    char rbuf[1024];                           /* remote input buffer */
+    int rbuf_len;                              /* available data in rbuf */
+    int f_remote;                              /* read remote input more? */
+    /** other variables **/
+    int nfds, len;
+    fd_set *ifds, *ofds;
+    struct timeval *tmo;
+#ifdef _WIN32
+    struct timeval win32_tmo;
+#endif /* _WIN32 */
+    
+    /* repeater between stdin/out and socket  */
+    nfds = ((local_in<remote)? remote: local_in) +1;
+    ifds = FD_ALLOC(nfds);
+    ofds = FD_ALLOC(nfds);
+    f_local = 1;                               /* yes, read from local */
+    f_remote = 1;                              /* yes, read from remote */
+    lbuf_len = 0;
+    rbuf_len = 0;
+
+    while ( f_local || f_remote ) {
+       FD_ZERO( ifds );
+       FD_ZERO( ofds );
+       tmo = NULL;
+
+       /** prepare for reading local input **/
+       if ( f_local && (lbuf_len < sizeof(lbuf)) ) {
+#ifdef _WIN32
+           if ( local_type != LOCAL_SOCKET ) {
+               /* select() on Winsock is not accept standard handle.
+                  So use select() with short timeout and checking data
+                  in stdin by another method. */
+               win32_tmo.tv_sec = 0;
+               win32_tmo.tv_usec = 10*1000;    /* 10 ms */
+               tmo = &win32_tmo;
+           } else
+#endif /* !_WIN32 */
+           FD_SET( local_in, ifds );
+       }
+       
+       /** prepare for reading remote input **/
+       if ( f_remote && (rbuf_len < sizeof(rbuf)) ) {
+           FD_SET( remote, ifds );
+       }
+       
+       /* FD_SET( local_out, ofds ); */
+       /* FD_SET( remote, ofds ); */
+       
+       if ( select( nfds, ifds, ofds, NULL, tmo ) == -1 ) {
+           /* some error */
+           error( "select() failed, %d\n", socket_errno());
+           return -1;
+       }
+#ifdef _WIN32
+       /* fake ifds if local is stdio handle because
+           select() of Winsock does not accept stdio
+           handle. */
+       if (f_local && (local_type!=LOCAL_SOCKET) && (0<fddatalen(local_in)))
+           FD_SET(0,ifds);             /* data ready */
+#endif
+
+       /* remote => local */
+       if ( FD_ISSET(remote, ifds) && (rbuf_len < sizeof(rbuf)) ) {
+           len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
+           if ( len == 0 ) {
+               debug("connection closed by peer\n");
+               f_remote = 0;                   /* no more read from socket */
+               f_local = 0;
+           } else if ( len == -1 ) {
+               if (socket_errno() != ECONNRESET) {
+                   /* error */
+                   fatal("recv() faield, %d\n", socket_errno());
+               } else {
+                   debug("ECONNRESET detected\n");
+               }
+           } else {
+               debug("recv %d bytes\n", len);
+               if ( 1 < f_debug )              /* more verbose */
+                   report_bytes( "<<<", rbuf, rbuf_len);
+               rbuf_len += len;
+           }
+       }
+       
+       /* local => remote */
+       if ( FD_ISSET(local_in, ifds) && (lbuf_len < sizeof(lbuf)) ) {
+           if (local_type == LOCAL_SOCKET)
+               len = recv(local_in, lbuf + lbuf_len,
+                          sizeof(lbuf)-lbuf_len, 0);
+           else
+               len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
+           if ( len == 0 ) {
+               /* stdin is EOF */
+               debug("local input is EOF\n");
+               shutdown(remote, 1);            /* no-more writing */
+               f_local = 0;
+           } else if ( len == -1 ) {
+               /* error on reading from stdin */
+               fatal("recv() failed, errno = %d\n", errno);
+           } else {
+               /* repeat */
+               lbuf_len += len;
+           }
+       }
+       
+       /* flush data in buffer to socket */
+       if ( 0 < lbuf_len ) {
+           len = send(remote, lbuf, lbuf_len, 0);
+           if ( 1 < f_debug )          /* more verbose */
+               report_bytes( ">>>", lbuf, lbuf_len);
+           if ( len == -1 ) {
+               fatal("send() failed, %d\n", socket_errno());
+           } else if ( 0 < len ) {
+               /* move data on to top of buffer */
+               debug("send %d bytes\n", len);
+               lbuf_len -= len;
+               if ( 0 < lbuf_len )
+                   memcpy( lbuf, lbuf+len, lbuf_len );
+               assert( 0 <= lbuf_len );
+           }
+       }
+       
+       /* flush data in buffer to local output */
+       if ( 0 < rbuf_len ) {
+           if (local_type == LOCAL_SOCKET)
+               len = send( local_out, rbuf, rbuf_len, 0);
+           else
+               len = write( local_out, rbuf, rbuf_len);
+           if ( len == -1 ) {
+               fatal("output (local) failed, errno=%d\n", errno);
+           } 
+           rbuf_len -= len;
+           if ( len < rbuf_len )
+               memcpy( rbuf, rbuf+len, rbuf_len );
+           assert( 0 <= rbuf_len );
+       }
+
+    }
+    
+    return 0;
+}
+
+
+int
+accept_connection (u_short port)
+{
+    int sock;
+    int connection;
+    struct sockaddr_in name;
+
+    struct sockaddr client;
+    int socklen;
+
+    /* Create the socket. */
+    debug("Creating source port to forward.\n");
+    sock = socket (PF_INET, SOCK_STREAM, 0);
+    if (sock < 0)
+       fatal("socket() failed, errno=%d\n", socket_errno());
+
+    /* Give the socket a name. */
+    name.sin_family = AF_INET;
+    name.sin_port = htons (port);
+    name.sin_addr.s_addr = htonl (INADDR_ANY);
+    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+       fatal ("bind() failed, errno=%d\n", socket_errno());
+
+    if (listen( sock, 1) < 0)
+       fatal ("listen() failed, errno=%d\n", socket_errno());
+
+    socklen = sizeof(client);
+    connection = accept( sock, &client, &socklen);
+    if ( connection < 0 )
+       fatal ("accept() failed, errno=%d\n", socket_errno());
+    
+    shutdown(sock, 2);
+
+    return connection;
+}
+
+
+
+/** Main of program **/
+int
+main( int argc, char **argv )
+{
+    int ret;
+    SOCKET  remote;                            /* socket */
+    SOCKET  local_in;                          /* Local input */
+    SOCKET  local_out;                         /* Local output */
+#ifdef _WIN32
+    WSADATA wsadata;
+    WSAStartup( 0x101, &wsadata);
+#endif /* _WIN32 */
+
+    /* initialization */
+    make_revstr();
+    getarg( argc, argv );
+    debug("Program is $Revision$\n");
+
+    /* Open local_in and local_out if forwarding a port */
+    if ( local_type == LOCAL_SOCKET ) {
+       /* Relay between local port and destination */
+        local_in = local_out = accept_connection( local_port );
+    } else {
+       /* Relay between stdin/stdout and desteination */
+       local_in = 0;
+       local_out = 1;
+#ifdef _WIN32
+       _setmode(local_in, O_BINARY);
+       _setmode(local_out, O_BINARY);
+#endif
+    }
+
+retry:
+#ifndef _WIN32
+    if (0 < connect_timeout)
+       set_timeout (connect_timeout);
+#endif /* not _WIN32 */
+
+    /* make connection */
+    if ( relay_method == METHOD_DIRECT ) {
+       remote = open_connection (dest_host, dest_port);
+       if ( remote == SOCKET_ERROR )
+           fatal( "Unable to connect to destination host, errno=%d\n",
+                  socket_errno());
+    } else {
+       remote = open_connection (relay_host, relay_port);
+       if ( remote == SOCKET_ERROR )
+           fatal( "Unable to connect to relay host, errno=%d\n",
+                  socket_errno());
+    }
+
+    /** resolve destination host (SOCKS) **/
+    if (relay_method == METHOD_SOCKS &&
+       socks_resolve == RESOLVE_LOCAL &&
+       local_resolve (dest_host, &dest_addr, &socks_ns) < 0) {
+       fatal("Unknown host: %s", dest_host);
+    }
+
+    /** relay negociation **/
+    switch ( relay_method ) {
+    case METHOD_SOCKS:
+       if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
+            ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
+           fatal( "failed to begin relaying via SOCKS.\n");
+       break;
+
+    case METHOD_HTTP:
+       ret = begin_http_relay(remote);
+       switch (ret) {
+       case START_ERROR:
+           fatal("failed to begin relaying via HTTP.\n");
+       case START_OK:
+           break;
+       case START_RETRY:
+           /* retry with authentication */
+           goto retry;
+       }
+       break;
+    }
+    debug("connected\n");
+
+#ifndef _WIN32
+    if (0 < connect_timeout)
+       set_timeout (0);
+#endif /* not _WIN32 */
+
+    /* main loop */
+    debug ("start relaying.\n");
+    do_repeater(local_in, local_out, remote);
+    debug ("relaying done.\n");
+    closesocket(remote);
+    if ( local_type == LOCAL_SOCKET)
+       closesocket(local_in);
+#ifdef _WIN32
+    WSACleanup();
+#endif /* _WIN32 */
+    debug ("that's all, bye.\n");
+
+    return 0;
+}
+
+/* ------------------------------------------------------------
+   Local Variables:
+   compile-command: "cc connect.c -o connect"
+   fill-column: 74
+   comment-column: 48
+   End:
+   ------------------------------------------------------------ */
+
+/*** end of connect.c ***/
This page took 0.205663 seconds and 4 git commands to generate.