/***********************************************************************
* connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
*
- * Copyright (c) 2000, 2001 Shun-ichi Goto
+ * Copyright (c) 2000-2004 Shun-ichi Goto
* Copyright (c) 2002, J. Grant (English Corrections)
*
* This program is free software; you can redistribute it and/or
* ==============
*
* 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
+ * http://www.taiyo.co.jp/~gotoh/ssh/connect.c
*
* Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
* is available:
- * http://www.imasy.or.jp/~gotoh/ssh/ssh-askpass.exe.gz
+ * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz
+ *
+ * See more detail:
+ * http://www.taiyo.co.jp/~gotoh/ssh/connect.html
*
* How To Compile
* ==============
* or
* $ gcc connect.c -o connect
*
+ * on Mac OS X environment:
+ * $ gcc connect.c -o connect -lresolv
+ * or
+ * $ gcc connect.c -o connect -DBIND_8_COMPAT=1
+ *
* 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]
+ * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
* [-H [user@]proxy-server[:port]]
* [-S [user@]socks-server[:port]]
+ * [-T proxy-server[:port]]
+ * [-c telnet proxy command]
* host port
*
* "host" and "port" is for the target hostname and port-number to
* "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
+ * The '-p' option will forward a local TCP port instead of using the
* standard input and output.
*
+ * The '-P' option is same to '-p' except keep remote session. The
+ * program repeats waiting the port with holding remote session without
+ * disconnecting. To disconnect the remote session, send EOF to stdin or
+ * kill the program.
+ *
* The '-w' option specifys timeout seconds for making connection with
* TARGET host.
*
* ===========================
*
* User name for authentication is specifed by an environment variable
- * or system login name. And Password is specified from environment
+ * 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.
*
* 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,
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
-#ifndef __CYGWIN32__
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+#define WITH_RESOLVER 1
#include <arpa/nameser.h>
#include <resolv.h>
-#endif /* !__CYGWIN32__ */
+#else /* not ( not _WIN32 && not __CYGWIN32__) */
+#undef WITH_RESOLVER
+#endif /* not ( not _WIN32 && not __CYGWIN32__) */
#endif /* !_WIN32 */
#ifdef _WIN32
/* consider Borland C */
#ifdef __BORLANDC__
#define _kbhit kbhit
+#define _setmode setmode
#endif
/* help message.
- Win32 environment does not support -R option.
- Win32 native compilers does not support -w option, yet.
+ Win32 environment does not support -R option (vc and cygwin)
+ Win32 native compilers does not support -w option, yet (vc)
*/
-static char *usage =
+static char *usage = "usage: %s [-dnhst45] [-p local-port]"
#ifdef _WIN32
-"usage: %s [-dnhs45] \n"
-#else /* not _WIN32 */
#ifdef __CYGWIN32__
-/* help message for UNIX */
-"usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
+"[-w timeout] \n" /* cygwin cannot -R */
#else /* not __CYGWIN32__ */
-"usage: %s [-dnhs45] [-R resolve] [-w timeout] \n"
-#endif /* __CYGWIN32__ */
+" \n" /* VC cannot -w nor -R */
+#endif /* not __CYGWIN32__ */
+#else /* not _WIN32 */
+/* help message for UNIX */
+"[-R resolve] [-w timeout] \n"
#endif /* not _WIN32 */
" [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
+" [-T proxy-server[:port]]\n"
+" [-c telnet-proxy-command]\n"
" host port\n";
/* name of this program */
char *rcs_revstr = "$Revision$";
char *revstr = NULL;
+/* set of character for strspn() */
+const char *digits = "0123456789";
+const char *dotdigits = "0123456789.";
+
/* options */
int f_debug = 0;
int connect_timeout = 0;
/* local input type */
-#define LOCAL_STDIO 0
-#define LOCAL_SOCKET 1
+#define LOCAL_STDIO 0
+#define LOCAL_SOCKET 1
char *local_type_names[] = { "stdio", "socket" };
int local_type = LOCAL_STDIO;
-u_short local_port = 0;
+u_short local_port = 0; /* option 'p' */
+int f_hold_session = 0; /* option 'P' */
+
+char *telnet_command = "telnet %h %p";
/* utiity types, pair holder of number and string */
typedef struct {
#define METHOD_DIRECT 1
#define METHOD_SOCKS 2
#define METHOD_HTTP 3
-char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP" };
+#define METHOD_TELNET 4
+char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
-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 */
+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;
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 */
+#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 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 */
+#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)"},
#define RESOLVE_BOTH 3
char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
-int socks_version = 5; /* SOCKS protocol version */
+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_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_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_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */
+#define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */
+#define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */
+#define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */
+#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */
-#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */
+#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_TELNET_PROXY "TELNET_PROXY" /* common env var */
-#define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */
+#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 */
+#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */
/* Prefix string of HTTP_PROXY */
#define HTTP_PROXY_PREFIX "http://"
#define PROXY_AUTH_DIGEST 2
int proxy_auth_type = PROXY_AUTH_NONE;
+/* reason of end repeating */
+#define REASON_UNK -2
+#define REASON_ERROR -1
+#define REASON_CLOSED_BY_LOCAL 0
+#define REASON_CLOSED_BY_REMOTE 1
/* return value of relay start function. */
#define START_ERROR -1
#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 */
{
va_list args;
if ( f_debug ) {
- va_start( args, fmt );
- fprintf(stderr, "DEBUG: ");
- vfprintf( stderr, fmt, args );
- va_end( args );
+ va_start( args, fmt );
+ fprintf(stderr, "DEBUG: ");
+ vfprintf( stderr, fmt, args );
+ va_end( args );
}
}
void
-debug_( const char *fmt, ... ) /* without prefix */
+debug_( const char *fmt, ... ) /* without prefix */
{
va_list args;
if ( f_debug ) {
- va_start( args, fmt );
- vfprintf( stderr, fmt, args );
- va_end( args );
+ va_start( args, fmt );
+ vfprintf( stderr, fmt, args );
+ va_end( args );
}
}
exit (EXIT_FAILURE);
}
+
+void *
+xmalloc (size_t size)
+{
+ void *ret = malloc(size);
+ if (ret == NULL)
+ fatal("Cannot allocate memory: %d bytes.\n", size);
+ return ret;
+}
+
void
downcase( char *buf )
{
while ( *buf ) {
- if ( isupper(*buf) )
- *buf += 'a'-'A';
- buf++;
+ if ( isupper(*buf) )
+ *buf += 'a'-'A';
+ buf++;
}
}
+char *
+expand_host_and_port (const char *fmt, const char *host, int port)
+{
+ const char *src;
+ char *buf, *dst, *ptr;
+ size_t len = strlen(fmt) + strlen(host) + 20;
+ buf = xmalloc (len);
+ dst = buf;
+ src = fmt;
+
+ while (*src) {
+ if (*src == '%') {
+ switch (src[1]) {
+ case 'h':
+ strcpy (dst, host);
+ src += 2;
+ break;
+ case 'p':
+ sprintf (dst, "%d", port);
+ src += 2;
+ break;
+ default:
+ src ++;
+ break;
+ }
+ dst = buf + strlen (buf);
+ } else if (*src == '\\') {
+ switch (src[1]) {
+ case 'r': /* CR */
+ *dst++ = '\r';
+ src += 2;
+ break;
+ case 'n': /* LF */
+ *dst++ = '\n';
+ src += 2;
+ break;
+ case 't': /* TAB */
+ *dst++ = '\t';
+ src += 2;
+ break;
+ default:
+ src ++;
+ break;
+ }
+ } else {
+ /* usual */
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ }
+ assert (strlen(buf) < len);
+ return buf;
+}
+
+
int
lookup_resolve( const char *str )
{
downcase( buf );
if ( strcmp( buf, "both" ) == 0 )
- ret = RESOLVE_BOTH;
+ ret = RESOLVE_BOTH;
else if ( strcmp( buf, "remote" ) == 0 )
- ret = RESOLVE_REMOTE;
+ 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;
+ ret = RESOLVE_LOCAL;
+ else if ( strspn(buf, dotdigits) == strlen(buf) ) {
+#ifndef WITH_RESOLVER
+ fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
+#endif /* not WITH_RESOLVER */
+ 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;
+ ret = RESOLVE_UNKNOWN;
free(buf);
return ret;
}
#else /* not _WIN32 */
struct passwd *pw = getpwuid(getuid());
if ( pw == NULL )
- fatal("getpwuid() failed for uid: %d\n", getuid());
+ fatal("getpwuid() failed for uid: %d\n", getuid());
return pw->pw_name;
#endif /* not _WIN32 */
}
{
int len = strlen(substr);
while ( 0 < len-- ) {
- if ( toupper(*str) != toupper(*substr) )
- return 0; /* not matched */
- str++, substr++;
+ if ( toupper(*str) != toupper(*substr) )
+ return 0; /* not matched */
+ str++, substr++;
}
- return 1; /* good, matched */
+ return 1; /* good, matched */
}
{ ENV_SOCKS5_RESOLVE, NULL },
{ ENV_SOCKS4_RESOLVE, NULL },
{ ENV_SOCKS5_USER, NULL },
+ { ENV_SOCKS5_PASSWD, NULL },
{ ENV_SOCKS5_PASSWORD, NULL },
{ ENV_HTTP_PROXY, NULL },
{ ENV_HTTP_PROXY_USER, NULL },
};
PARAMETER_ITEM*
-find_parameter_item(const char* name){
+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 ¶meter_table[i];
+ if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
+ return ¶meter_table[i];
}
return NULL;
}
void
-read_parameter_file_1(const char* name){
+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);
- }
- }
+ debug("Reading parameter file(%s)\n", name);
+ for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
+ char *p, *q, *param, *value;
+ 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){
+read_parameter_file(void)
+{
#ifndef _WIN32
char *name;
struct passwd *pw;
#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");
+ fatal("getpwuid() failed for uid: %d\n", getuid());
+ name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
strcpy(name, pw->pw_dir);
strcat(name, "/" PARAMETER_DOTFILE);
read_parameter_file_1(name);
}
char*
-getparam(const char* name){
+getparam(const char* name)
+{
char *value = getenv(name);
if ( value == NULL ){
- PARAMETER_ITEM *item = find_parameter_item(name);
- if ( item != NULL )
- value = item->value;
+ PARAMETER_ITEM *item = find_parameter_item(name);
+ if ( item != NULL )
+ value = item->value;
}
return value;
}
a = addr;
m = mask;
while ( 0 < addrlen-- )
- *a++ &= *m++;
+ *a++ &= *m++;
}
int
struct in_addr iaddr;
char *s;
if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
- error("direct address table is full!\n");
- return -1;
+ error("direct address table is full!\n");
+ return -1;
}
iaddr = *addr;
mask_addr(&iaddr, mask, sizeof(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));
+ &iaddr, sizeof(iaddr));
memcpy( &direct_addr_list[n_direct_addr_list].mask,
- mask, sizeof(*mask));
+ mask, sizeof(*mask));
direct_addr_list[n_direct_addr_list].negative = negative;
n_direct_addr_list++;
return 0;
}
-int
+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.
+ /* NOTE: */
+ /* Assume 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;
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;
+ 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 */
+ return 0; /* complete as format #3 */
if ( *ptr != '/' )
- return -1; /* format error */
+ return -1; /* format error */
/* Now parse mask for format #1 or #2 */
ptr++;
- mask->s_addr = 0; /* clear automatic mask */
-
+ 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 */
+ /* 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 */
+ /* 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;
}
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);
+ 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);
+ env = getparam(ENV_HTTP_DIRECT);
}
if ( env == NULL )
- env = getparam(ENV_CONNECT_DIRECT);
-
+ 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 */
+ return; /* no entry */
+ debug("making direct 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;
+ 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 memcmp( addr1, addr2, addrlen );
}
-int
-is_direct_address (const struct sockaddr_in *addr, int addrlen)
+int
+is_direct_address (const struct sockaddr_in *addr)
{
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*/
- }
- }
+ 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 */
+ return 0; /* not direct */
}
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 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;
- }
+ /* 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);
- }
+ /* 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;
}
tty = open(TTY_NAME, O_RDWR);
if ( tty < 0 ) {
- error("Unable to open %s\n", TTY_NAME);
- return -1; /* can't open tty */
+ error("Unable to open %s\n", TTY_NAME);
+ return -1; /* can't open tty */
}
if ( size <= 0 )
- return -1; /* no room */
+ return -1; /* no room */
write(tty, prompt, strlen(prompt));
buf[0] = '\0';
- tty_change_echo(tty, 0); /* disable echo */
+ 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 */
+ tty_change_echo(tty, 1); /* restore */
+ write(tty, "\n", 1); /* new line */
close(tty);
if ( strchr(buf,'\n') == NULL )
- return -1;
+ return -1;
if ( 0 < ret )
- buf[ret] = '\0';
+ buf[ret] = '\0';
return ret;
}
w32_intr_handler(DWORD dwCtrlType)
{
if ( dwCtrlType == CTRL_C_EVENT ) {
- intr_flag = 1;
- return TRUE;
+ intr_flag = 1;
+ return TRUE;
} else {
- return FALSE;
+ return FALSE;
}
}
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);
+ 0, NULL, OPEN_EXISTING, 0, NULL);
HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
- 0, NULL, OPEN_EXISTING, 0, NULL);
+ 0, NULL, OPEN_EXISTING, 0, NULL);
DWORD mode;
- int ret, bytes;
+ DWORD ret, bytes;
if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
- fatal("Cannot open console. (errno=%d)", GetLastError());
+ 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 */
+ SetConsoleMode(in, mode); /* enable echo */
SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
if ( intr_flag )
- GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
+ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
WriteFile(out,"\n", 1, &bytes, 0);
CloseHandle(in);
CloseHandle(out);
#endif /* _WIN32 */
+/*** User / Password ***/
+
+/* SOCKS5 and HTTP Proxy authentication may requires username and
+ password. We ll give it via environment variable or tty.
+ Username and password for authentication are decided by
+ following rules:
+
+ Username is taken from
+ 1) server location spec (i.e. user@host:port)
+ 2) environment variables (see tables.1)
+ 3) system account name currently logged in.
+
+ Table.1 Order of environment variables for username
+
+ | SOCKS v5 | SOCKS v4 | HTTP proxy |
+ --+-------------+-------------+-----------------+
+ 1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
+ --+-------------+-------------+ |
+ 2 | SOCKS_USER | |
+ --+---------------------------+-----------------+
+ 3 | CONNECT_USER |
+ --+---------------------------------------------+
+
+ Password is taken from
+ 1) by environment variables (see table.2)
+ 2) by entering from tty.
+
+ Table.2 Order of environment variables for password
+
+ | SOCKS v5 | HTTP proxy |
+ --+-----------------+---------------------+
+ 1 | SOCKS5_PASSWD | |
+ --+-----------------+ HTTP_PROXY_PASSWORD |
+ 2 | SOCKS5_PASSWORD | |
+ --+-----------------+---------------------+
+ 3 | CONNECT_PASSWORD |
+ --+---------------------------------------+
+
+ Note: SOCKS5_PASSWD which is added in rev. 1.79
+ to share value with NEC SOCKS implementation.
+ */
+
+char *
+determine_relay_user ()
+{
+ char *user = NULL;
+ /* get username from environment variable, or system. */
+ if (relay_method == METHOD_SOCKS) {
+ if (user == NULL && socks_version == 5)
+ user = getparam (ENV_SOCKS5_USER);
+ if (user == NULL && socks_version == 4)
+ user = getparam (ENV_SOCKS4_USER);
+ if (user == NULL)
+ user = getparam (ENV_SOCKS_USER);
+ } else if (relay_method == METHOD_HTTP) {
+ if (user == NULL)
+ user = getparam (ENV_HTTP_PROXY_USER);
+ }
+ if (user == NULL)
+ user = getparam (ENV_CONNECT_USER);
+ /* determine relay user by system call if not yet. */
+ if (user == NULL)
+ user = getusername();
+ return user;
+}
+
+char *
+determine_relay_password ()
+{
+ char *pass = NULL;
+ if (pass == NULL && relay_method == METHOD_HTTP)
+ pass = getparam(ENV_HTTP_PROXY_PASSWORD);
+ if (pass == NULL && relay_method == METHOD_SOCKS)
+ pass = getparam(ENV_SOCKS5_PASSWD);
+ if (pass == NULL && relay_method == METHOD_SOCKS)
+ pass = getparam(ENV_SOCKS5_PASSWORD);
+ if (pass == NULL)
+ pass = getparam(ENV_CONNECT_PASSWORD);
+ return pass;
+}
+
+
/*** network operations ***/
+
/* set_relay()
Determine relay informations:
method, host, port, and username.
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");
+ 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);
- }
+ 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 */
-
+ 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;
-
+ 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 ( 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;
+ case METHOD_TELNET:
+ if ( spec == NULL )
+ spec = getparam(ENV_TELNET_PROXY);
+ if ( spec == NULL )
+ fatal("You must specify telnet proxy server\n");
+ relay_port = 23; /* set default first */
}
-
+
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';
+ /* 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 );
+ /* assume spec is aready "server:port" format */
+ buf = strdup( spec );
}
spec = buf;
-
- /* check username in spec */
+
+ /* 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");
+ *sep = '\0';
+ relay_user = strdup( spec );
+ spec = sep +1;
}
-
+ if (relay_user == NULL)
+ relay_user = determine_relay_user();
+
/* 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 );
+ /* 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 );
+ /* hostname and port */
+ relay_port = atoi(sep+1);
+ *sep = '\0';
+ relay_host = strdup( spec );
}
free(buf);
return 0;
resolve_port( const char *service )
{
int port;
- if ( service[strspn (service, "0123456789")] == '\0' ) {
- /* all digits, port number */
- port = atoi(service);
+ if ( service[strspn (service, digits)] == '\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);
- }
+ /* 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;
}
size_t len;
ptr = strstr(rcs_revstr, ": ");
if (!ptr) {
- revstr = strdup("unknown");
- return;
+ revstr = strdup("unknown");
+ return;
}
ptr += 2;
- len = strspn(ptr, "0123456789.");
+ len = strspn(ptr, dotdigits);
if (0 < len) {
- revstr = malloc(len+1);
- memcpy(revstr, ptr, len);
- revstr[len] = '\0';
+ revstr = xmalloc(len+1);
+ memcpy(revstr, ptr, len);
+ revstr[len] = '\0';
}
}
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;
+ 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 't':
+ method = METHOD_TELNET;
+ break;
+
+ case 'S': /* specify SOCKS server */
+ if ( 1 < 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 ( 1 < argc ) {
+ argv++, argc--;
+ method = METHOD_HTTP;
+ server = *argv;
+ } else {
+ error("option '-%c' needs argument.\n", *ptr);
+ err++;
+ }
+ break;
+ case 'T': /* specify telnet proxy server */
+ if ( 1 < argc ) {
+ argv++, argc--;
+ method = METHOD_TELNET;
+ server = *argv;
+ } else {
+ error("option '-%c' needs argument.\n", *ptr);
+ err++;
+ }
+ break;
+
+ case 'c':
+ if (1 < argc) {
+ argv++, argc--;
+ telnet_command = *argv;
+ } else {
+ error("option '%c' needs argument.\n", *ptr);
+ err++;
+ }
+ break;
+
+ case 'P':
+ f_hold_session = 1;
+ /* without break */
+ case 'p': /* specify port to forward */
+ if ( 1 < argc ) {
+ argv++, argc--;
+ local_type = LOCAL_SOCKET;
+ local_port = resolve_port(*argv);
+ } else {
+ error("option '-%c' needs argument.\n", *ptr);
+ err++;
+ }
+ 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;
+#ifndef _WIN32
+ case 'w':
+ if ( 1 < 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++;
+ case '4':
+ socks_version = 4;
+ break;
+
+ case '5':
+ socks_version = 5;
+ break;
+
+ case 'a':
+ if ( 1 < argc ) {
+ argv++, argc--;
+ socks5_auth = *argv;
+ } else {
+ error("option '-%c' needs argument.\n", *ptr);
+ err++;
+ }
+ break;
+
+ case 'R': /* specify resolve method */
+ if ( 1 < 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++;
+ break;
+
+ default:
+ error("unknown option '-%c'\n", *ptr);
+ err++;
+ }
+ ptr++;
+ }
+ argc--, argv++;
}
-
+
/* check error */
if ( 0 < err )
- goto quit;
-
+ 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);
+ 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++;
+ ((ptr=strchr( progname, '\\')) != NULL) )
+ ptr++;
else
- ptr = progname;
+ 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);
- }
+ /* 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;
+ 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;
+ 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);
+ 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);
+ 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("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);
+ if ( local_type == LOCAL_SOCKET ) {
+ debug("local_port=%d\n", local_port);
+ if (f_hold_session)
+ debug (" with holding remote session.\n");
+ }
debug("dest_host=%s\n", dest_host);
debug("dest_port=%d\n", dest_port);
if ( 0 < err ) {
- fprintf(stderr, usage, progname);
- exit(1);
+ fprintf(stderr, usage, progname);
+ exit(1);
}
return 0;
}
}
#endif
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+void
+switch_ns (struct sockaddr_in *ns)
+{
+ 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__ */
-/* TODO: IPv6 */
+/* TODO: IPv6
+ TODO: fallback if askpass execution failed.
+ */
int
-local_resolve (const char *host, struct sockaddr_in *addr,
- struct sockaddr_in *ns)
+local_resolve (const char *host, struct sockaddr_in *addr)
{
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);
+ if ( strspn(host, dotdigits) == 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 */
+ 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
+int
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;
+ host = dest_host;
+ port = dest_port;
+ } else if ((local_resolve (dest_host, &saddr) >= 0)&&
+ (is_direct_address(&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;
+ host = relay_host;
+ port = relay_port;
}
- if (local_resolve (host, &saddr, NULL) < 0) {
- error("can't resolve hostname: %s\n", host);
- return SOCKET_ERROR;
+ if (local_resolve (host, &saddr) < 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;
+ == SOCKET_ERROR) {
+ debug( "connect() failed.\n");
+ return SOCKET_ERROR;
}
return s;
}
char *tmp;
if ( !f_debug )
- return;
+ return;
if ( !f_report )
- return; /* don't 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);
+ memset( work, 0, sizeof(work));
+ tmp = work;
+ while ( *buf && ((tmp-work) < (int)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");
}
report_bytes( char *prefix, char *buf, int len )
{
if ( ! f_debug )
- return;
+ return;
debug( "%s", prefix );
while ( 0 < len ) {
- fprintf( stderr, " %02x", *(unsigned char *)buf);
- buf++;
- len--;
+ fprintf( stderr, " %02x", *(unsigned char *)buf);
+ buf++;
+ len--;
}
fprintf(stderr, "\n");
return;
/* 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;
+ 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");
+ debug("atomic_out() [some bytes]\n");
+ debug(">>> xx xx xx xx ...\n");
} else {
- debug("atomic_out() [%d bytes]\n", ret);
- report_bytes(">>>", buf, ret);
+ debug("atomic_out() [%d bytes]\n", ret);
+ report_bytes(">>>", buf, ret);
}
return ret;
}
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;
+ 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");
+ debug("atomic_in() [some bytes]\n");
+ debug("<<< xx xx xx xx ...\n");
} else {
- debug("atomic_in() [%d bytes]\n", ret);
- report_bytes("<<<", buf, ret);
+ debug("atomic_in() [%d bytes]\n", ret);
+ report_bytes("<<<", buf, ret);
}
return ret;
}
{
char *dst = buf;
if ( size == 0 )
- return 0; /* no error */
+ 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++;
- }
+ 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
char *ptr = str + strcspn(str, delim);
char *end = ptr + strspn(ptr, delim);
if ( ptr == str )
- return NULL;
+ return NULL;
while ( ptr < end )
- *ptr++ = '\0';
+ *ptr++ = '\0';
return ptr;
}
{
int i = 0;
while (0 <= items[i].num) {
- if (items[i].num == num)
- return items[i].str;
- i++;
+ if (items[i].num == num)
+ return items[i].str;
+ i++;
}
return "(unknown)";
}
/* readpass()
- password input routine
+ 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 */
+ static char buf[1000]; /* XXX, don't be fix length */
va_list args;
va_start(args, prompt);
vsprintf(buf, prompt, args);
if ( getparam(ENV_SSH_ASKPASS)
#if !defined(_WIN32) && !defined(__CYGWIN32__)
- && getenv("DISPLAY")
+ && 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);
+ ) {
+ /* use ssh-askpass to get password */
+ FILE *fp;
+ char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
+ cmd = xmalloc(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));
+ tty_readpass( buf, buf, sizeof(buf));
}
buf[strcspn(buf, "\r\n")] = '\0';
return buf;
socks5_do_auth_userpass( int s )
{
unsigned char buf[1024], *ptr;
- char *pass;
+ char *pass = NULL;
int len;
-
+
/* do User/Password authentication. */
- /* This feature requires username and password from
+ /* 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");
+ if (relay_user == NULL)
+ fatal("cannot determine user name.\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);
+ if ((pass=determine_relay_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++, 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 */
+ len = strlen( pass ); /* PLEN and PASSWD */
PUT_BYTE( ptr++, strlen(pass));
strcpy( ptr, pass );
ptr += len;
-
+ memset (pass, 0, strlen(pass)); /* erase password */
+
/* send it and get answer */
f_report = 0;
atomic_out( s, buf, ptr-buf );
/* check status */
if ( buf[1] == 0 )
- return 0; /* success */
+ return 0; /* success */
else
- return -1; /* fail */
+ return -1; /* fail */
}
static const char *
socks5_auth_parse_1(char *start, char *end){
int i, len;
for ( ; *start; start++ )
- if ( *start != ' ' && *start != '\t') break;
+ if ( *start != ' ' && *start != '\t') break;
for ( end--; end >= start; end-- ) {
- if ( *end != ' ' && *end != '\t'){
- end++;
- break;
- }
+ 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;
- }
+ 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;
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;
- }
+ end = strchr(start, ',');
+ if (*start && end) {
+ 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);
+ for( end = start; *end; end++ );
+ auth_list[i++] = socks5_auth_parse_1(start, end);
} else {
- fatal("Too much auth method.\n");
+ fatal("Too much auth method.\n");
}
return i;
}
int len, auth_result, i;
debug( "begin_socks_relay()\n");
-
+
/* request authentication */
ptr = buf;
- PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
+ PUT_BYTE( ptr++, 5); /* SOCKS version (5) */
if ( env == NULL )
- env = getparam(ENV_SOCKS5_AUTH);
+ 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;
+ /* 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);
+ n_auth = socks5_auth_parse(env, auth_list, 10);
}
- PUT_BYTE( ptr++, n_auth); /* num auth */
+ 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 */
+ 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;
+ 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 */
-
+ error("No acceptable authentication method\n");
+ return -1; /* fail */
+
case SOCKS5_AUTH_NOAUTH:
- /* nothing to do */
- auth_result = 0;
- break;
+ /* nothing to do */
+ auth_result = 0;
+ break;
case SOCKS5_AUTH_USERPASS:
- auth_result = socks5_do_auth_userpass(s);
- break;
-
+ auth_result = socks5_do_auth_userpass(s);
+ break;
+
default:
- error("Unsupported authentication method: %s\n",
- socks5_getauthname( auth_method ));
- return -1; /* fail */
+ error("Unsupported authentication method: %s\n",
+ socks5_getauthname( auth_method ));
+ return -1; /* fail */
}
if ( auth_result != 0 ) {
- error("Authentication failed.\n");
- return -1;
+ 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 */
+ 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;
+ /* 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);
+ /* 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>>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;
+ 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;
+ 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;
}
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).
unsigned char buf[256], *ptr;
debug( "begin_socks_relay()\n");
-
- /* make connect request packet
+
+ /* 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++, 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 */
+ *(ptr-1) = 1; /* fake, protocol 4a */
/* username */
+ if (relay_user == NULL)
+ fatal( "Cannot determine user name.\n");
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;
+ 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 */
+ 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? */
-
+ 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;
+ debug("failed to send http request. errno=%d\n", socket_errno());
+ return -1;
}
return 0;
}
/* make base64 string */
src_len = strlen(str);
dst_len = (src_len+2)/3*4;
- buf = malloc(dst_len+1);
+ buf = xmalloc(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;
+ 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 = '=';
+ *--dst = '=';
case 2:
- *--dst = '=';
+ *--dst = '=';
}
return buf;
}
int
-basic_auth( SOCKET s, const char *user, const char *pass )
+basic_auth (SOCKET s)
{
char *userpass;
char *cred;
+ const char *user = relay_user;
+ char *pass = NULL;
int len, ret;
-
+
+ /* Get username/password for authentication */
+ if (user == NULL)
+ fatal("Cannot decide username for proxy authentication.");
+ if ((pass = determine_relay_password ()) == NULL &&
+ (pass = readpass("Enter proxy authentication password for %s@%s: ",
+ relay_user, relay_host)) == NULL)
+ fatal("Cannot decide password for proxy authentication.");
+
len = strlen(user)+strlen(pass)+1;
- userpass = malloc(len+1);
+ userpass = xmalloc(len+1);
sprintf(userpass,"%s:%s", user, pass);
+ memset (pass, 0, strlen(pass));
cred = make_base64_string(userpass);
- memset( userpass, 0, len );
+ memset (userpass, 0, len);
- f_report = 0; /* don't report for security */
+ 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;
}
{
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;
+ return START_ERROR;
+ if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
+ return START_ERROR;
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); }
-
+ return START_ERROR;
+
/* get response */
if ( line_input(s, buf, sizeof(buf)) < 0 ) {
- debug("failed to read http response.\n");
- return -1;
+ debug("failed to read http response.\n");
+ return START_ERROR;
}
+
/* check status */
+ if (!strchr(buf, ' ')) {
+ error ("Unexpected http response: '%s'.\n", buf);
+ return START_ERROR;
+ }
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;
+ /* 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);
+ 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;
- }
-
+ 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 START_ERROR;
+ }
+ 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 START_ERROR; /* fail */
+ }
+ /* check supported auth type */
+ if (expect(scheme, "basic")) {
+ proxy_auth_type = PROXY_AUTH_BASIC;
+ } else {
+ debug("Unsupported authentication type: %s", scheme);
+ }
+ }
+ } while (strcmp(buf,"\r\n") != 0);
+ if ( proxy_auth_type == PROXY_AUTH_NONE ) {
+ debug("Can't find %s in response header.", auth_what);
+ return START_ERROR;
+ } else {
+ return START_RETRY;
+ }
+
default:
- /* Not allowed */
- debug("http proxy is not allowed.\n");
- return -1;
+ /* Not allowed */
+ debug("http proxy is not allowed.\n");
+ return START_ERROR;
}
/* skip to end of response header */
do {
- if ( line_input(s, buf, sizeof(buf) ) ) {
- debug("Can't skip response headers\n");
- return -1;
- }
+ if ( line_input(s, buf, sizeof(buf) ) ) {
+ debug("Can't skip response headers\n");
+ return START_ERROR;
+ }
} while ( strcmp(buf,"\r\n") != 0 );
- return 0;
+ return START_OK;
+}
+
+/* begin relaying via TELNET proxy.
+ Sends string specified by telnet_command (-c option) with
+ replacing host name and port number to the socket. */
+int
+begin_telnet_relay( SOCKET s )
+{
+ char buf[1024];
+ char *cmd;
+ char *good_phrase = "connected to";
+ char *bad_phrase_list[] = {
+ " failed", " refused", " rejected", " closed"
+ };
+ char sep = ' ';
+ int i;
+
+ debug("begin_telnet_relay()\n");
+
+ /* report phrase */
+ debug("good phrase: '%s'\n", good_phrase);
+ debug("bad phrases");
+ sep = ':';
+ for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
+ debug_("%c '%s'", sep, bad_phrase_list[i]);
+ sep = ',';
+ }
+ debug_("\n");
+
+ /* make request string with replacing %h by destination hostname
+ and %p by port number, etc. */
+ cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
+
+ /* Sorry, we send request string now without waiting a prompt. */
+ if (sendf(s, "%s\r\n", cmd) < 0) {
+ free(cmd);
+ return START_ERROR;
+ }
+ free(cmd);
+
+ /* Process answer from proxy until good or bad phrase is detected. We
+ assume that the good phrase should be appeared only in the final
+ line of proxy responses. Bad keywods in the line causes operation
+ fail. First checks a good phrase, then checks bad phrases.
+ If no match, continue reading line from proxy. */
+ while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
+ downcase(buf);
+ /* first, check good phrase */
+ if (strstr(buf, good_phrase)) {
+ debug("good phrase is detected: '%s'\n", good_phrase);
+ return START_OK;
+ }
+ /* then, check bad phrase */
+ for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) {
+ if (strstr(buf, bad_phrase_list[i]) != NULL) {
+ debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]);
+ return START_ERROR;
+ }
+ }
+ }
+ debug("error reading from telnet proxy\n");
+
+ return START_ERROR;
}
Returns 1 if data is available, otherwise return 0
*/
int
-fddatalen( SOCKET fd )
+stdindatalen (void)
{
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());
- }
- }
+ 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 */
+ /* in case of regular file (redirected) */
+ len = 1; /* always data ready */
} else if ( _kbhit() ) {
- /* in case of console */
- len = 1;
+ /* in case of console */
+ len = 1;
}
return len;
}
#endif /* _WIN32 */
-/* relay byte from stdin to socket and fro socket to stdout */
+/* relay byte from stdin to socket and fro socket to stdout.
+ returns reason of termination */
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? */
+ 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? */
+ char rbuf[1024]; /* remote input buffer */
+ int rbuf_len; /* available data in rbuf */
+ int f_remote; /* read remote input more? */
+ int close_reason = REASON_UNK; /* reason of end repeating */
/** other variables **/
int nfds, len;
- fd_set *ifds, *ofds;
+ 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 */
+ 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;
+ FD_ZERO(&ifds );
+ FD_ZERO(&ofds );
+ tmo = NULL;
- /** prepare for reading local input **/
- if ( f_local && (lbuf_len < sizeof(lbuf)) ) {
+ /** prepare for reading local input **/
+ if ( f_local && (lbuf_len < (int)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
+ 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;
- }
+ FD_SET( local_in, &ifds );
+ }
+
+ /** prepare for reading remote input **/
+ if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
+ FD_SET( remote, &ifds );
+ }
+
+ /* FD_SET( local_out, ofds ); */
+ /* FD_SET( remote, ofds ); */
+
+ if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
+ /* some error */
+ error( "select() failed, %d\n", socket_errno());
+ return REASON_ERROR;
+ }
#ifdef _WIN32
- /* fake ifds if local is stdio handle because
+ /* 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 */
+ if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
+ 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;
+ /* remote => local */
+ if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
+ len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
+ if ( len == 0 ) {
+ debug("connection closed by peer\n");
+ close_reason = REASON_CLOSED_BY_REMOTE;
+ 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 < (int)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");
+ if (!f_hold_session)
+ shutdown(remote, 1); /* no-more writing */
+ f_local = 0;
+ close_reason = REASON_CLOSED_BY_LOCAL;
+ } else if ( len == -1 ) {
+ /* error on reading from stdin */
+ if (f_hold_session) {
+ debug ("failed to read from local\n");
+ f_local = 0;
+ close_reason = REASON_CLOSED_BY_LOCAL;
+ } else
+ 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 );
+ }
+ if (f_local == 0 && f_hold_session) {
+ debug ("closing local port without disconnecting from remote\n");
+ f_remote = 0;
+ shutdown (local_out, 2);
+ close (local_out);
+ break;
+ }
+ }
+
+ return close_reason;
}
-
int
accept_connection (u_short port)
{
- int sock;
+ static int sock = -1;
int connection;
struct sockaddr_in name;
-
struct sockaddr client;
int socklen;
+ fd_set ifds;
+ int nfds;
+ int sockopt;
/* 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());
+ fatal("socket() failed, errno=%d\n", socket_errno());
+ sockopt = 1;
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
+ (void*)&sockopt, sizeof(sockopt));
/* 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());
+ fatal ("bind() failed, errno=%d\n", socket_errno());
if (listen( sock, 1) < 0)
- fatal ("listen() failed, errno=%d\n", socket_errno());
+ fatal ("listen() failed, errno=%d\n", socket_errno());
+ /* wait for new connection with watching EOF of stdin. */
+ debug ("waiting new connection at port %d (socket=%d)\n", port, sock);
+ nfds = sock + 1;
+ do {
+ int n;
+ struct timeval *ptmo = NULL;
+#ifdef _WIN32
+ struct timeval tmo;
+ tmo.tv_sec = 0;
+ tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */
+ ptmo = &tmo;
+#endif /* _WIN32 */
+ FD_ZERO (&ifds);
+ FD_SET ((SOCKET)sock, &ifds);
+#ifndef _WIN32
+ FD_SET (0, &ifds); /* watch stdin */
+#endif
+ n = select (nfds, &ifds, NULL, NULL, ptmo);
+ if (n == -1) {
+ fatal ("select() failed, %d\n", socket_errno());
+ exit (1);
+ }
+#ifdef _WIN32
+ if (0 < stdindatalen()) {
+ FD_SET (0, &ifds); /* fake */
+ n++;
+ }
+#endif
+ if (0 < n) {
+ if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
+ /* EOF */
+ debug ("Give-up waiting port because stdin is closed.");
+ exit(0);
+ }
+ if (FD_ISSET(sock, &ifds))
+ break; /* socket is stimulated */
+ }
+ } while (1);
socklen = sizeof(client);
connection = accept( sock, &client, &socklen);
if ( connection < 0 )
- fatal ("accept() failed, errno=%d\n", socket_errno());
-
- shutdown(sock, 2);
-
+ fatal ("accept() failed, errno=%d\n", socket_errno());
return connection;
}
main( int argc, char **argv )
{
int ret;
- SOCKET remote; /* socket */
- SOCKET local_in; /* Local input */
- SOCKET local_out; /* Local output */
+ int remote; /* socket */
+ int local_in; /* Local input */
+ int local_out; /* Local output */
+ int reason;
#ifdef _WIN32
WSADATA wsadata;
WSAStartup( 0x101, &wsadata);
/* Open local_in and local_out if forwarding a port */
if ( local_type == LOCAL_SOCKET ) {
- /* Relay between local port and destination */
+ /* 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;
+ /* Relay between stdin/stdout and desteination */
+ local_in = 0;
+ local_out = 1;
#ifdef _WIN32
- _setmode(local_in, O_BINARY);
- _setmode(local_out, O_BINARY);
+ _setmode(local_in, O_BINARY);
+ _setmode(local_out, O_BINARY);
#endif
}
retry:
#ifndef _WIN32
if (0 < connect_timeout)
- set_timeout (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());
+ 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());
+ 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 !defined(_WIN32) && !defined(__CYGWIN32__)
+ if (socks_ns.sin_addr.s_addr != 0)
+ switch_ns (&socks_ns);
+#endif /* not _WIN32 && not __CYGWIN32__ */
if (relay_method == METHOD_SOCKS &&
- socks_resolve == RESOLVE_LOCAL &&
- local_resolve (dest_host, &dest_addr, &socks_ns) < 0) {
- fatal("Unknown host: %s", dest_host);
+ socks_resolve == RESOLVE_LOCAL &&
+ local_resolve (dest_host, &dest_addr) < 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;
+ 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;
+ ret = begin_http_relay(remote);
+ switch (ret) {
+ case START_ERROR:
+ close (remote);
+ fatal("failed to begin relaying via HTTP.\n");
+ case START_OK:
+ break;
+ case START_RETRY:
+ /* retry with authentication */
+ close (remote);
+ goto retry;
+ }
+ break;
+ case METHOD_TELNET:
+ if (begin_telnet_relay(remote) < 0)
+ fatal("failed to begin relaying via telnet.\n");
+ break;
}
debug("connected\n");
#ifndef _WIN32
if (0 < connect_timeout)
- set_timeout (0);
+ set_timeout (0);
#endif /* not _WIN32 */
/* main loop */
debug ("start relaying.\n");
- do_repeater(local_in, local_out, remote);
+do_repeater:
+ reason = do_repeater(local_in, local_out, remote);
debug ("relaying done.\n");
+ if (local_type == LOCAL_SOCKET &&
+ reason == REASON_CLOSED_BY_LOCAL &&
+ f_hold_session) {
+ /* re-wait at local port without closing remote session */
+ debug ("re-waiting at local port %d\n", local_port);
+ local_in = local_out = accept_connection( local_port );
+ debug ("re-start relaying\n");
+ goto do_repeater;
+ }
closesocket(remote);
if ( local_type == LOCAL_SOCKET)
- closesocket(local_in);
+ closesocket(local_in);
#ifdef _WIN32
WSACleanup();
#endif /* _WIN32 */
/* ------------------------------------------------------------
Local Variables:
compile-command: "cc connect.c -o connect"
+ tab-width: 8
fill-column: 74
comment-column: 48
End: