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