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