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