forked from leproxy/leproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
leproxy.php
executable file
·212 lines (176 loc) · 7.91 KB
/
leproxy.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#!/usr/bin/env php
<?php
/**
* LeProxy is the HTTP/SOCKS proxy server for everybody!
*
* LeProxy should be run from the command line. Assuming this file is
* named `leproxy.php`, try running `$ php leproxy.php --help`.
*
* @link https://leproxy.org/ LeProxy project homepage
* @license https://leproxy.org/#license MIT license
* @copyright 2017 Christian Lück
* @version dev
*/
namespace LeProxy\LeProxy;
use Clue\Commander\Router;
use Clue\Commander\NoRouteFoundException;
use Clue\Commander\Tokens\Tokenizer;
use React\EventLoop\Factory;
use React\Dns\Config\HostsFile;
if (PHP_VERSION_ID < 50400 || PHP_SAPI !== 'cli') {
echo 'LeProxy HTTP/SOCKS proxy requires running ' . (PHP_SAPI !== 'cli' ? ('via command line (not ' . PHP_SAPI . ')') : ('on PHP 5.4+ (is ' . PHP_VERSION . ')')) . PHP_EOL;
exit(1);
}
// get current version from git or default to "unknown" otherwise
// this line will be replaced with the static const in the release file.
define('VERSION', ltrim(exec('git describe --always --dirty 2>/dev/null || echo unknown'), 'v'));
require __DIR__ . '/vendor/autoload.php';
// parse options from command line arguments (argv)
$tokenizer = new Tokenizer();
$tokenizer->addFilter('block', function (&$value) {
$value = ConnectorFactory::coerceBlockUri($value);
return true;
});
$tokenizer->addFilter('proxy', function (&$value) {
$value = ConnectorFactory::coerceProxyUri($value);
return true;
});
$tokenizer->addFilter('hosts', function (&$value) {
$value = HostsFile::loadFromPathBlocking($value)->getHostsForIp('0.0.0.0');
return true;
});
$commander = new Router($tokenizer);
$commander->add('--version', function () {
exit('LeProxy development version ' . VERSION . PHP_EOL);
});
$commander->add('-h | --help', function () {
exit('LeProxy HTTP/SOCKS proxy
Usage:
$ php leproxy.php [<listenAddress>] [--allow-unprotected] [--block=<destination>...] [--block-hosts=<path>...] [--proxy=<upstreamProxy>...] [--no-log]
$ php leproxy.php --version
$ php leproxy.php --help
Arguments:
<listenAddress>
The socket address to listen on.
The address consists of a full URI which may contain a username and
password, host and port.
By default, LeProxy will listen on the public address 0.0.0.0:8080.
LeProxy will report an error if it fails to listen on the given address,
you may try another address or use port `0` to pick a random free port.
If this address does not contain a username and password, LeProxy will
run in protected mode and only forward requests from the local host,
see also `--allow-unprotected`.
--allow-unprotected
If no username and password has been given, then LeProxy runs in
protected mode by default, so that it only forwards requests from the
local host and can not be abused as an open proxy.
If you have ensured only legit users can access your system, you can
pass the `--allow-unprotected` flag to forward requests from all hosts.
This option should be used with care, you have been warned.
--block=<destination>
Blocks forwarding connections to the given destination address.
Any number of destination addresses can be given.
Each destination address can be in the form `host` or `host:port` and
`host` may contain the `*` wildcard to match anything.
Subdomains for each host will automatically be blocked.
--block-hosts=<path>
Loads the hosts file from the given file path and blocks all of the
hostnames (and subdomains) that match the IP `0.0.0.0`.
Any number of hosts files can be given, all hosts will be blocked.
--proxy=<upstreamProxy>
An upstream proxy server where each connection request will be
forwarded to (proxy chaining).
Any number of upstream proxies can be given.
Each address consists of full URI which may contain a scheme, username
and password, host and port. Default scheme is `http://`, default port
is `8080` for all schemes.
--no-log
By default, LeProxy logs all connection attempts to STDOUT for
debugging purposes. This can be avoided by passing this argument.
--version
Prints the current version of LeProxy and exits.
--help, -h
Shows this help and exits.
Examples:
$ php leproxy.php
Runs LeProxy on public default address 0.0.0.0:8080 (protected mode)
$ php leproy.php 127.0.0.1:1080
Runs LeProxy on custom address 127.0.0.1:1080 (protected mode, local only)
$ php leproxy.php user:pass@0.0.0.0:8080
Runs LeProxy on public default addresses and require authentication
$ php leproxy.php --block=youtube.com --block=*:80
Runs LeProxy on default address and blocks access to youtube.com and
port 80 on all hosts (standard plaintext HTTP port).
$ php leproxy.php --proxy=http://user:pass@127.1.1.1:8080
Runs LeProxy so that all connection requests will be forwarded through
an upstream proxy server that requires authentication.
');
});
$commander->add('[--allow-unprotected] [--block=<block:block>...] [--block-hosts=<file:hosts>...] [--proxy=<proxy:proxy>...] [--no-log] [<listen>]', function ($args) {
// validate listening URI or assume default URI
$args['listen'] = ConnectorFactory::coerceListenUri(isset($args['listen']) ? $args['listen'] : '');
$args['allow-unprotected'] = isset($args['allow-unprotected']);
if ($args['allow-unprotected'] && strpos($args['listen'], '@') !== false) {
throw new \InvalidArgumentException('Unprotected mode can not be used with authentication required');
}
if (isset($args['block-hosts'])) {
if (!isset($args['block'])) {
$args['block'] = array();
}
foreach ($args['block-hosts'] as $hosts) {
$args['block'] += $hosts;
}
}
// filter duplicate block entries and subdomains
if (isset($args['block'])) {
$args['block'] = ConnectorFactory::filterRootDomains($args['block']);
}
return $args;
});
try {
$args = $commander->handleArgv();
} catch (\Exception $e) {
$message = '';
if (!$e instanceof NoRouteFoundException) {
$message = ' (' . $e->getMessage() . ')';
}
fwrite(STDERR, 'Usage Error: Invalid command arguments given, see --help' . $message . PHP_EOL);
// sysexits.h: #define EX_USAGE 64 /* command line usage error */
exit(64);
}
$loop = Factory::create();
// set next proxy server chain -> p1 -> p2 -> p3 -> destination
$connector = ConnectorFactory::createConnectorChain(isset($args['proxy']) ? $args['proxy'] : array(), $loop);
if (isset($args['block'])) {
$connector = ConnectorFactory::createBlockingConnector($args['block'], $connector);
}
// log all connection attempts to STDOUT (unless `--no-log` has been given)
if (!isset($args['no-log'])) {
$connector = new LoggingConnector($connector, new Logger());
}
// create proxy server and start listening on given address
$proxy = new LeProxyServer($loop, $connector);
try {
$socket = $proxy->listen($args['listen'], $args['allow-unprotected']);
} catch (\RuntimeException $e) {
fwrite(STDERR, 'Program error: Unable to start listening, maybe try another port? (' . $e->getMessage() . ')'. PHP_EOL);
// sysexits.h: #define EX_OSERR 71 /* system error (e.g., can't fork) */
exit(71);
}
$addr = str_replace('tcp://', 'http://', $socket->getAddress());
echo 'LeProxy HTTP/SOCKS proxy now listening on ' . $addr . ' (';
if (strpos($args['listen'], '@') !== false) {
echo 'authentication required';
} elseif ($args['allow-unprotected']) {
echo 'unprotected mode, open proxy';
} else {
echo 'protected mode, local access only';
}
echo ')' . PHP_EOL;
if (isset($args['proxy'])) {
echo 'Forwarding via: ' . implode(' -> ', $args['proxy']) . PHP_EOL;
}
if (isset($args['block'])) {
echo 'Blocking a total of ' . count($args['block']) . ' destination(s)' . PHP_EOL;
}
$loop->run();