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