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