Nov 18, 2013
Technology###Tips:
wget http://www.meadowy.org/~gotoh/ssh/connect.c
gcc -o connect connect.c
chmod 777 connect && mv connect /bin/
http-proxy-gw content:
/bin/connect -H http://10.0.0.221:9001 $@
git config --global core.gitproxy "http-proxy-gw for *.*"
echo "export GIT_PROXY_COMMAND=/bin/http-proxy-gw" >> ~/.bashrc
###C source code:
#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>
#if !defined(_WIN32) && !defined(__CYGWIN32__)
#define WITH_RESOLVER 1
#include <arpa/nameser.h>
#include <resolv.h>
#else /* not ( not _WIN32 && not __CYGWIN32__) */
#undef WITH_RESOLVER
#endif /* not ( not _WIN32 && not __CYGWIN32__) */
#endif /* !_WIN32 */
#ifdef _WIN32
#define ECONNRESET WSAECONNRESET
#endif /* _WI32 */
#ifndef LINT
static char *vcid = "$Id: connect.c 100 2007-07-03 10:48:26Z gotoh $";
#endif
#ifdef _MSC_VER
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif
#ifdef __BORLANDC__
#define _kbhit kbhit
#define _setmode setmode
#endif
static char *usage = "usage: %s [-dnhst45] [-p local-port]"
#ifdef _WIN32
#ifdef __CYGWIN32__
"[-w timeout] \n"
#else /* not __CYGWIN32__ */
" \n"
#endif /* not __CYGWIN32__ */
#else /* not _WIN32 */
"[-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";
char *progname = NULL;
char *progdesc = "connect --- simple relaying command via proxy.";
char *rcs_revstr = "$Revision: 100 $";
char *revstr = NULL;
int major_version = 1;
int minor_version = 0;
const char *digits = "0123456789";
const char *dotdigits = "0123456789.";
int f_debug = 0;
int f_report = 1;
int connect_timeout = 0;
#define LOCAL_STDIO 0
#define LOCAL_SOCKET 1
char *local_type_names[] = { "stdio", "socket" };
int local_type = LOCAL_STDIO;
u_short local_port = 0;
int f_hold_session = 0;
char *telnet_command = "telnet %h %p";
typedef struct {
int num;
const char *str;
} LOOKUP_ITEM;
#define METHOD_UNDECIDED 0
#define METHOD_DIRECT 1
#define METHOD_SOCKS 2
#define METHOD_HTTP 3
#define METHOD_TELNET 4
char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
int relay_method = METHOD_UNDECIDED;
char *relay_host = NULL;
u_short relay_port = 0;
char *relay_user = NULL;
char *dest_host = NULL;
struct sockaddr_in dest_addr;
u_short dest_port = 0;
#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 }
};
#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;
int socks_resolve = RESOLVE_UNKNOWN;
struct sockaddr_in socks_ns;
char *socks5_auth = NULL;
#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_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_USER "HTTP_PROXY_USER" /* auth user */
#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */
#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 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;
#define REASON_UNK -2
#define REASON_ERROR -1
#define REASON_CLOSED_BY_LOCAL 0
#define REASON_CLOSED_BY_REMOTE 1
#define START_ERROR -1
#define START_OK 0
#define START_RETRY 1
#ifndef _WIN32
#define SOCKET int
#endif
#ifndef SOCKET_ERROR
#define SOCKET_ERROR -1
#endif
#ifdef _WIN32
#define socket_errno() WSAGetLastError()
#else /* !_WIN32 */
#define closesocket close
#define socket_errno() (errno)
#endif /* !_WIN32 */
#ifdef _WIN32
#define popen _popen
#endif /* WIN32 */
#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
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, ... )
{
va_list args;
if ( f_debug ) {
va_start( args, fmt );
vfprintf( stderr, fmt, args );
va_end( args );
}
}
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 *
xmalloc (size_t size)
{
void *ret = malloc(size);
if (ret == NULL)
fatal("Cannot allocate memory: %d bytes.\n", size);
return ret;
}
char *
downcase( char *str )
{
char *buf = str;
while ( *buf ) {
if ( isupper(*buf) )
*buf += 'a'-'A';
buf++;
}
return str;
}
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':
snprintf (dst, len, "%d", port);
src += 2;
break;
default:
src ++;
break;
}
dst = buf + strlen (buf);
} else if (*src == '\\') {
switch (src[1]) {
case 'r':
*dst++ = '\r';
src += 2;
break;
case 'n':
*dst++ = '\n';
src += 2;
break;
case 't':
*dst++ = '\t';
src += 2;
break;
default:
src ++;
break;
}
} else {
*dst++ = *src++;
}
*dst = '\0';
}
assert (strlen(buf) < len);
return 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, 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;
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 */
}
int
expect( char *str, char *substr)
{
int len = strlen(substr);
while ( 0 < len-- ) {
if ( toupper(*str) != toupper(*substr) )
return 0;
str++, substr++;
}
return 1;
}
#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_PASSWD, 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 ( strcmp(name, parameter_table[i].name) == 0 )
return ¶meter_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, *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)
{
#if !defined(_WIN32) || defined(cygwin)
char *name;
struct passwd *pw;
#endif
read_parameter_file_1(PARAMETER_FILE);
#if !defined(_WIN32) || defined(cygwin)
pw = getpwuid(getuid());
if ( pw == NULL )
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);
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;
}
#define MAX_DIRECT_ADDR_LIST 256
struct ADDRPAIR {
struct in_addr addr;
struct in_addr mask;
char *name;
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 addr entry: %s%s/%s\n",
negative? "!": "", 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].name = NULL;
direct_addr_list[n_direct_addr_list].negative = negative;
n_direct_addr_list++;
return 0;
}
int
add_direct_host( const char *name, int negative)
{
if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
error("direct address table is full!\n");
return -1;
}
if (*name == '*')
name++;
if (*name == '.')
name++;
debug("adding direct name entry: %s%s\n", negative? "!": "", name);
direct_addr_list[n_direct_addr_list].name = downcase(strdup(name));
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)
{
const char *ptr;
u_char *dsta, *dstm;
int i, n;
assert( str != NULL );
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;
if ( !isdigit(*ptr) )
return -1;
*dsta++ = atoi( ptr );
*dstm++ = 255;
while ( isdigit(*ptr) )
ptr++;
if ( *ptr == '.' )
ptr++;
else
break;
}
if ( *ptr == '\0' )
return 0;
if ( *ptr != '/' )
return -1;
ptr++;
mask->s_addr = 0;
if ( strchr( ptr, '.') ) {
dstm = (u_char*)&mask->s_addr;
for (i=0; i<4; i++) {
if ( !isdigit(*ptr) )
return -1;
*dstm++ = atoi(ptr);
while ( isdigit(*ptr) )
ptr++;
if ( *ptr == '.' )
ptr++;
else
break;
}
} else {
if ( !isdigit(*ptr) )
return -1;
n = atoi(ptr);
if ( n<0 || 32<n)
return -1;
mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
}
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;
debug("making direct addr list from: '%s'\n", env);
env = strdup( env );
beg = next = env;
n_entries = 0;
do {
if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
error("too many entries in %s", envkey);
break;
}
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 {
add_direct_host( beg, negative );
}
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 in_addr addr)
{
int i, neg;
struct in_addr iaddr;
for (i=0; i<n_direct_addr_list; i++ ) {
if (direct_addr_list[i].name != NULL)
continue;
neg = direct_addr_list[i].negative;
iaddr = addr;
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) {
char *a, *m;
a = strdup(inet_ntoa(direct_addr_list[i].addr));
m = strdup(inet_ntoa(direct_addr_list[i].mask));
debug("match with: %s/%s%s\n", a, m, neg? " (negative)": "");
free(a);
free(m);
return !neg? 1: 0;
}
}
debug("not matched, addr to be relayed: %s\n", inet_ntoa(addr));
return 0;
}
domain_match(const char *s1, const char *s2)
{
int len1, len2;
const char *tail1, *tail2;
len1 = strlen(s1);
len2 = strlen(s2);
if (len1 < len2 || len1 == 0 || len2 == 0)
return 0;
tail1 = s1 + len1;
tail2 = s2 + len2;
while (0 < len1 && 0 < len2) {
if (*--tail1 != *--tail2)
break;
len1--, len2--;
}
if (len2 != 0)
return 0;
if (tail1 == s1 || tail1[-1] == '.')
return 1;
return 0;
}
int
is_direct_name (const char *name)
{
int len, i;
const char *tail;
debug("checking %s is for direct?\n", name);
name = downcase(strdup(name));
len = strlen(name);
if (len < 1)
return 0;
tail = &name[len];
for (i=0; i<n_direct_addr_list; i++ ) {
int dlen, neg;
const char *dname;
const char *n, *d;
dname = direct_addr_list[i].name;
if (dname == NULL)
continue;
neg = direct_addr_list[i].negative;
if (domain_match(name, dname)) {
debug("match with: %s%s\n", dname, neg? " (negative)": "");
if (neg) {
return 0;
} else {
return 1;
}
}
}
return 0;
}
int
check_direct(const char *host)
{
struct in_addr addr;
addr.s_addr = inet_addr(host);
if (addr.s_addr != INADDR_NONE) {
if (is_direct_address(addr)) {
debug("%s is for direct.\n", host);
return 1;
}
} else {
if (is_direct_name(host)) {
debug("%s is for direct.\n", host);
return 1;
}
}
debug("%s is for not direct.\n", host);
return 0;
}
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;
static sigset_t nset, oset;
static struct sigaction nsa, osa;
static int disabled = 0;
if ( disabled && enable ) {
tcsetattr(fd, TCSANOW, &otio);
disabled = 0;
sigprocmask(SIG_SETMASK, &oset, NULL);
sigaction(SIGINT, &osa, NULL);
if ( intr_flag != 0 ) {
kill(getpid(), SIGINT);
sigemptyset(&nset);
sigsuspend(&nset);
intr_flag = 0;
}
} else if (!disabled && !enable) {
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);
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;
}
if ( size <= 0 )
return -1;
write(tty, prompt, strlen(prompt));
buf[0] = '\0';
tty_change_echo(tty, 0);
ret = read(tty,buf, size-1);
tty_change_echo(tty, 1);
write(tty, "\n", 1);
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;
DWORD 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 );
GetConsoleMode(in, &mode);
SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT);
ret = ReadFile(in, buf, size, &bytes, 0);
SetConsoleMode(in, mode);
SetConsoleCtrlHandler( w32_intr_handler, FALSE );
if ( intr_flag )
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
WriteFile(out,"\n", 1, &bytes, 0);
CloseHandle(in);
CloseHandle(out);
return ret;
}
#endif /* _WIN32 */
char *
determine_relay_user ()
{
char *user = NULL;
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);
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;
}
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 ("No direct address are specified.\n");
} else {
debug ("%d direct address entries.\n", n_direct_addr_list);
}
switch ( method ) {
case METHOD_DIRECT:
return -1;
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;
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 {
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;
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;
}
if (expect( spec, HTTP_PROXY_PREFIX)) {
buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
buf[strcspn(buf, "/")] = '\0';
} else {
buf = strdup( spec );
}
spec = buf;
sep = strchr( spec, '@' );
if ( sep != NULL ) {
*sep = '\0';
relay_user = strdup( spec );
spec = sep +1;
}
if (relay_user == NULL)
relay_user = determine_relay_user();
sep = strchr(spec,':');
if ( sep == NULL ) {
relay_host = strdup( spec );
} else {
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, digits)] == '\0' ) {
port = atoi(service);
} else {
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;
minor_version = atoi(ptr);
revstr = xmalloc(20);
snprintf(revstr, 20, "%d.%d", major_version, minor_version);
}
int
getarg( int argc, char **argv )
{
int err = 0;
char *ptr, *server = (char*)NULL;
int method = METHOD_DIRECT;
progname = *argv;
argc--, argv++;
while ( (0 < argc) && (**argv == '-') ) {
ptr = *argv + 1;
while ( *ptr ) {
switch ( *ptr ) {
case 's':
method = METHOD_SOCKS;
break;
case 'n':
method = METHOD_DIRECT;
break;
case 'h':
method = METHOD_HTTP;
break;
case 't':
method = METHOD_TELNET;
break;
case 'S':
if ( 1 < argc ) {
argv++, argc--;
method = METHOD_SOCKS;
server = *argv;
} else {
error("option '-%c' needs argument.\n", *ptr);
err++;
}
break;
case 'H':
if ( 1 < argc ) {
argv++, argc--;
method = METHOD_HTTP;
server = *argv;
} else {
error("option '-%c' needs argument.\n", *ptr);
err++;
}
break;
case 'T':
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;
case 'p':
if ( 1 < 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 ( 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 ( 1 < argc ) {
argv++, argc--;
socks5_auth = *argv;
} else {
error("option '-%c' needs argument.\n", *ptr);
err++;
}
break;
case 'R':
if ( 1 < argc ) {
argv++, argc--;
socks_resolve = lookup_resolve( *argv );
} else {
error("option '-%c' needs argument.\n", *ptr);
err++;
}
break;
case 'V':
fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
exit(0);
case 'd':
f_debug++;
break;
default:
error("unknown option '-%c'\n", *ptr);
err++;
}
ptr++;
}
argc--, argv++;
}
if ( 0 < err )
goto quit;
set_relay( method, server );
if ( argc == 0 ) {
fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
fprintf(stderr, usage, progname);
exit(0);
}
dest_host = argv[0];
if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
((ptr=strchr( progname, '\\')) != NULL) )
ptr++;
else
ptr = progname;
if ( dest_port == 0 ) {
if ( 1 < argc ) {
dest_port = resolve_port(argv[1]);
} else if ( strncmp( ptr, "connect-", 8) == 0 ) {
char *str = strdup( ptr+8 );
str[strcspn( str, "." )] = '\0';
dest_port = resolve_port(str);
free(str);
}
}
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:
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);
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);
}
return 0;
}
#ifndef _WIN32
void
sig_timeout(void)
{
signal( SIGALRM, SIG_IGN );
alarm( 0 );
error( "timed out\n" );
exit(1);
}
void
set_timeout(int timeout)
{
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
#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__ */
int
local_resolve (const char *host, struct sockaddr_in *addr)
{
struct hostent *ent;
if ( strspn(host, dotdigits) == strlen(host) ) {
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(host);
} else {
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;
}
}
return 0;
}
int
open_connection( const char *host, u_short port )
{
SOCKET s;
struct sockaddr_in saddr;
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;
}
return s;
}
void
report_text( char *prefix, char *buf )
{
static char work[1024];
char *tmp;
if ( !f_debug )
return;
if ( !f_report )
return;
debug("%s \"", prefix);
while ( *buf ) {
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 {
int consumed = tmp - work;
snprintf( tmp, sizeof(work)-consumed,
"\\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 );
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 );
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;
size--;
while ( 0 < size ) {
switch ( recv( s, dst, 1, 0) ) {
case SOCKET_ERROR:
error("recv() error\n");
return -1;
case 0:
size = 0;
break;
default:
if ( *dst == '\n' ) {
size = 0;
} else {
size--;
}
dst++;
}
}
*dst = '\0';
report_text( "<<<", buf);
return 0;
}
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)";
}
char *
readpass( const char* prompt, ...)
{
static char buf[1000];
va_list args;
va_start(args, prompt);
vsnprintf(buf, sizeof(buf), prompt, args);
va_end(args);
if ( getparam(ENV_SSH_ASKPASS)
#if !defined(_WIN32) && !defined(__CYGWIN32__)
&& getenv("DISPLAY")
#endif /* not _WIN32 && not __CYGWIN32__ */
) {
FILE *fp;
char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
int cmd_size = strlen(askpass) +1 +1 +strlen(buf) +1 +1;
cmd = xmalloc(cmd_size);
snprintf(cmd, cmd_size, "%s \"%s\"", askpass, buf);
fp = popen(cmd, "r");
free(cmd);
if ( fp == NULL )
return NULL;
buf[0] = '\0';
if (fgets(buf, sizeof(buf), fp) == NULL)
return NULL;
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 = NULL;
int len;
if (relay_user == NULL)
fatal("cannot determine user name.\n");
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);
ptr = buf;
PUT_BYTE( ptr++, 1 );
len = strlen( relay_user );
PUT_BYTE( ptr++, len );
strcpy( ptr, relay_user );
ptr += len;
len = strlen( pass );
PUT_BYTE( ptr++, strlen(pass));
strcpy( ptr, pass );
ptr += len;
memset (pass, 0, strlen(pass));
f_report = 0;
atomic_out( s, buf, ptr-buf );
f_report = 1;
atomic_in( s, buf, 2 );
if ( buf[1] == 0 )
return 0;
else
return -1;
}
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 ) {
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);
} else {
fatal("Too much auth method.\n");
}
return i;
}
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");
ptr = buf;
PUT_BYTE( ptr++, 5);
if ( env == NULL )
env = getparam(ENV_SOCKS5_AUTH);
if ( env == NULL ) {
auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
} else {
n_auth = socks5_auth_parse(env, auth_list, 10);
}
PUT_BYTE( ptr++, n_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]);
}
atomic_out( s, buf, ptr-buf );
atomic_in( s, buf, 2 );
if ( (buf[0] != 5) ||
(buf[1] == 0xFF) ) {
error("No auth method accepted.\n");
return -1;
}
auth_method = buf[1];
debug("auth method: %s\n", socks5_getauthname(auth_method));
switch ( auth_method ) {
case SOCKS5_AUTH_REJECT:
error("No acceptable authentication method\n");
return -1;
case SOCKS5_AUTH_NOAUTH:
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;
}
if ( auth_result != 0 ) {
error("Authentication failed.\n");
return -1;
}
ptr = buf;
PUT_BYTE( ptr++, 5);
PUT_BYTE( ptr++, 1);
PUT_BYTE( ptr++, 0);
if ( dest_addr.sin_addr.s_addr == 0 ) {
PUT_BYTE( ptr++, 3);
len = strlen(dest_host);
PUT_BYTE( ptr++, len);
memcpy( ptr, dest_host, len );
ptr += len;
} else {
PUT_BYTE( ptr++, 1 );
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);
PUT_BYTE( ptr++, dest_port&0xFF);
atomic_out( s, buf, ptr-buf);
atomic_in( s, buf, 4 );
if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) {
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 1:
atomic_in( s, ptr, 4+2 );
break;
case 3:
atomic_in( s, ptr, 1 );
atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
break;
case 4:
atomic_in( s, ptr, 16+2 );
break;
}
return 0;
}
int
begin_socks4_relay( SOCKET s )
{
unsigned char buf[256], *ptr;
debug( "begin_socks_relay()\n");
ptr = buf;
PUT_BYTE( ptr++, 4);
PUT_BYTE( ptr++, 1);
PUT_BYTE( ptr++, dest_port>>8);
PUT_BYTE( ptr++, dest_port&0xFF);
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;
if (relay_user == NULL)
fatal( "Cannot determine user name.\n");
strcpy( ptr, relay_user );
ptr += strlen( relay_user ) +1;
if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
strcpy( ptr, dest_host );
ptr += strlen( dest_host ) +1;
}
atomic_out( s, buf, ptr-buf);
atomic_in( s, buf, 8 );
if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) {
error("Got error response: %d: '%s'.\n",
buf[1], lookup(buf[1], socks4_rep_names));
return -1;
}
return 0;
}
int
sendf(SOCKET s, const char *fmt,...)
{
static char buf[10240];
va_list args;
va_start( args, fmt );
vsnprintf( buf, sizeof(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;
src_len = strlen(str);
dst_len = (src_len+2)/3*4;
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;
}
*dst = '\0';
switch ( src_len%3 ) {
case 1:
*--dst = '=';
case 2:
*--dst = '=';
}
return buf;
}
int
basic_auth (SOCKET s)
{
char *userpass;
char *cred;
const char *user = relay_user;
char *pass = NULL;
int len, ret;
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 = xmalloc(len+1);
snprintf(userpass, len+1, "%s:%s", user, pass);
memset (pass, 0, strlen(pass));
cred = make_base64_string(userpass);
memset (userpass, 0, len);
f_report = 0;
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;
}
int
begin_http_relay( SOCKET s )
{
char buf[1024];
int result;
char *auth_what;
debug("begin_http_relay()\n");
if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
return START_ERROR;
if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
return START_ERROR;
if (sendf(s,"\r\n") < 0)
return START_ERROR;
if ( line_input(s, buf, sizeof(buf)) < 0 ) {
debug("failed to read http response.\n");
return START_ERROR;
}
if (!strchr(buf, ' ')) {
error ("Unexpected http response: '%s'.\n", buf);
return START_ERROR;
}
result = atoi(strchr(buf,' '));
switch ( result ) {
case 200:
debug("connected, start user session.\n");
break;
case 302:
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;
case 401:
case 407:
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)) {
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;
}
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:
debug("http proxy is not allowed.\n");
return START_ERROR;
}
do {
if ( line_input(s, buf, sizeof(buf) ) ) {
debug("Can't skip response headers\n");
return START_ERROR;
}
} while ( strcmp(buf,"\r\n") != 0 );
return START_OK;
}
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");
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");
cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
if (sendf(s, "%s\r\n", cmd) < 0) {
free(cmd);
return START_ERROR;
}
free(cmd);
while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
downcase(buf);
if (strstr(buf, good_phrase)) {
debug("good phrase is detected: '%s'\n", good_phrase);
return START_OK;
}
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;
}
#ifdef _WIN32
int
stdindatalen (void)
{
DWORD len = 0;
struct stat st;
fstat( 0, &st );
if ( st.st_mode & _S_IFIFO ) {
if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
NULL, 0, NULL, &len, NULL) ) {
if ( GetLastError() == ERROR_BROKEN_PIPE ) {
len = 1;
} else {
fatal("PeekNamedPipe() failed, errno=%d\n",
GetLastError());
}
}
} else if ( st.st_mode & _S_IFREG ) {
len = 1;
} else if ( _kbhit() ) {
len = 1;
}
return len;
}
#endif /* _WIN32 */
int
do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
{
char lbuf[1024];
int lbuf_len;
int f_local;
char rbuf[1024];
int rbuf_len;
int f_remote;
int close_reason = REASON_UNK;
int nfds, len;
fd_set ifds, ofds;
struct timeval *tmo;
#ifdef _WIN32
struct timeval win32_tmo;
#endif /* _WIN32 */
nfds = ((local_in<remote)? remote: local_in) +1;
f_local = 1;
f_remote = 1;
lbuf_len = 0;
rbuf_len = 0;
while ( f_local || f_remote ) {
FD_ZERO(&ifds );
FD_ZERO(&ofds );
tmo = NULL;
if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
#ifdef _WIN32
if ( local_type != LOCAL_SOCKET ) {
win32_tmo.tv_sec = 0;
win32_tmo.tv_usec = 10*1000;
tmo = &win32_tmo;
} else
#endif /* !_WIN32 */
FD_SET( local_in, &ifds );
}
if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
FD_SET( remote, &ifds );
}
if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
error( "select() failed, %d\n", socket_errno());
return REASON_ERROR;
}
#ifdef _WIN32
if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
FD_SET(0,&ifds);
#endif
if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) {
debug("connection %s by peer\n",
(len==0)? "closed": "reset");
close_reason = REASON_CLOSED_BY_REMOTE;
f_remote = 0;
f_local = 0;
} else if ( len == -1 ) {
fatal("recv() failed, %d\n", socket_errno());
} else {
debug("recv %d bytes\n", len);
if ( 1 < f_debug )
report_bytes( "<<<", rbuf+rbuf_len, len);
rbuf_len += len;
}
}
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 ) {
debug("local input is EOF\n");
if (!f_hold_session)
shutdown(remote, 1);
f_local = 0;
close_reason = REASON_CLOSED_BY_LOCAL;
} else if ( len == -1 ) {
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 {
lbuf_len += len;
}
}
if ( 0 < lbuf_len ) {
len = send(remote, lbuf, lbuf_len, 0);
if ( len == -1 ) {
fatal("send() failed, %d\n", socket_errno());
} else if ( 0 < len ) {
if ( 1 < f_debug )
report_bytes( ">>>", lbuf, len);
debug("sent %d bytes\n", len);
lbuf_len -= len;
if ( 0 < lbuf_len )
memcpy( lbuf, lbuf+len, lbuf_len );
assert( 0 <= lbuf_len );
}
}
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)
{
static int sock = -1;
int connection;
struct sockaddr_in name;
struct sockaddr client;
int socklen;
fd_set ifds;
int nfds;
int sockopt;
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());
sockopt = 1;
setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
(void*)&sockopt, sizeof(sockopt));
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());
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;
ptmo = &tmo;
#endif /* _WIN32 */
FD_ZERO (&ifds);
FD_SET ((SOCKET)sock, &ifds);
#ifndef _WIN32
FD_SET (0, &ifds);
#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);
n++;
}
#endif
if (0 < n) {
if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
debug ("Give-up waiting port because stdin is closed.");
exit(0);
}
if (FD_ISSET(sock, &ifds))
break;
}
} while (1);
socklen = sizeof(client);
connection = accept( sock, &client, &socklen);
if ( connection < 0 )
fatal ("accept() failed, errno=%d\n", socket_errno());
return connection;
}
int
main( int argc, char **argv )
{
int ret;
int remote;
int local_in;
int local_out;
int reason;
#ifdef _WIN32
WSADATA wsadata;
WSAStartup( 0x101, &wsadata);
#endif /* _WIN32 */
make_revstr();
getarg( argc, argv );
debug("Program is $Revision: 100 $\n");
if ( local_type == LOCAL_SOCKET ) {
local_in = local_out = accept_connection( local_port );
} else {
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 */
if (check_direct(dest_host))
relay_method = METHOD_DIRECT;
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());
}
#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) < 0) {
fatal("Unknown host: %s", dest_host);
}
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:
close (remote);
fatal("failed to begin relaying via HTTP.\n");
case START_OK:
break;
case START_RETRY:
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);
#endif /* not _WIN32 */
debug ("start relaying.\n");
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) {
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);
#ifdef _WIN32
WSACleanup();
#endif /* _WIN32 */
debug ("that's all, bye.\n");
return 0;
}