mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-20 13:03:50 +00:00
Merge pull request #3061 from shadowsocks/feature/redesigned-cli
Redesign CLI flags, help, and argument validation
This commit is contained in:
@@ -16,6 +16,10 @@ Current version: 3.3.6 | [Changelog](debian/changelog)
|
||||
[CLI reference and configuration guide](https://shadowsocks.github.io/shadowsocks-c/)
|
||||
are generated from the source with Doxygen and published after updates to `master`.
|
||||
|
||||
Use `--help` for grouped CLI options and `--version` for version information.
|
||||
See [CLI conventions](https://shadowsocks.github.io/shadowsocks-c/index.html#cli_conventions)
|
||||
for the long flags, short aliases, and configuration precedence.
|
||||
|
||||
## Community
|
||||
|
||||
See the [contribution guide](CONTRIBUTION.md) for development setup, testing,
|
||||
|
||||
@@ -14,3 +14,34 @@ source documentation.
|
||||
[Download the generated man pages](https://shadowsocks.github.io/shadowsocks-c/man-pages.tar.gz)
|
||||
from the published site. The archive includes the six CLI manuals and both
|
||||
overview lookup names.
|
||||
|
||||
\section cli_conventions CLI conventions
|
||||
|
||||
Use descriptive long flags or their existing short aliases. Connections use
|
||||
different names for the remote server and the local listener:
|
||||
|
||||
- Clients: `--server HOST --server-port PORT` selects the remote Shadowsocks server.
|
||||
- Listeners: `--listen-address ADDRESS --listen-port PORT` controls where a client
|
||||
or server accepts connections. The manager uses `--manager-address` for its
|
||||
control socket and configuration or its API for individual server ports.
|
||||
- Tunnels: `--destination HOST:PORT` selects the forwarding destination.
|
||||
- Configuration: `--config FILE`, `--cipher NAME`, and `--password SECRET` map to
|
||||
`-c`, `-m`, and `-k`. AEAD-2022 keys can also be supplied with `--key BASE64`.
|
||||
|
||||
Explicit transport and address-family flags override the JSON configuration.
|
||||
The last transport flag
|
||||
(`--tcp-only`, `--udp`, or `--udp-only`) wins, as does the last address preference
|
||||
(`--ipv4-first` or `--ipv6-first`). Address preferences do not disable either IP
|
||||
family. Other options retain their existing semantics, including repeatable
|
||||
server addresses and `ss-local --server-url` imports. Zero-valued resource
|
||||
settings such as MTU and TCP buffer sizes retain their existing behavior: they
|
||||
fall back to the configured value, or the system default when none is configured.
|
||||
|
||||
`--help` and `--version` write to standard output and exit successfully. Unknown
|
||||
options, missing values, invalid numeric CLI values, and unexpected positional
|
||||
arguments write usage errors to standard error and exit with status 2.
|
||||
|
||||
The formerly ignored `-l` flag in `ss-server` and `ss-manager` is now an error.
|
||||
Use `ss-server --listen-port` instead; manager-controlled ports come from its
|
||||
configuration or API. The removed one-time-authentication flag `-A` remains an
|
||||
error. Use an AEAD cipher.
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ ss-local(1) can be started from command line and run in foreground.
|
||||
Here is an example:
|
||||
```
|
||||
# Start ss-local with given parameters
|
||||
ss-local -s example.com -p 12345 -l 1080 -k foobar -m aes-256-cfb
|
||||
ss-local --server example.com --server-port 12345 --listen-port 1080 --password foobar --cipher aes-256-gcm
|
||||
```
|
||||
|
||||
\section ss_local_see_also SEE ALSO
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ Check out the `SEE ALSO` section for the default path of config file.
|
||||
|
||||
```
|
||||
# Start the ss-server
|
||||
ss-server -c /etc/shadowsocks-libev/config.json
|
||||
ss-server --config /etc/shadowsocks-libev/config.json
|
||||
```
|
||||
|
||||
\section ss_server_incompatibility INCOMPATIBILITY
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ through the shadowsocks tunnel. Here is an example:
|
||||
|
||||
```
|
||||
# Forward local UDP port 5353 to 8.8.8.8:53 through the ss-server
|
||||
ss-tunnel -s example.com -p 12345 -l 5353 -k foobar -m aes-256-cfb -L 8.8.8.8:53 -u
|
||||
ss-tunnel --server example.com --server-port 12345 --listen-port 5353 --password foobar --cipher aes-256-gcm --destination 8.8.8.8:53 --udp
|
||||
|
||||
# Then configure your system to use 127.0.0.1:5353 as the DNS server
|
||||
dig @127.0.0.1 -p 5353 www.google.com
|
||||
|
||||
@@ -51,7 +51,7 @@ def options(source, shell=False):
|
||||
if len(tables) != 1:
|
||||
raise ValueError("Expected one long_options table")
|
||||
table = re.sub(r'^\s*#.*$', '', tables[0], flags=re.M)
|
||||
entry = re.compile(r'\{\s*"([a-z0-9-]+)"\s*,\s*(no_argument|required_argument)\s*,\s*NULL\s*,\s*GETOPT_VAL_[A-Z0-9_]+\s*\}\s*,', re.S)
|
||||
entry = re.compile(r'\{\s*"([a-z0-9-]+)"\s*,\s*(no_argument|required_argument)\s*,\s*NULL\s*,\s*(?:GETOPT_VAL_[A-Z0-9_]+|\x27[A-Za-z0-9]\x27)\s*\}\s*,', re.S)
|
||||
for match in entry.finditer(table):
|
||||
flag, argument = match.groups()
|
||||
if '--' + flag in result:
|
||||
|
||||
+4
-1
@@ -76,7 +76,10 @@ enum {
|
||||
GETOPT_VAL_TCP_OUTGOING_SNDBUF,
|
||||
GETOPT_VAL_TCP_OUTGOING_RCVBUF,
|
||||
GETOPT_VAL_NFTABLES_SETS,
|
||||
GETOPT_VAL_SERVER_URL
|
||||
GETOPT_VAL_SERVER_URL,
|
||||
GETOPT_VAL_VERSION,
|
||||
GETOPT_VAL_TCP_ONLY,
|
||||
GETOPT_VAL_IPV4_FIRST
|
||||
};
|
||||
|
||||
#endif // _COMMON_H
|
||||
|
||||
+179
-18
@@ -1512,6 +1512,91 @@ main(int argc, char **argv)
|
||||
|
||||
memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM);
|
||||
|
||||
/* [cli_long_config]
|
||||
\par `--config <config_file>`
|
||||
Read JSON configuration. See the CLI conventions for precedence and default-value behavior. Short alias: `-c`.
|
||||
[cli_long_config] */
|
||||
|
||||
/* [cli_long_cipher]
|
||||
\par `--cipher <encrypt_method>`
|
||||
Select the encryption cipher. See `-m` for supported cipher names and key requirements. Short alias: `-m`.
|
||||
[cli_long_cipher] */
|
||||
|
||||
/* [cli_long_timeout]
|
||||
\par `--timeout <timeout>`
|
||||
Same behavior as `-t`; see that option for details. Short alias: `-t`.
|
||||
[cli_long_timeout] */
|
||||
|
||||
/* [cli_long_user]
|
||||
\par `--user <user_name>`
|
||||
Same behavior as `-a`; see that option for details. Short alias: `-a`.
|
||||
[cli_long_user] */
|
||||
|
||||
/* [cli_long_pid_file]
|
||||
\par `--pid-file <pid_file>`
|
||||
Same behavior as `-f`; see that option for details. Short alias: `-f`.
|
||||
[cli_long_pid_file] */
|
||||
|
||||
/* [cli_long_nofile]
|
||||
\par `--nofile <number>`
|
||||
Same behavior as `-n`; see that option for details. Short alias: `-n`.
|
||||
[cli_long_nofile] */
|
||||
|
||||
/* [cli_long_udp]
|
||||
\par `--udp`
|
||||
Enable both TCP and UDP relay, overriding the configured mode. Short alias: `-u`.
|
||||
[cli_long_udp] */
|
||||
|
||||
/* [cli_long_udp_only]
|
||||
\par `--udp-only`
|
||||
Enable UDP relay only, overriding the configured mode. Short alias: `-U`.
|
||||
[cli_long_udp_only] */
|
||||
|
||||
/* [cli_long_ipv6_first]
|
||||
\par `--ipv6-first`
|
||||
Prefer IPv6 DNS results; overrides configuration. This does not restrict connections to IPv6. Short alias: `-6`.
|
||||
[cli_long_ipv6_first] */
|
||||
|
||||
/* [cli_long_verbose]
|
||||
\par `--verbose`
|
||||
Same behavior as `-v`; see that option for details. Short alias: `-v`.
|
||||
[cli_long_verbose] */
|
||||
|
||||
/* [cli_long_server]
|
||||
\par `--server <server_host>`
|
||||
Set a remote Shadowsocks server hostname or IP address; may be repeated. Short alias: `-s`.
|
||||
[cli_long_server] */
|
||||
|
||||
/* [cli_long_server_port]
|
||||
\par `--server-port <server_port>`
|
||||
Set the remote Shadowsocks server port. Short alias: `-p`.
|
||||
[cli_long_server_port] */
|
||||
|
||||
/* [cli_long_listen_address]
|
||||
\par `--listen-address <local_address>`
|
||||
Set the listening address. Server and manager addresses may be repeated. Short alias: `-b`.
|
||||
[cli_long_listen_address] */
|
||||
|
||||
/* [cli_long_listen_port]
|
||||
\par `--listen-port <local_port>`
|
||||
Set the listening port. Short alias: `-l`.
|
||||
[cli_long_listen_port] */
|
||||
|
||||
/* [cli_long_interface]
|
||||
\par `--interface <interface>`
|
||||
Same behavior as `-i`; see that option for details. Short alias: `-i`.
|
||||
[cli_long_interface] */
|
||||
|
||||
/* [cli_long_vpn]
|
||||
\par `--vpn`
|
||||
Same behavior as `-V`; see that option for details. Short alias: `-V`.
|
||||
[cli_long_vpn] */
|
||||
|
||||
/* [cli_long_stat_path]
|
||||
\par `--stat-path <path>`
|
||||
Same behavior as `-S`; see that option for details. Short alias: `-S`.
|
||||
[cli_long_stat_path] */
|
||||
|
||||
/* [cli-options]
|
||||
\snippet{doc} utils.c cli_short_f
|
||||
\snippet{doc} utils.c cli_short_s
|
||||
@@ -1533,6 +1618,26 @@ main(int argc, char **argv)
|
||||
\snippet{doc} utils.c cli_short_V
|
||||
\snippet{doc} utils.c cli_short_6
|
||||
\snippet{doc} utils.c cli_short_A
|
||||
\snippet{doc} local.c cli_long_config
|
||||
\snippet{doc} local.c cli_long_cipher
|
||||
\snippet{doc} local.c cli_long_timeout
|
||||
\snippet{doc} local.c cli_long_user
|
||||
\snippet{doc} local.c cli_long_pid_file
|
||||
\snippet{doc} local.c cli_long_nofile
|
||||
\snippet{doc} local.c cli_long_udp
|
||||
\snippet{doc} local.c cli_long_udp_only
|
||||
\snippet{doc} local.c cli_long_ipv6_first
|
||||
\snippet{doc} local.c cli_long_verbose
|
||||
\snippet{doc} local.c cli_long_server
|
||||
\snippet{doc} local.c cli_long_server_port
|
||||
\snippet{doc} local.c cli_long_listen_address
|
||||
\snippet{doc} local.c cli_long_listen_port
|
||||
\snippet{doc} local.c cli_long_interface
|
||||
\snippet{doc} utils.c cli_long_version
|
||||
\snippet{doc} utils.c cli_long_tcp_only
|
||||
\snippet{doc} utils.c cli_long_ipv4_first
|
||||
\snippet{doc} local.c cli_long_vpn
|
||||
\snippet{doc} local.c cli_long_stat_path
|
||||
\snippet{doc} utils.c cli_long_reuse_port
|
||||
\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf
|
||||
\snippet{doc} utils.c cli_long_tcp_incoming_rcvbuf
|
||||
@@ -1551,6 +1656,28 @@ main(int argc, char **argv)
|
||||
\snippet{doc} utils.c cli_long_help
|
||||
[cli-options] */
|
||||
static struct option long_options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "cipher", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "user", required_argument, NULL, 'a' },
|
||||
{ "pid-file", required_argument, NULL, 'f' },
|
||||
{ "nofile", required_argument, NULL, 'n' },
|
||||
{ "udp", no_argument, NULL, 'u' },
|
||||
{ "udp-only", no_argument, NULL, 'U' },
|
||||
{ "ipv6-first", no_argument, NULL, '6' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "server", required_argument, NULL, 's' },
|
||||
{ "server-port", required_argument, NULL, 'p' },
|
||||
{ "listen-address", required_argument, NULL, 'b' },
|
||||
{ "listen-port", required_argument, NULL, 'l' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "version", no_argument, NULL, GETOPT_VAL_VERSION },
|
||||
{ "tcp-only", no_argument, NULL, GETOPT_VAL_TCP_ONLY },
|
||||
{ "ipv4-first", no_argument, NULL, GETOPT_VAL_IPV4_FIRST },
|
||||
#ifdef __ANDROID__
|
||||
{ "vpn", no_argument, NULL, 'V' },
|
||||
{ "stat-path", required_argument, NULL, 'S' },
|
||||
#endif
|
||||
{ "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT },
|
||||
{ "tcp-incoming-sndbuf", required_argument, NULL, GETOPT_VAL_TCP_INCOMING_SNDBUF },
|
||||
{ "tcp-incoming-rcvbuf", required_argument, NULL, GETOPT_VAL_TCP_INCOMING_RCVBUF },
|
||||
@@ -1570,18 +1697,31 @@ main(int argc, char **argv)
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int mode_set = 0;
|
||||
int ipv6first_set = 0;
|
||||
opterr = 0;
|
||||
|
||||
USE_TTY();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:S:huUvV6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:l:k:t:m:i:c:b:a:n:S:huUvV6A",
|
||||
long_options, NULL)) != -1) {
|
||||
#else
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:n:huUv6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:l:k:t:m:i:c:b:a:n:huUv6A",
|
||||
long_options, NULL)) != -1) {
|
||||
#endif
|
||||
switch (c) {
|
||||
case GETOPT_VAL_VERSION:
|
||||
cli_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case GETOPT_VAL_TCP_ONLY:
|
||||
mode = TCP_ONLY;
|
||||
mode_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_IPV4_FIRST:
|
||||
ipv6first = 0;
|
||||
ipv6first_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_FAST_OPEN:
|
||||
fast_open = 1;
|
||||
break;
|
||||
@@ -1591,7 +1731,7 @@ main(int argc, char **argv)
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &mtu) == -1) {
|
||||
FATAL("invalid MTU");
|
||||
cli_error("invalid MTU", c, NULL);
|
||||
}
|
||||
LOGI("set MTU to %d", mtu);
|
||||
break;
|
||||
@@ -1620,7 +1760,7 @@ main(int argc, char **argv)
|
||||
* since they are applied as getopt reaches them.
|
||||
*/
|
||||
if (ss_url_parse(optarg, &server_url) != 0) {
|
||||
FATAL("invalid ss:// server URL");
|
||||
cli_error("invalid ss:// server URL", c, NULL);
|
||||
}
|
||||
if (remote_num < MAX_REMOTE_NUM) {
|
||||
remote_addr[remote_num].host = server_url.host;
|
||||
@@ -1642,22 +1782,22 @@ main(int argc, char **argv)
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_sndbuf) == -1) {
|
||||
FATAL("invalid TCP incoming send buffer size");
|
||||
cli_error("invalid TCP incoming send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP incoming receive buffer size");
|
||||
cli_error("invalid TCP incoming receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_sndbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing send buffer size");
|
||||
cli_error("invalid TCP outgoing send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing receive buffer size");
|
||||
cli_error("invalid TCP outgoing receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
@@ -1666,9 +1806,19 @@ main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
remote_port = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
local_port = optarg;
|
||||
break;
|
||||
case GETOPT_VAL_PASSWORD:
|
||||
@@ -1680,6 +1830,11 @@ main(int argc, char **argv)
|
||||
pid_path = optarg;
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
int checked_timeout;
|
||||
if (ss_parse_int(optarg, 1, INT_MAX, &checked_timeout) != 0)
|
||||
cli_error("timeout must be a positive integer", c, NULL);
|
||||
}
|
||||
timeout = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -1700,14 +1855,16 @@ main(int argc, char **argv)
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
case 'n':
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &nofile) == -1) {
|
||||
FATAL("invalid nofile");
|
||||
cli_error("invalid nofile", c, NULL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'u':
|
||||
mode_set = 1;
|
||||
mode = TCP_AND_UDP;
|
||||
break;
|
||||
case 'U':
|
||||
mode_set = 1;
|
||||
mode = UDP_ONLY;
|
||||
break;
|
||||
case 'v':
|
||||
@@ -1718,6 +1875,7 @@ main(int argc, char **argv)
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '6':
|
||||
ipv6first_set = 1;
|
||||
ipv6first = 1;
|
||||
break;
|
||||
#ifdef __ANDROID__
|
||||
@@ -1729,19 +1887,22 @@ main(int argc, char **argv)
|
||||
break;
|
||||
#endif
|
||||
case 'A':
|
||||
FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
|
||||
cli_error("one-time authentication was removed; use an AEAD cipher", 'A', NULL);
|
||||
break;
|
||||
case ':':
|
||||
cli_error("missing required argument for option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
// The option character is not recognized.
|
||||
LOGE("Unrecognized option: %s", optarg);
|
||||
opterr = 1;
|
||||
cli_error("unrecognized or invalid option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
default:
|
||||
cli_error("option is unsupported on this platform", c, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opterr) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
if (optind < argc) {
|
||||
cli_error("unexpected positional argument", 0, NULL);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
@@ -1804,7 +1965,7 @@ main(int argc, char **argv)
|
||||
if (fast_open == 0) {
|
||||
fast_open = conf->fast_open;
|
||||
}
|
||||
if (mode == TCP_ONLY) {
|
||||
if (!mode_set) {
|
||||
mode = conf->mode;
|
||||
}
|
||||
if (mtu == 0) {
|
||||
@@ -1821,7 +1982,7 @@ main(int argc, char **argv)
|
||||
nofile = conf->nofile;
|
||||
}
|
||||
#endif
|
||||
if (ipv6first == 0) {
|
||||
if (!ipv6first_set) {
|
||||
ipv6first = conf->ipv6_first;
|
||||
}
|
||||
if (acl == 0 && conf->acl != NULL) {
|
||||
|
||||
+133
-18
@@ -1276,20 +1276,79 @@ main(int argc, char **argv)
|
||||
Set a server listening hostname or IP address. May be repeated.
|
||||
[cli_short_s] */
|
||||
|
||||
/* [cli_short_l]
|
||||
\par `-l <local_port>`
|
||||
Accepted for compatibility but ignored by this program; it does not configure a local listener.
|
||||
[cli_short_l] */
|
||||
|
||||
/* [cli_short_c]
|
||||
\par `-c <config_file>`
|
||||
Use a JSON configuration file. The "port_password" field can start multiple ss-server instances.
|
||||
[cli_short_c] */
|
||||
|
||||
/* [cli_long_config]
|
||||
\par `--config <config_file>`
|
||||
Read JSON configuration. See the CLI conventions for precedence and default-value behavior. Short alias: `-c`.
|
||||
[cli_long_config] */
|
||||
|
||||
/* [cli_long_cipher]
|
||||
\par `--cipher <encrypt_method>`
|
||||
Select the encryption cipher. See `-m` for supported cipher names and key requirements. Short alias: `-m`.
|
||||
[cli_long_cipher] */
|
||||
|
||||
/* [cli_long_timeout]
|
||||
\par `--timeout <timeout>`
|
||||
Same behavior as `-t`; see that option for details. Short alias: `-t`.
|
||||
[cli_long_timeout] */
|
||||
|
||||
/* [cli_long_user]
|
||||
\par `--user <user_name>`
|
||||
Same behavior as `-a`; see that option for details. Short alias: `-a`.
|
||||
[cli_long_user] */
|
||||
|
||||
/* [cli_long_pid_file]
|
||||
\par `--pid-file <pid_file>`
|
||||
Same behavior as `-f`; see that option for details. Short alias: `-f`.
|
||||
[cli_long_pid_file] */
|
||||
|
||||
/* [cli_long_nofile]
|
||||
\par `--nofile <number>`
|
||||
Same behavior as `-n`; see that option for details. Short alias: `-n`.
|
||||
[cli_long_nofile] */
|
||||
|
||||
/* [cli_long_udp]
|
||||
\par `--udp`
|
||||
Enable both TCP and UDP relay, overriding the configured mode. Short alias: `-u`.
|
||||
[cli_long_udp] */
|
||||
|
||||
/* [cli_long_udp_only]
|
||||
\par `--udp-only`
|
||||
Enable UDP relay only, overriding the configured mode. Short alias: `-U`.
|
||||
[cli_long_udp_only] */
|
||||
|
||||
/* [cli_long_ipv6_first]
|
||||
\par `--ipv6-first`
|
||||
Prefer IPv6 DNS results; overrides configuration. This does not restrict connections to IPv6. Short alias: `-6`.
|
||||
[cli_long_ipv6_first] */
|
||||
|
||||
/* [cli_long_verbose]
|
||||
\par `--verbose`
|
||||
Same behavior as `-v`; see that option for details. Short alias: `-v`.
|
||||
[cli_long_verbose] */
|
||||
|
||||
/* [cli_long_listen_address]
|
||||
\par `--listen-address <server_host>`
|
||||
Set the listening address. Server and manager addresses may be repeated. Short alias: `-s`.
|
||||
[cli_long_listen_address] */
|
||||
|
||||
/* [cli_long_interface]
|
||||
\par `--interface <interface>`
|
||||
Same behavior as `-i`; see that option for details. Short alias: `-i`.
|
||||
[cli_long_interface] */
|
||||
|
||||
/* [cli_long_nameserver]
|
||||
\par `--nameserver <addr>`
|
||||
Same behavior as `-d`; see that option for details. Short alias: `-d`.
|
||||
[cli_long_nameserver] */
|
||||
|
||||
/* [cli-options]
|
||||
\snippet{doc} utils.c cli_short_f
|
||||
\snippet{doc} manager.c cli_short_s
|
||||
\snippet{doc} manager.c cli_short_l
|
||||
\snippet{doc} utils.c cli_short_k
|
||||
\snippet{doc} utils.c cli_short_t
|
||||
\snippet{doc} utils.c cli_short_m
|
||||
@@ -1305,6 +1364,22 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
\snippet{doc} utils.c cli_short_U
|
||||
\snippet{doc} utils.c cli_short_v
|
||||
\snippet{doc} utils.c cli_short_A
|
||||
\snippet{doc} manager.c cli_long_config
|
||||
\snippet{doc} manager.c cli_long_cipher
|
||||
\snippet{doc} manager.c cli_long_timeout
|
||||
\snippet{doc} manager.c cli_long_user
|
||||
\snippet{doc} manager.c cli_long_pid_file
|
||||
\snippet{doc} manager.c cli_long_nofile
|
||||
\snippet{doc} manager.c cli_long_udp
|
||||
\snippet{doc} manager.c cli_long_udp_only
|
||||
\snippet{doc} manager.c cli_long_ipv6_first
|
||||
\snippet{doc} manager.c cli_long_verbose
|
||||
\snippet{doc} manager.c cli_long_listen_address
|
||||
\snippet{doc} manager.c cli_long_interface
|
||||
\snippet{doc} manager.c cli_long_nameserver
|
||||
\snippet{doc} utils.c cli_long_version
|
||||
\snippet{doc} utils.c cli_long_tcp_only
|
||||
\snippet{doc} utils.c cli_long_ipv4_first
|
||||
\snippet{doc} utils.c cli_long_fast_open
|
||||
\snippet{doc} utils.c cli_long_no_delay
|
||||
\snippet{doc} utils.c cli_long_reuse_port
|
||||
@@ -1319,6 +1394,22 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
\snippet{doc} utils.c cli_long_help
|
||||
[cli-options] */
|
||||
static struct option long_options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "cipher", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "user", required_argument, NULL, 'a' },
|
||||
{ "pid-file", required_argument, NULL, 'f' },
|
||||
{ "nofile", required_argument, NULL, 'n' },
|
||||
{ "udp", no_argument, NULL, 'u' },
|
||||
{ "udp-only", no_argument, NULL, 'U' },
|
||||
{ "ipv6-first", no_argument, NULL, '6' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "listen-address", required_argument, NULL, 's' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "nameserver", required_argument, NULL, 'd' },
|
||||
{ "version", no_argument, NULL, GETOPT_VAL_VERSION },
|
||||
{ "tcp-only", no_argument, NULL, GETOPT_VAL_TCP_ONLY },
|
||||
{ "ipv4-first", no_argument, NULL, GETOPT_VAL_IPV4_FIRST },
|
||||
{ "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN },
|
||||
{ "no-delay", no_argument, NULL, GETOPT_VAL_NODELAY },
|
||||
{ "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT },
|
||||
@@ -1336,13 +1427,26 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int mode_set = 0;
|
||||
int ipv6first_set = 0;
|
||||
opterr = 0;
|
||||
|
||||
USE_TTY();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "f:s:l:k:t:m:c:i:d:a:n:D:6huUvA",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:k:t:m:c:i:d:a:n:D:6huUvA",
|
||||
long_options, NULL)) != -1)
|
||||
switch (c) {
|
||||
case GETOPT_VAL_VERSION:
|
||||
cli_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case GETOPT_VAL_TCP_ONLY:
|
||||
mode = TCP_ONLY;
|
||||
mode_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_IPV4_FIRST:
|
||||
ipv6first = 0;
|
||||
ipv6first_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_REUSE_PORT:
|
||||
reuse_port = 1;
|
||||
break;
|
||||
@@ -1363,7 +1467,7 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &mtu) == -1) {
|
||||
FATAL("invalid MTU");
|
||||
cli_error("invalid MTU", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_PLUGIN:
|
||||
@@ -1386,6 +1490,11 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
pid_path = optarg;
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
int checked_timeout;
|
||||
if (ss_parse_int(optarg, 1, INT_MAX, &checked_timeout) != 0)
|
||||
cli_error("timeout must be a positive integer", c, NULL);
|
||||
}
|
||||
timeout = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -1404,12 +1513,15 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
user = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
mode_set = 1;
|
||||
mode = TCP_AND_UDP;
|
||||
break;
|
||||
case 'U':
|
||||
mode_set = 1;
|
||||
mode = UDP_ONLY;
|
||||
break;
|
||||
case '6':
|
||||
ipv6first_set = 1;
|
||||
ipv6first = 1;
|
||||
break;
|
||||
case GETOPT_VAL_WORKDIR:
|
||||
@@ -1426,23 +1538,26 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
case 'n':
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &nofile) == -1) {
|
||||
FATAL("invalid nofile");
|
||||
cli_error("invalid nofile", c, NULL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'A':
|
||||
FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
|
||||
cli_error("one-time authentication was removed; use an AEAD cipher", 'A', NULL);
|
||||
break;
|
||||
case ':':
|
||||
cli_error("missing required argument for option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
// The option character is not recognized.
|
||||
LOGE("Unrecognized option: %s", optarg);
|
||||
opterr = 1;
|
||||
cli_error("unrecognized or invalid option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
default:
|
||||
cli_error("option is unsupported on this platform", c, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (opterr) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
if (optind < argc) {
|
||||
cli_error("unexpected positional argument", 0, NULL);
|
||||
}
|
||||
|
||||
if (conf_path != NULL) {
|
||||
@@ -1476,7 +1591,7 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
if (nameservers == NULL) {
|
||||
nameservers = conf->nameserver;
|
||||
}
|
||||
if (mode == TCP_ONLY) {
|
||||
if (!mode_set) {
|
||||
mode = conf->mode;
|
||||
}
|
||||
if (mtu == 0) {
|
||||
@@ -1488,7 +1603,7 @@ Use a JSON configuration file. The "port_password" field can start multiple ss-s
|
||||
if (plugin_opts == NULL) {
|
||||
plugin_opts = conf->plugin_opts;
|
||||
}
|
||||
if (ipv6first == 0) {
|
||||
if (!ipv6first_set) {
|
||||
ipv6first = conf->ipv6_first;
|
||||
}
|
||||
if (workdir == NULL) {
|
||||
|
||||
+161
-16
@@ -927,6 +927,81 @@ main(int argc, char **argv)
|
||||
Enable UDP relay. Requires Linux TPROXY support and permission to configure transparent proxying.
|
||||
[cli_short_u] */
|
||||
|
||||
/* [cli_long_config]
|
||||
\par `--config <config_file>`
|
||||
Read JSON configuration. See the CLI conventions for precedence and default-value behavior. Short alias: `-c`.
|
||||
[cli_long_config] */
|
||||
|
||||
/* [cli_long_cipher]
|
||||
\par `--cipher <encrypt_method>`
|
||||
Select the encryption cipher. See `-m` for supported cipher names and key requirements. Short alias: `-m`.
|
||||
[cli_long_cipher] */
|
||||
|
||||
/* [cli_long_timeout]
|
||||
\par `--timeout <timeout>`
|
||||
Same behavior as `-t`; see that option for details. Short alias: `-t`.
|
||||
[cli_long_timeout] */
|
||||
|
||||
/* [cli_long_user]
|
||||
\par `--user <user_name>`
|
||||
Same behavior as `-a`; see that option for details. Short alias: `-a`.
|
||||
[cli_long_user] */
|
||||
|
||||
/* [cli_long_pid_file]
|
||||
\par `--pid-file <pid_file>`
|
||||
Same behavior as `-f`; see that option for details. Short alias: `-f`.
|
||||
[cli_long_pid_file] */
|
||||
|
||||
/* [cli_long_nofile]
|
||||
\par `--nofile <number>`
|
||||
Same behavior as `-n`; see that option for details. Short alias: `-n`.
|
||||
[cli_long_nofile] */
|
||||
|
||||
/* [cli_long_udp]
|
||||
\par `--udp`
|
||||
Enable both TCP and UDP relay, overriding the configured mode. Short alias: `-u`.
|
||||
[cli_long_udp] */
|
||||
|
||||
/* [cli_long_udp_only]
|
||||
\par `--udp-only`
|
||||
Enable UDP relay only, overriding the configured mode. Short alias: `-U`.
|
||||
[cli_long_udp_only] */
|
||||
|
||||
/* [cli_long_ipv6_first]
|
||||
\par `--ipv6-first`
|
||||
Prefer IPv6 DNS results; overrides configuration. This does not restrict connections to IPv6. Short alias: `-6`.
|
||||
[cli_long_ipv6_first] */
|
||||
|
||||
/* [cli_long_verbose]
|
||||
\par `--verbose`
|
||||
Same behavior as `-v`; see that option for details. Short alias: `-v`.
|
||||
[cli_long_verbose] */
|
||||
|
||||
/* [cli_long_server]
|
||||
\par `--server <server_host>`
|
||||
Set a remote Shadowsocks server hostname or IP address; may be repeated. Short alias: `-s`.
|
||||
[cli_long_server] */
|
||||
|
||||
/* [cli_long_server_port]
|
||||
\par `--server-port <server_port>`
|
||||
Set the remote Shadowsocks server port. Short alias: `-p`.
|
||||
[cli_long_server_port] */
|
||||
|
||||
/* [cli_long_listen_address]
|
||||
\par `--listen-address <local_address>`
|
||||
Set the listening address. Server and manager addresses may be repeated. Short alias: `-b`.
|
||||
[cli_long_listen_address] */
|
||||
|
||||
/* [cli_long_listen_port]
|
||||
\par `--listen-port <local_port>`
|
||||
Set the listening port. Short alias: `-l`.
|
||||
[cli_long_listen_port] */
|
||||
|
||||
/* [cli_long_tproxy]
|
||||
\par `--tproxy`
|
||||
Same behavior as `-T`; see that option for details. Short alias: `-T`.
|
||||
[cli_long_tproxy] */
|
||||
|
||||
/* [cli-options]
|
||||
\snippet{doc} utils.c cli_short_f
|
||||
\snippet{doc} utils.c cli_short_s
|
||||
@@ -946,6 +1021,24 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
\snippet{doc} utils.c cli_short_v
|
||||
\snippet{doc} utils.c cli_short_6
|
||||
\snippet{doc} utils.c cli_short_A
|
||||
\snippet{doc} redir.c cli_long_config
|
||||
\snippet{doc} redir.c cli_long_cipher
|
||||
\snippet{doc} redir.c cli_long_timeout
|
||||
\snippet{doc} redir.c cli_long_user
|
||||
\snippet{doc} redir.c cli_long_pid_file
|
||||
\snippet{doc} redir.c cli_long_nofile
|
||||
\snippet{doc} redir.c cli_long_udp
|
||||
\snippet{doc} redir.c cli_long_udp_only
|
||||
\snippet{doc} redir.c cli_long_ipv6_first
|
||||
\snippet{doc} redir.c cli_long_verbose
|
||||
\snippet{doc} redir.c cli_long_server
|
||||
\snippet{doc} redir.c cli_long_server_port
|
||||
\snippet{doc} redir.c cli_long_listen_address
|
||||
\snippet{doc} redir.c cli_long_listen_port
|
||||
\snippet{doc} redir.c cli_long_tproxy
|
||||
\snippet{doc} utils.c cli_long_version
|
||||
\snippet{doc} utils.c cli_long_tcp_only
|
||||
\snippet{doc} utils.c cli_long_ipv4_first
|
||||
\snippet{doc} utils.c cli_long_fast_open
|
||||
\snippet{doc} utils.c cli_long_mtu
|
||||
\snippet{doc} utils.c cli_long_mptcp
|
||||
@@ -962,6 +1055,24 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
\snippet{doc} utils.c cli_long_help
|
||||
[cli-options] */
|
||||
static struct option long_options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "cipher", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "user", required_argument, NULL, 'a' },
|
||||
{ "pid-file", required_argument, NULL, 'f' },
|
||||
{ "nofile", required_argument, NULL, 'n' },
|
||||
{ "udp", no_argument, NULL, 'u' },
|
||||
{ "udp-only", no_argument, NULL, 'U' },
|
||||
{ "ipv6-first", no_argument, NULL, '6' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "server", required_argument, NULL, 's' },
|
||||
{ "server-port", required_argument, NULL, 'p' },
|
||||
{ "listen-address", required_argument, NULL, 'b' },
|
||||
{ "listen-port", required_argument, NULL, 'l' },
|
||||
{ "tproxy", no_argument, NULL, 'T' },
|
||||
{ "version", no_argument, NULL, GETOPT_VAL_VERSION },
|
||||
{ "tcp-only", no_argument, NULL, GETOPT_VAL_TCP_ONLY },
|
||||
{ "ipv4-first", no_argument, NULL, GETOPT_VAL_IPV4_FIRST },
|
||||
{ "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN },
|
||||
{ "mtu", required_argument, NULL, GETOPT_VAL_MTU },
|
||||
{ "mptcp", no_argument, NULL, GETOPT_VAL_MPTCP },
|
||||
@@ -979,19 +1090,32 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int mode_set = 0;
|
||||
int ipv6first_set = 0;
|
||||
opterr = 0;
|
||||
|
||||
USE_TTY();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUTv6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:l:k:t:m:c:b:a:n:huUTv6A",
|
||||
long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case GETOPT_VAL_VERSION:
|
||||
cli_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case GETOPT_VAL_TCP_ONLY:
|
||||
mode = TCP_ONLY;
|
||||
mode_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_IPV4_FIRST:
|
||||
ipv6first = 0;
|
||||
ipv6first_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_FAST_OPEN:
|
||||
fast_open = 1;
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &mtu) == -1) {
|
||||
FATAL("invalid MTU");
|
||||
cli_error("invalid MTU", c, NULL);
|
||||
}
|
||||
LOGI("set MTU to %d", mtu);
|
||||
break;
|
||||
@@ -1018,22 +1142,22 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_sndbuf) == -1) {
|
||||
FATAL("invalid TCP incoming send buffer size");
|
||||
cli_error("invalid TCP incoming send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP incoming receive buffer size");
|
||||
cli_error("invalid TCP incoming receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_sndbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing send buffer size");
|
||||
cli_error("invalid TCP outgoing send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing receive buffer size");
|
||||
cli_error("invalid TCP outgoing receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
@@ -1042,9 +1166,19 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
remote_port = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
local_port = optarg;
|
||||
break;
|
||||
case GETOPT_VAL_PASSWORD:
|
||||
@@ -1056,6 +1190,11 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
pid_path = optarg;
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
int checked_timeout;
|
||||
if (ss_parse_int(optarg, 1, INT_MAX, &checked_timeout) != 0)
|
||||
cli_error("timeout must be a positive integer", c, NULL);
|
||||
}
|
||||
timeout = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -1073,14 +1212,16 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
case 'n':
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &nofile) == -1) {
|
||||
FATAL("invalid nofile");
|
||||
cli_error("invalid nofile", c, NULL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'u':
|
||||
mode_set = 1;
|
||||
mode = TCP_AND_UDP;
|
||||
break;
|
||||
case 'U':
|
||||
mode_set = 1;
|
||||
mode = UDP_ONLY;
|
||||
break;
|
||||
case 'T':
|
||||
@@ -1094,22 +1235,26 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '6':
|
||||
ipv6first_set = 1;
|
||||
ipv6first = 1;
|
||||
break;
|
||||
case 'A':
|
||||
FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
|
||||
cli_error("one-time authentication was removed; use an AEAD cipher", 'A', NULL);
|
||||
break;
|
||||
case ':':
|
||||
cli_error("missing required argument for option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
// The option character is not recognized.
|
||||
LOGE("Unrecognized option: %s", optarg);
|
||||
opterr = 1;
|
||||
cli_error("unrecognized or invalid option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
default:
|
||||
cli_error("option is unsupported on this platform", c, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opterr) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
if (optind < argc) {
|
||||
cli_error("unexpected positional argument", 0, NULL);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
@@ -1155,7 +1300,7 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
if (plugin_opts == NULL) {
|
||||
plugin_opts = conf->plugin_opts;
|
||||
}
|
||||
if (mode == TCP_ONLY) {
|
||||
if (!mode_set) {
|
||||
mode = conf->mode;
|
||||
}
|
||||
if (tcp_tproxy == 0) {
|
||||
@@ -1193,7 +1338,7 @@ Enable UDP relay. Requires Linux TPROXY support and permission to configure tran
|
||||
nofile = conf->nofile;
|
||||
}
|
||||
#endif
|
||||
if (ipv6first == 0) {
|
||||
if (!ipv6first_set) {
|
||||
ipv6first = conf->ipv6_first;
|
||||
}
|
||||
dscp_num = conf->dscp_num;
|
||||
|
||||
+156
-22
@@ -1848,21 +1848,90 @@ Set a server listening hostname or IP address. May be repeated.
|
||||
Set the server listening port.
|
||||
[cli_short_p] */
|
||||
|
||||
/* [cli_short_l]
|
||||
\par `-l <local_port>`
|
||||
Accepted for compatibility but ignored by this program; it does not configure a local listener.
|
||||
[cli_short_l] */
|
||||
|
||||
/* [cli_short_b]
|
||||
\par `-b <local_address>`
|
||||
Set the local address for outbound connections to destination servers.
|
||||
[cli_short_b] */
|
||||
|
||||
/* [cli_long_config]
|
||||
\par `--config <config_file>`
|
||||
Read JSON configuration. See the CLI conventions for precedence and default-value behavior. Short alias: `-c`.
|
||||
[cli_long_config] */
|
||||
|
||||
/* [cli_long_cipher]
|
||||
\par `--cipher <encrypt_method>`
|
||||
Select the encryption cipher. See `-m` for supported cipher names and key requirements. Short alias: `-m`.
|
||||
[cli_long_cipher] */
|
||||
|
||||
/* [cli_long_timeout]
|
||||
\par `--timeout <timeout>`
|
||||
Same behavior as `-t`; see that option for details. Short alias: `-t`.
|
||||
[cli_long_timeout] */
|
||||
|
||||
/* [cli_long_user]
|
||||
\par `--user <user_name>`
|
||||
Same behavior as `-a`; see that option for details. Short alias: `-a`.
|
||||
[cli_long_user] */
|
||||
|
||||
/* [cli_long_pid_file]
|
||||
\par `--pid-file <pid_file>`
|
||||
Same behavior as `-f`; see that option for details. Short alias: `-f`.
|
||||
[cli_long_pid_file] */
|
||||
|
||||
/* [cli_long_nofile]
|
||||
\par `--nofile <number>`
|
||||
Same behavior as `-n`; see that option for details. Short alias: `-n`.
|
||||
[cli_long_nofile] */
|
||||
|
||||
/* [cli_long_udp]
|
||||
\par `--udp`
|
||||
Enable both TCP and UDP relay, overriding the configured mode. Short alias: `-u`.
|
||||
[cli_long_udp] */
|
||||
|
||||
/* [cli_long_udp_only]
|
||||
\par `--udp-only`
|
||||
Enable UDP relay only, overriding the configured mode. Short alias: `-U`.
|
||||
[cli_long_udp_only] */
|
||||
|
||||
/* [cli_long_ipv6_first]
|
||||
\par `--ipv6-first`
|
||||
Prefer IPv6 DNS results; overrides configuration. This does not restrict connections to IPv6. Short alias: `-6`.
|
||||
[cli_long_ipv6_first] */
|
||||
|
||||
/* [cli_long_verbose]
|
||||
\par `--verbose`
|
||||
Same behavior as `-v`; see that option for details. Short alias: `-v`.
|
||||
[cli_long_verbose] */
|
||||
|
||||
/* [cli_long_listen_address]
|
||||
\par `--listen-address <server_host>`
|
||||
Set the listening address. Server and manager addresses may be repeated. Short alias: `-s`.
|
||||
[cli_long_listen_address] */
|
||||
|
||||
/* [cli_long_listen_port]
|
||||
\par `--listen-port <server_port>`
|
||||
Set the listening port. Short alias: `-p`.
|
||||
[cli_long_listen_port] */
|
||||
|
||||
/* [cli_long_outbound_address]
|
||||
\par `--outbound-address <local_address>`
|
||||
Set the source address for connections to destination servers. Short alias: `-b`.
|
||||
[cli_long_outbound_address] */
|
||||
|
||||
/* [cli_long_interface]
|
||||
\par `--interface <interface>`
|
||||
Same behavior as `-i`; see that option for details. Short alias: `-i`.
|
||||
[cli_long_interface] */
|
||||
|
||||
/* [cli_long_nameserver]
|
||||
\par `--nameserver <addr>`
|
||||
Same behavior as `-d`; see that option for details. Short alias: `-d`.
|
||||
[cli_long_nameserver] */
|
||||
|
||||
/* [cli-options]
|
||||
\snippet{doc} utils.c cli_short_f
|
||||
\snippet{doc} server.c cli_short_s
|
||||
\snippet{doc} server.c cli_short_p
|
||||
\snippet{doc} server.c cli_short_l
|
||||
\snippet{doc} utils.c cli_short_k
|
||||
\snippet{doc} utils.c cli_short_t
|
||||
\snippet{doc} utils.c cli_short_m
|
||||
@@ -1878,6 +1947,24 @@ Set the local address for outbound connections to destination servers.
|
||||
\snippet{doc} utils.c cli_short_v
|
||||
\snippet{doc} utils.c cli_short_6
|
||||
\snippet{doc} utils.c cli_short_A
|
||||
\snippet{doc} server.c cli_long_config
|
||||
\snippet{doc} server.c cli_long_cipher
|
||||
\snippet{doc} server.c cli_long_timeout
|
||||
\snippet{doc} server.c cli_long_user
|
||||
\snippet{doc} server.c cli_long_pid_file
|
||||
\snippet{doc} server.c cli_long_nofile
|
||||
\snippet{doc} server.c cli_long_udp
|
||||
\snippet{doc} server.c cli_long_udp_only
|
||||
\snippet{doc} server.c cli_long_ipv6_first
|
||||
\snippet{doc} server.c cli_long_verbose
|
||||
\snippet{doc} server.c cli_long_listen_address
|
||||
\snippet{doc} server.c cli_long_listen_port
|
||||
\snippet{doc} server.c cli_long_outbound_address
|
||||
\snippet{doc} server.c cli_long_interface
|
||||
\snippet{doc} server.c cli_long_nameserver
|
||||
\snippet{doc} utils.c cli_long_version
|
||||
\snippet{doc} utils.c cli_long_tcp_only
|
||||
\snippet{doc} utils.c cli_long_ipv4_first
|
||||
\snippet{doc} utils.c cli_long_fast_open
|
||||
\snippet{doc} utils.c cli_long_reuse_port
|
||||
\snippet{doc} utils.c cli_long_tcp_incoming_sndbuf
|
||||
@@ -1897,6 +1984,24 @@ Set the local address for outbound connections to destination servers.
|
||||
\snippet{doc} utils.c cli_long_nftables_sets
|
||||
[cli-options] */
|
||||
static struct option long_options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "cipher", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "user", required_argument, NULL, 'a' },
|
||||
{ "pid-file", required_argument, NULL, 'f' },
|
||||
{ "nofile", required_argument, NULL, 'n' },
|
||||
{ "udp", no_argument, NULL, 'u' },
|
||||
{ "udp-only", no_argument, NULL, 'U' },
|
||||
{ "ipv6-first", no_argument, NULL, '6' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "listen-address", required_argument, NULL, 's' },
|
||||
{ "listen-port", required_argument, NULL, 'p' },
|
||||
{ "outbound-address", required_argument, NULL, 'b' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "nameserver", required_argument, NULL, 'd' },
|
||||
{ "version", no_argument, NULL, GETOPT_VAL_VERSION },
|
||||
{ "tcp-only", no_argument, NULL, GETOPT_VAL_TCP_ONLY },
|
||||
{ "ipv4-first", no_argument, NULL, GETOPT_VAL_IPV4_FIRST },
|
||||
{ "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN },
|
||||
{ "reuse-port", no_argument, NULL, GETOPT_VAL_REUSE_PORT },
|
||||
{ "tcp-incoming-sndbuf", required_argument, NULL, GETOPT_VAL_TCP_INCOMING_SNDBUF },
|
||||
@@ -1922,13 +2027,26 @@ Set the local address for outbound connections to destination servers.
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int mode_set = 0;
|
||||
int ipv6first_set = 0;
|
||||
opterr = 0;
|
||||
|
||||
USE_TTY();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:b:c:i:d:a:n:huUv6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:k:t:m:b:c:i:d:a:n:huUv6A",
|
||||
long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case GETOPT_VAL_VERSION:
|
||||
cli_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case GETOPT_VAL_TCP_ONLY:
|
||||
mode = TCP_ONLY;
|
||||
mode_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_IPV4_FIRST:
|
||||
ipv6first = 0;
|
||||
ipv6first_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_FAST_OPEN:
|
||||
fast_open = 1;
|
||||
break;
|
||||
@@ -1945,7 +2063,7 @@ Set the local address for outbound connections to destination servers.
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &mtu) == -1) {
|
||||
FATAL("invalid MTU");
|
||||
cli_error("invalid MTU", c, NULL);
|
||||
}
|
||||
LOGI("set MTU to %d", mtu);
|
||||
break;
|
||||
@@ -1968,22 +2086,22 @@ Set the local address for outbound connections to destination servers.
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_sndbuf) == -1) {
|
||||
FATAL("invalid TCP incoming send buffer size");
|
||||
cli_error("invalid TCP incoming send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP incoming receive buffer size");
|
||||
cli_error("invalid TCP incoming receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_sndbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing send buffer size");
|
||||
cli_error("invalid TCP outgoing send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing receive buffer size");
|
||||
cli_error("invalid TCP outgoing receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
#ifdef USE_NFTABLES
|
||||
@@ -2000,6 +2118,11 @@ Set the local address for outbound connections to destination servers.
|
||||
is_bind_local_addr += parse_local_addr(&local_addr_v4, &local_addr_v6, optarg);
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
server_port = optarg;
|
||||
break;
|
||||
case GETOPT_VAL_PASSWORD:
|
||||
@@ -2011,6 +2134,11 @@ Set the local address for outbound connections to destination servers.
|
||||
pid_path = optarg;
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
int checked_timeout;
|
||||
if (ss_parse_int(optarg, 1, INT_MAX, &checked_timeout) != 0)
|
||||
cli_error("timeout must be a positive integer", c, NULL);
|
||||
}
|
||||
timeout = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -2031,14 +2159,16 @@ Set the local address for outbound connections to destination servers.
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
case 'n':
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &nofile) == -1) {
|
||||
FATAL("invalid nofile");
|
||||
cli_error("invalid nofile", c, NULL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 'u':
|
||||
mode_set = 1;
|
||||
mode = TCP_AND_UDP;
|
||||
break;
|
||||
case 'U':
|
||||
mode_set = 1;
|
||||
mode = UDP_ONLY;
|
||||
break;
|
||||
case 'v':
|
||||
@@ -2049,22 +2179,26 @@ Set the local address for outbound connections to destination servers.
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '6':
|
||||
ipv6first_set = 1;
|
||||
ipv6first = 1;
|
||||
break;
|
||||
case 'A':
|
||||
FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
|
||||
cli_error("one-time authentication was removed; use an AEAD cipher", 'A', NULL);
|
||||
break;
|
||||
case ':':
|
||||
cli_error("missing required argument for option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
// The option character is not recognized.
|
||||
LOGE("Unrecognized option: %s", optarg);
|
||||
opterr = 1;
|
||||
cli_error("unrecognized or invalid option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
default:
|
||||
cli_error("option is unsupported on this platform", c, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opterr) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
if (optind < argc) {
|
||||
cli_error("unexpected positional argument", 0, NULL);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
@@ -2104,7 +2238,7 @@ Set the local address for outbound connections to destination servers.
|
||||
if (plugin_opts == NULL) {
|
||||
plugin_opts = conf->plugin_opts;
|
||||
}
|
||||
if (mode == TCP_ONLY) {
|
||||
if (!mode_set) {
|
||||
mode = conf->mode;
|
||||
}
|
||||
if (mtu == 0) {
|
||||
@@ -2149,7 +2283,7 @@ Set the local address for outbound connections to destination servers.
|
||||
if (nameservers == NULL) {
|
||||
nameservers = conf->nameserver;
|
||||
}
|
||||
if (ipv6first == 0) {
|
||||
if (!ipv6first_set) {
|
||||
ipv6first = conf->ipv6_first;
|
||||
}
|
||||
if (acl == 0 && conf->acl != NULL) {
|
||||
|
||||
+180
-16
@@ -957,6 +957,91 @@ main(int argc, char **argv)
|
||||
|
||||
memset(remote_addr, 0, sizeof(ss_addr_t) * MAX_REMOTE_NUM);
|
||||
|
||||
/* [cli_long_config]
|
||||
\par `--config <config_file>`
|
||||
Read JSON configuration. See the CLI conventions for precedence and default-value behavior. Short alias: `-c`.
|
||||
[cli_long_config] */
|
||||
|
||||
/* [cli_long_cipher]
|
||||
\par `--cipher <encrypt_method>`
|
||||
Select the encryption cipher. See `-m` for supported cipher names and key requirements. Short alias: `-m`.
|
||||
[cli_long_cipher] */
|
||||
|
||||
/* [cli_long_timeout]
|
||||
\par `--timeout <timeout>`
|
||||
Same behavior as `-t`; see that option for details. Short alias: `-t`.
|
||||
[cli_long_timeout] */
|
||||
|
||||
/* [cli_long_user]
|
||||
\par `--user <user_name>`
|
||||
Same behavior as `-a`; see that option for details. Short alias: `-a`.
|
||||
[cli_long_user] */
|
||||
|
||||
/* [cli_long_pid_file]
|
||||
\par `--pid-file <pid_file>`
|
||||
Same behavior as `-f`; see that option for details. Short alias: `-f`.
|
||||
[cli_long_pid_file] */
|
||||
|
||||
/* [cli_long_nofile]
|
||||
\par `--nofile <number>`
|
||||
Same behavior as `-n`; see that option for details. Short alias: `-n`.
|
||||
[cli_long_nofile] */
|
||||
|
||||
/* [cli_long_udp]
|
||||
\par `--udp`
|
||||
Enable both TCP and UDP relay, overriding the configured mode. Short alias: `-u`.
|
||||
[cli_long_udp] */
|
||||
|
||||
/* [cli_long_udp_only]
|
||||
\par `--udp-only`
|
||||
Enable UDP relay only, overriding the configured mode. Short alias: `-U`.
|
||||
[cli_long_udp_only] */
|
||||
|
||||
/* [cli_long_ipv6_first]
|
||||
\par `--ipv6-first`
|
||||
Prefer IPv6 DNS results; overrides configuration. This does not restrict connections to IPv6. Short alias: `-6`.
|
||||
[cli_long_ipv6_first] */
|
||||
|
||||
/* [cli_long_verbose]
|
||||
\par `--verbose`
|
||||
Same behavior as `-v`; see that option for details. Short alias: `-v`.
|
||||
[cli_long_verbose] */
|
||||
|
||||
/* [cli_long_server]
|
||||
\par `--server <server_host>`
|
||||
Set a remote Shadowsocks server hostname or IP address; may be repeated. Short alias: `-s`.
|
||||
[cli_long_server] */
|
||||
|
||||
/* [cli_long_server_port]
|
||||
\par `--server-port <server_port>`
|
||||
Set the remote Shadowsocks server port. Short alias: `-p`.
|
||||
[cli_long_server_port] */
|
||||
|
||||
/* [cli_long_listen_address]
|
||||
\par `--listen-address <local_address>`
|
||||
Set the listening address. Server and manager addresses may be repeated. Short alias: `-b`.
|
||||
[cli_long_listen_address] */
|
||||
|
||||
/* [cli_long_listen_port]
|
||||
\par `--listen-port <local_port>`
|
||||
Set the listening port. Short alias: `-l`.
|
||||
[cli_long_listen_port] */
|
||||
|
||||
/* [cli_long_interface]
|
||||
\par `--interface <interface>`
|
||||
Same behavior as `-i`; see that option for details. Short alias: `-i`.
|
||||
[cli_long_interface] */
|
||||
|
||||
/* [cli_long_destination]
|
||||
\par `--destination <addr:port>`
|
||||
Set the forwarding destination as HOST:PORT; use [IPv6]:PORT for an IPv6 literal. Short alias: `-L`.
|
||||
[cli_long_destination] */
|
||||
|
||||
/* [cli_long_vpn]
|
||||
\par `--vpn`
|
||||
Same behavior as `-V`; see that option for details. Short alias: `-V`.
|
||||
[cli_long_vpn] */
|
||||
|
||||
/* [cli-options]
|
||||
\snippet{doc} utils.c cli_short_f
|
||||
\snippet{doc} utils.c cli_short_s
|
||||
@@ -978,6 +1063,26 @@ main(int argc, char **argv)
|
||||
\snippet{doc} utils.c cli_short_V
|
||||
\snippet{doc} utils.c cli_short_6
|
||||
\snippet{doc} utils.c cli_short_A
|
||||
\snippet{doc} tunnel.c cli_long_config
|
||||
\snippet{doc} tunnel.c cli_long_cipher
|
||||
\snippet{doc} tunnel.c cli_long_timeout
|
||||
\snippet{doc} tunnel.c cli_long_user
|
||||
\snippet{doc} tunnel.c cli_long_pid_file
|
||||
\snippet{doc} tunnel.c cli_long_nofile
|
||||
\snippet{doc} tunnel.c cli_long_udp
|
||||
\snippet{doc} tunnel.c cli_long_udp_only
|
||||
\snippet{doc} tunnel.c cli_long_ipv6_first
|
||||
\snippet{doc} tunnel.c cli_long_verbose
|
||||
\snippet{doc} tunnel.c cli_long_server
|
||||
\snippet{doc} tunnel.c cli_long_server_port
|
||||
\snippet{doc} tunnel.c cli_long_listen_address
|
||||
\snippet{doc} tunnel.c cli_long_listen_port
|
||||
\snippet{doc} tunnel.c cli_long_interface
|
||||
\snippet{doc} tunnel.c cli_long_destination
|
||||
\snippet{doc} utils.c cli_long_version
|
||||
\snippet{doc} utils.c cli_long_tcp_only
|
||||
\snippet{doc} utils.c cli_long_ipv4_first
|
||||
\snippet{doc} tunnel.c cli_long_vpn
|
||||
\snippet{doc} utils.c cli_long_fast_open
|
||||
\snippet{doc} utils.c cli_long_mtu
|
||||
\snippet{doc} utils.c cli_long_no_delay
|
||||
@@ -994,6 +1099,28 @@ main(int argc, char **argv)
|
||||
\snippet{doc} utils.c cli_long_help
|
||||
[cli-options] */
|
||||
static struct option long_options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "cipher", required_argument, NULL, 'm' },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "user", required_argument, NULL, 'a' },
|
||||
{ "pid-file", required_argument, NULL, 'f' },
|
||||
{ "nofile", required_argument, NULL, 'n' },
|
||||
{ "udp", no_argument, NULL, 'u' },
|
||||
{ "udp-only", no_argument, NULL, 'U' },
|
||||
{ "ipv6-first", no_argument, NULL, '6' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "server", required_argument, NULL, 's' },
|
||||
{ "server-port", required_argument, NULL, 'p' },
|
||||
{ "listen-address", required_argument, NULL, 'b' },
|
||||
{ "listen-port", required_argument, NULL, 'l' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "destination", required_argument, NULL, 'L' },
|
||||
{ "version", no_argument, NULL, GETOPT_VAL_VERSION },
|
||||
{ "tcp-only", no_argument, NULL, GETOPT_VAL_TCP_ONLY },
|
||||
{ "ipv4-first", no_argument, NULL, GETOPT_VAL_IPV4_FIRST },
|
||||
#ifdef __ANDROID__
|
||||
{ "vpn", no_argument, NULL, 'V' },
|
||||
#endif
|
||||
{ "fast-open", no_argument, NULL, GETOPT_VAL_FAST_OPEN },
|
||||
{ "mtu", required_argument, NULL, GETOPT_VAL_MTU },
|
||||
{ "no-delay", no_argument, NULL, GETOPT_VAL_NODELAY },
|
||||
@@ -1011,24 +1138,37 @@ main(int argc, char **argv)
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int mode_set = 0;
|
||||
int ipv6first_set = 0;
|
||||
opterr = 0;
|
||||
|
||||
USE_TTY();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUvV6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:l:k:t:m:i:c:b:L:a:n:huUvV6A",
|
||||
long_options, NULL)) != -1) {
|
||||
#else
|
||||
while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:n:huUv6A",
|
||||
while ((c = getopt_long(argc, argv, ":f:s:p:l:k:t:m:i:c:b:L:a:n:huUv6A",
|
||||
long_options, NULL)) != -1) {
|
||||
#endif
|
||||
switch (c) {
|
||||
case GETOPT_VAL_VERSION:
|
||||
cli_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case GETOPT_VAL_TCP_ONLY:
|
||||
mode = TCP_ONLY;
|
||||
mode_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_IPV4_FIRST:
|
||||
ipv6first = 0;
|
||||
ipv6first_set = 1;
|
||||
break;
|
||||
case GETOPT_VAL_FAST_OPEN:
|
||||
fast_open = 1;
|
||||
break;
|
||||
case GETOPT_VAL_MTU:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &mtu) == -1) {
|
||||
FATAL("invalid MTU");
|
||||
cli_error("invalid MTU", c, NULL);
|
||||
}
|
||||
LOGI("set MTU to %d", mtu);
|
||||
break;
|
||||
@@ -1055,22 +1195,22 @@ main(int argc, char **argv)
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_sndbuf) == -1) {
|
||||
FATAL("invalid TCP incoming send buffer size");
|
||||
cli_error("invalid TCP incoming send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_INCOMING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_incoming_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP incoming receive buffer size");
|
||||
cli_error("invalid TCP incoming receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_SNDBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_sndbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing send buffer size");
|
||||
cli_error("invalid TCP outgoing send buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case GETOPT_VAL_TCP_OUTGOING_RCVBUF:
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &tcp_outgoing_rcvbuf) == -1) {
|
||||
FATAL("invalid TCP outgoing receive buffer size");
|
||||
cli_error("invalid TCP outgoing receive buffer size", c, NULL);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
@@ -1079,9 +1219,19 @@ main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
remote_port = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
{
|
||||
uint16_t checked_port;
|
||||
if (ss_parse_uint16_port(optarg, &checked_port) != 0)
|
||||
cli_error("port must be an integer from 1 to 65535", c, NULL);
|
||||
}
|
||||
local_port = optarg;
|
||||
break;
|
||||
case GETOPT_VAL_PASSWORD:
|
||||
@@ -1093,6 +1243,11 @@ main(int argc, char **argv)
|
||||
pid_path = optarg;
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
int checked_timeout;
|
||||
if (ss_parse_int(optarg, 1, INT_MAX, &checked_timeout) != 0)
|
||||
cli_error("timeout must be a positive integer", c, NULL);
|
||||
}
|
||||
timeout = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
@@ -1108,9 +1263,11 @@ main(int argc, char **argv)
|
||||
local_addr = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
mode_set = 1;
|
||||
mode = TCP_AND_UDP;
|
||||
break;
|
||||
case 'U':
|
||||
mode_set = 1;
|
||||
mode = UDP_ONLY;
|
||||
break;
|
||||
case 'L':
|
||||
@@ -1122,7 +1279,7 @@ main(int argc, char **argv)
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
case 'n':
|
||||
if (ss_parse_int(optarg, 0, INT_MAX, &nofile) == -1) {
|
||||
FATAL("invalid nofile");
|
||||
cli_error("invalid nofile", c, NULL);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -1134,6 +1291,7 @@ main(int argc, char **argv)
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '6':
|
||||
ipv6first_set = 1;
|
||||
ipv6first = 1;
|
||||
break;
|
||||
#ifdef __ANDROID__
|
||||
@@ -1142,19 +1300,22 @@ main(int argc, char **argv)
|
||||
break;
|
||||
#endif
|
||||
case 'A':
|
||||
FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
|
||||
cli_error("one-time authentication was removed; use an AEAD cipher", 'A', NULL);
|
||||
break;
|
||||
case ':':
|
||||
cli_error("missing required argument for option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
case '?':
|
||||
// The option character is not recognized.
|
||||
LOGE("Unrecognized option: %s", optarg);
|
||||
opterr = 1;
|
||||
cli_error("unrecognized or invalid option", optopt, argv[optind - 1]);
|
||||
break;
|
||||
default:
|
||||
cli_error("option is unsupported on this platform", c, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opterr) {
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
if (optind < argc) {
|
||||
cli_error("unexpected positional argument", 0, NULL);
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
@@ -1203,7 +1364,10 @@ main(int argc, char **argv)
|
||||
if (tunnel_addr_str == NULL) {
|
||||
tunnel_addr_str = conf->tunnel_address;
|
||||
}
|
||||
if (mode == TCP_ONLY) {
|
||||
if (!ipv6first_set) {
|
||||
ipv6first = conf->ipv6_first;
|
||||
}
|
||||
if (!mode_set) {
|
||||
mode = conf->mode;
|
||||
}
|
||||
if (mtu == 0) {
|
||||
|
||||
+148
-147
@@ -369,7 +369,7 @@ some require backend support):
|
||||
\snippet stream.c cli-stream-ciphers
|
||||
|
||||
The `2022-blake3-*` ciphers implement Shadowsocks 2022 (SIP022). They require
|
||||
a base64-encoded pre-shared key supplied with *-k*: 16 bytes for
|
||||
a base64-encoded pre-shared key supplied with `--key` or `--password` (*-k*): 16 bytes for
|
||||
2022-blake3-aes-128-gcm and 32 bytes for the other 2022 ciphers.
|
||||
Generate a 32-byte key with `openssl rand -base64 32`.
|
||||
Passwords are not stretched into keys for these ciphers.
|
||||
@@ -409,8 +409,7 @@ Send outbound traffic through the specified network interface where supported by
|
||||
|
||||
/* [cli_short_b]
|
||||
\par `-b <local_address>`
|
||||
Specify the local address to use while this client is making outbound
|
||||
connections to the server.
|
||||
Set the local address to bind for the client listener.
|
||||
[cli_short_b] */
|
||||
|
||||
/* [cli_short_u]
|
||||
@@ -560,167 +559,169 @@ Set the executable path of ss-server used by ss-manager.
|
||||
Linux builds with USE_NFTABLES only: add malicious IP addresses to nftables sets. Format: `[<table1>:]<set1>[,[<table2>:]<set2>...]`.
|
||||
[cli_long_nftables_sets] */
|
||||
|
||||
void
|
||||
usage()
|
||||
/* [cli_long_version]
|
||||
\par `--version`
|
||||
Print the program name and version to standard output and exit successfully.
|
||||
[cli_long_version] */
|
||||
|
||||
/* [cli_long_tcp_only]
|
||||
\par `--tcp-only`
|
||||
Enable TCP relay only, overriding the configuration file's mode.
|
||||
The last of `--tcp-only`, `--udp`, and `--udp-only` wins.
|
||||
[cli_long_tcp_only] */
|
||||
|
||||
/* [cli_long_ipv4_first]
|
||||
\par `--ipv4-first`
|
||||
Prefer IPv4 DNS results, overriding `ipv6_first` in the configuration file.
|
||||
This is an address preference, not a restriction to IPv4. The last of
|
||||
`--ipv4-first` and `--ipv6-first` wins.
|
||||
[cli_long_ipv4_first] */
|
||||
|
||||
static const char *
|
||||
cli_program(void)
|
||||
{
|
||||
printf("\n");
|
||||
printf("shadowsocks-c %s\n\n", VERSION);
|
||||
printf(
|
||||
" maintained by Max Lv <max.c.lv@gmail.com> and Linus Yang <laokongzi@gmail.com>\n\n");
|
||||
printf(" usage:\n\n");
|
||||
#ifdef MODULE_LOCAL
|
||||
printf(" ss-local\n");
|
||||
#elif MODULE_REMOTE
|
||||
printf(" ss-server\n");
|
||||
#elif MODULE_TUNNEL
|
||||
printf(" ss-tunnel\n");
|
||||
#elif MODULE_REDIR
|
||||
printf(" ss-redir\n");
|
||||
#elif MODULE_MANAGER
|
||||
printf(" ss-manager\n");
|
||||
#endif
|
||||
printf("\n");
|
||||
printf(
|
||||
" -s <server_host> Host name or IP address of your remote server.\n");
|
||||
printf(
|
||||
" -p <server_port> Port number of your remote server.\n");
|
||||
printf(
|
||||
" -l <local_port> Port number of your local server.\n");
|
||||
printf(
|
||||
" -k <password> Password of your remote server.\n");
|
||||
printf(
|
||||
" -m <encrypt_method> Encrypt method: rc4-md5, \n");
|
||||
printf(
|
||||
" aes-128-gcm, aes-192-gcm, aes-256-gcm,\n");
|
||||
printf(
|
||||
" aes-128-cfb, aes-192-cfb, aes-256-cfb,\n");
|
||||
printf(
|
||||
" aes-128-ctr, aes-192-ctr, aes-256-ctr,\n");
|
||||
printf(
|
||||
" camellia-128-cfb, camellia-192-cfb,\n");
|
||||
printf(
|
||||
" camellia-256-cfb, bf-cfb,\n");
|
||||
printf(
|
||||
" chacha20-ietf-poly1305,\n");
|
||||
#ifdef FS_HAVE_XCHACHA20IETF
|
||||
printf(
|
||||
" xchacha20-ietf-poly1305,\n");
|
||||
#endif
|
||||
printf(
|
||||
" salsa20, chacha20 and chacha20-ietf.\n");
|
||||
printf(
|
||||
" The default cipher is chacha20-ietf-poly1305.\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
" [-a <user>] Run as another user.\n");
|
||||
printf(
|
||||
" [-f <pid_file>] The file path to store pid.\n");
|
||||
printf(
|
||||
" [-t <timeout>] Socket timeout in seconds.\n");
|
||||
printf(
|
||||
" [-c <config_file>] The path to config file.\n");
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
printf(
|
||||
" [-n <number>] Max number of open files.\n");
|
||||
#endif
|
||||
#ifndef MODULE_REDIR
|
||||
printf(
|
||||
" [-i <interface>] Network interface to bind.\n");
|
||||
#endif
|
||||
printf(
|
||||
" [-b <local_address>] Local address to bind.\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
" [-u] Enable UDP relay.\n");
|
||||
#ifdef MODULE_REDIR
|
||||
printf(
|
||||
" TPROXY is required in redir mode.\n");
|
||||
#endif
|
||||
printf(
|
||||
" [-U] Enable UDP relay and disable TCP relay.\n");
|
||||
#ifdef MODULE_REDIR
|
||||
printf(
|
||||
" [-T] Use tproxy instead of redirect (for tcp).\n");
|
||||
return "ss-local";
|
||||
#elif defined(MODULE_REMOTE)
|
||||
return "ss-server";
|
||||
#elif defined(MODULE_TUNNEL)
|
||||
return "ss-tunnel";
|
||||
#elif defined(MODULE_REDIR)
|
||||
return "ss-redir";
|
||||
#elif defined(MODULE_MANAGER)
|
||||
return "ss-manager";
|
||||
#else
|
||||
return "shadowsocks-c";
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
cli_version(void)
|
||||
{
|
||||
printf("%s (shadowsocks-c) %s\n", cli_program(), VERSION);
|
||||
}
|
||||
|
||||
void
|
||||
cli_error(const char *message, int option, const char *token)
|
||||
{
|
||||
fprintf(stderr, "%s: %s", cli_program(), message);
|
||||
/* Report option names, never attached passwords or positional values. */
|
||||
if (token != NULL && token[0] == '-' && token[1] == '-') {
|
||||
size_t length = 2;
|
||||
while (isalnum((unsigned char)token[length]) || token[length] == '-')
|
||||
length++;
|
||||
if (length > 2 && length < 80)
|
||||
fprintf(stderr, " '%.*s'", (int)length, token);
|
||||
} else if (option > 0 && option < 128 && isalnum((unsigned char)option)) {
|
||||
fprintf(stderr, " '-%c'", option);
|
||||
}
|
||||
fprintf(stderr, ". Try '%s --help'.\n", cli_program());
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void
|
||||
cli_help_option(const char *flags, const char *description)
|
||||
{
|
||||
printf(" %-36s %s\n", flags, description);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
cli_version();
|
||||
printf("Usage: %s [options]\n\n", cli_program());
|
||||
puts("Connection:");
|
||||
#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER)
|
||||
cli_help_option("-s, --listen-address HOST", "Server listening address; may be repeated.");
|
||||
#ifdef MODULE_REMOTE
|
||||
printf(
|
||||
" [-6] Resovle hostname to IPv6 address first.\n");
|
||||
cli_help_option("-p, --listen-port PORT", "Server listening port.");
|
||||
cli_help_option("-b, --outbound-address ADDRESS", "Source address for outbound connections.");
|
||||
#endif
|
||||
#else
|
||||
cli_help_option("-s, --server HOST", "Remote server hostname or IP; may be repeated.");
|
||||
cli_help_option("-p, --server-port PORT", "Remote server port.");
|
||||
cli_help_option("-b, --listen-address ADDRESS", "Local address to bind.");
|
||||
cli_help_option("-l, --listen-port PORT", "Local listening port.");
|
||||
#endif
|
||||
#ifdef MODULE_LOCAL
|
||||
cli_help_option("--server-url URL", "Import an ss:// URL; later options override it.");
|
||||
#endif
|
||||
printf("\n");
|
||||
#ifdef MODULE_TUNNEL
|
||||
printf(
|
||||
" [-L <addr>:<port>] Destination server address and port\n");
|
||||
printf(
|
||||
" for local port forwarding.\n");
|
||||
cli_help_option("-L, --destination HOST:PORT", "Destination for local port forwarding.");
|
||||
#endif
|
||||
#ifdef MODULE_REMOTE
|
||||
printf(
|
||||
" [-d <addr>] Name servers for internal DNS resolver.\n");
|
||||
puts("\nConfiguration and credentials:");
|
||||
cli_help_option("-c, --config FILE", "Read JSON configuration.");
|
||||
cli_help_option("-m, --cipher NAME", "Cipher (default: chacha20-ietf-poly1305).");
|
||||
cli_help_option("-k, --password SECRET", "Password, or a base64 pre-shared key for AEAD-2022.");
|
||||
#ifndef MODULE_MANAGER
|
||||
cli_help_option("--key BASE64", "Use an explicit base64 key instead of a password.");
|
||||
#endif
|
||||
printf(
|
||||
" [--reuse-port] Enable port reuse.\n");
|
||||
#if defined(MODULE_REMOTE) || defined(MODULE_LOCAL) || defined(MODULE_REDIR)
|
||||
printf(
|
||||
" [--fast-open] Enable TCP fast open.\n");
|
||||
printf(
|
||||
" with Linux kernel > 3.7.0.\n");
|
||||
#endif
|
||||
printf(
|
||||
" [--tcp-incoming-sndbuf] Size of the incoming connection TCP send buffer.\n");
|
||||
printf(
|
||||
" [--tcp-incoming-rcvbuf] Size of the incoming connection TCP receive buffer.\n");
|
||||
printf(
|
||||
" [--tcp-outgoing-sndbuf] Size of the outgoing connection TCP send buffer.\n");
|
||||
printf(
|
||||
" [--tcp-outgoing-rcvbuf] Size of the outgoing connection TCP receive buffer.\n");
|
||||
#if defined(MODULE_REMOTE) || defined(MODULE_LOCAL)
|
||||
printf(
|
||||
" [--acl <acl_file>] Path to ACL (Access Control List).\n");
|
||||
puts(" AEAD-2022: 2022-blake3-aes-128-gcm, 2022-blake3-aes-256-gcm,");
|
||||
puts(" 2022-blake3-chacha20-poly1305.");
|
||||
puts("\nTransport and networking:");
|
||||
cli_help_option("--tcp-only", "TCP only (default); overrides the configured mode.");
|
||||
cli_help_option("-u, --udp", "Enable both TCP and UDP relay.");
|
||||
cli_help_option("-U, --udp-only", "Enable UDP relay only.");
|
||||
cli_help_option("--ipv4-first", "Prefer IPv4 DNS results; overrides configuration.");
|
||||
cli_help_option("-6, --ipv6-first", "Prefer IPv6 DNS results.");
|
||||
cli_help_option("-t, --timeout SECONDS", "Socket timeout (default: 60).");
|
||||
#ifndef MODULE_REDIR
|
||||
cli_help_option("-i, --interface NAME", "Outbound network interface, where supported.");
|
||||
#endif
|
||||
#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER)
|
||||
printf(
|
||||
" [--manager-address <addr>] UNIX domain socket address.\n");
|
||||
cli_help_option("-d, --nameserver ADDRESS", "Name servers for the internal DNS resolver.");
|
||||
#endif
|
||||
#ifdef MODULE_MANAGER
|
||||
printf(
|
||||
" [--executable <path>] Path to the executable of ss-server.\n");
|
||||
printf(
|
||||
" [-D <path>] Path to the working directory of ss-manager.\n");
|
||||
#endif
|
||||
printf(
|
||||
" [--mtu <MTU>] MTU of your network interface.\n");
|
||||
#ifdef __linux__
|
||||
printf(
|
||||
" [--mptcp] Enable Multipath TCP on MPTCP Kernel.\n");
|
||||
#ifdef USE_NFTABLES
|
||||
printf(
|
||||
" [--nftables-sets <sets>] Add malicious IP into nftables sets.\n");
|
||||
printf(
|
||||
" sets spec: [<table1>:]<set1>[,[<table2>:]<set2>...]\n");
|
||||
#ifdef MODULE_REDIR
|
||||
cli_help_option("-T, --tproxy", "Use TPROXY for TCP; UDP always requires TPROXY.");
|
||||
#endif
|
||||
cli_help_option("--mtu BYTES", "Network MTU (0 selects the default).");
|
||||
cli_help_option("--fast-open", "Enable TCP Fast Open where supported.");
|
||||
cli_help_option("--reuse-port", "Enable port reuse where supported.");
|
||||
cli_help_option("--no-delay", "Enable TCP_NODELAY.");
|
||||
#if !defined(MODULE_MANAGER) && (!defined(MODULE_REMOTE) || defined(__linux__))
|
||||
cli_help_option("--mptcp", "Enable Multipath TCP where supported.");
|
||||
#endif
|
||||
#ifndef MODULE_MANAGER
|
||||
printf(
|
||||
" [--no-delay] Enable TCP_NODELAY.\n");
|
||||
printf(
|
||||
" [--key <key_in_base64>] Key of your remote server.\n");
|
||||
cli_help_option("--tcp-incoming-sndbuf BYTES", "Incoming TCP send buffer (0: system default).");
|
||||
cli_help_option("--tcp-incoming-rcvbuf BYTES", "Incoming TCP receive buffer (0: system default).");
|
||||
cli_help_option("--tcp-outgoing-sndbuf BYTES", "Outgoing TCP send buffer (0: system default).");
|
||||
cli_help_option("--tcp-outgoing-rcvbuf BYTES", "Outgoing TCP receive buffer (0: system default).");
|
||||
#endif
|
||||
puts("\nAccess control and plugins:");
|
||||
#if defined(MODULE_LOCAL) || defined(MODULE_REMOTE) || defined(MODULE_MANAGER)
|
||||
cli_help_option("--acl FILE", "Access control list.");
|
||||
#endif
|
||||
#if defined(MODULE_REMOTE) && defined(__linux__) && defined(USE_NFTABLES)
|
||||
cli_help_option("--nftables-sets SETS", "Record malicious IPs in [table:]set[,set...] entries.");
|
||||
#endif
|
||||
cli_help_option("--plugin NAME", "SIP003 plugin (requires plugin support in this build).");
|
||||
cli_help_option("--plugin-opts OPTIONS", "Options passed to the SIP003 plugin.");
|
||||
puts("\nProcess and diagnostics:");
|
||||
cli_help_option("-a, --user USER", "Run as the specified user.");
|
||||
cli_help_option("-f, --pid-file FILE", "Daemonize and write the process ID to FILE.");
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
cli_help_option("-n, --nofile COUNT", "Maximum number of open files.");
|
||||
#endif
|
||||
#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER)
|
||||
cli_help_option("--manager-address ADDRESS", "Manager UNIX socket path or IP address and port.");
|
||||
#endif
|
||||
#ifdef MODULE_MANAGER
|
||||
cli_help_option("--executable PATH", "ss-server executable used for managed servers.");
|
||||
cli_help_option("-D, --workdir DIRECTORY", "Working directory for managed servers.");
|
||||
#endif
|
||||
#if defined(__ANDROID__) && (defined(MODULE_LOCAL) || defined(MODULE_TUNNEL))
|
||||
cli_help_option("-V, --vpn", "Enable Android VPN socket protection.");
|
||||
#ifdef MODULE_LOCAL
|
||||
printf(
|
||||
" [--server-url <ss_url>] Server as an ss:// URL, carrying the\n");
|
||||
printf(
|
||||
" address, cipher, password and plugin.\n");
|
||||
cli_help_option("-S, --stat-path PATH", "Android traffic-statistics socket.");
|
||||
#endif
|
||||
printf(
|
||||
" [--plugin <name>] Enable SIP003 plugin. (Experimental)\n");
|
||||
printf(
|
||||
" [--plugin-opts <options>] Set SIP003 plugin options. (Experimental)\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
" [-v] Verbose mode.\n");
|
||||
printf(
|
||||
" [-h, --help] Print this message.\n");
|
||||
printf("\n");
|
||||
#endif
|
||||
cli_help_option("-v, --verbose", "Enable verbose logging.");
|
||||
cli_help_option("-h, --help", "Show this help and exit.");
|
||||
cli_help_option("--version", "Show the version and exit.");
|
||||
puts("\nOptions apply in command-line order; later mode and address-family flags win.");
|
||||
puts("No positional arguments are accepted. Use --option=value for values starting with '-'.");
|
||||
puts("The obsolete -A option is rejected; use an AEAD cipher instead.");
|
||||
puts("Manual: https://shadowsocks.github.io/shadowsocks-c/");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -219,6 +219,8 @@ int ss_parse_uint16_port(const char *s, uint16_t *out);
|
||||
int run_as(const char *user);
|
||||
void FATAL(const char *msg) __attribute__((noreturn));
|
||||
void usage(void);
|
||||
void cli_version(void);
|
||||
void cli_error(const char *message, int option, const char *token) __attribute__((noreturn));
|
||||
void daemonize(const char *path);
|
||||
char *ss_strndup(const char *s, size_t n);
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
|
||||
@@ -214,3 +214,13 @@ ss_add_test(test_resolv_async
|
||||
"test_resolv_async.c;${PROJECT_SOURCE_DIR}/src/resolv.c;${PROJECT_SOURCE_DIR}/src/utils.c"
|
||||
"ss_ev;ss_cares;ss_sodium")
|
||||
set_tests_properties(test_resolv_async PROPERTIES TIMEOUT 30)
|
||||
|
||||
if(SS_BUILD_EXECUTABLES AND NOT CMAKE_CROSSCOMPILING)
|
||||
find_package(Python3 COMPONENTS Interpreter QUIET)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
add_test(NAME test_cli
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_cli.py
|
||||
--bin-dir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
set_tests_properties(test_cli PROPERTIES LABELS "integration" TIMEOUT 60)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise CLI parsing, diagnostics, and configuration overrides on real binaries."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
|
||||
BIN_DIR = Path(os.environ.get('SS_BIN_DIR', 'build/bin')).resolve()
|
||||
|
||||
|
||||
class CliTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
suffix = '.exe' if os.name == 'nt' else ''
|
||||
cls.binaries = {name: BIN_DIR / ('ss-' + name + suffix)
|
||||
for name in ('local', 'server', 'tunnel', 'redir', 'manager')
|
||||
if (BIN_DIR / ('ss-' + name + suffix)).is_file()}
|
||||
for name in ('local', 'server', 'tunnel'):
|
||||
if name not in cls.binaries:
|
||||
raise RuntimeError(f'Missing required CLI binary: ss-{name}')
|
||||
|
||||
def run_cli(self, name, *arguments):
|
||||
return subprocess.run([str(self.binaries[name]), *arguments],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
|
||||
def test_help_and_version(self):
|
||||
for name in self.binaries:
|
||||
with self.subTest(name=name):
|
||||
help_result = self.run_cli(name, '--help')
|
||||
self.assertEqual(help_result.returncode, 0)
|
||||
self.assertEqual(help_result.stderr, '')
|
||||
for flag in ('--config', '--cipher', '--tcp-only', '--udp-only',
|
||||
'--ipv4-first', '--ipv6-first', '--version'):
|
||||
self.assertIn(flag, help_result.stdout)
|
||||
version = self.run_cli(name, '--version')
|
||||
self.assertEqual(version.returncode, 0)
|
||||
self.assertRegex(version.stdout, rf'^ss-{name} \(shadowsocks-c\) \d+\.\d+')
|
||||
self.assertEqual(len(version.stdout.splitlines()), 1)
|
||||
self.assertEqual(version.stderr, '')
|
||||
if name == 'manager':
|
||||
self.assertNotIn('--server-port', help_result.stdout)
|
||||
self.assertNotIn('--listen-port', help_result.stdout)
|
||||
self.assertNotIn('--tcp-incoming-sndbuf', help_result.stdout)
|
||||
self.assertNotIn('--mptcp', help_result.stdout)
|
||||
elif name == 'server':
|
||||
self.assertIn('--outbound-address', help_result.stdout)
|
||||
self.assertNotIn('--server-port', help_result.stdout)
|
||||
|
||||
def test_long_aliases_accept_their_arguments(self):
|
||||
for name in self.binaries:
|
||||
args = ['--config', 'not-loaded-before-help.json', '--cipher', 'aes-128-gcm',
|
||||
'--password', 'test-password', '--timeout', '60', '--user', 'nobody',
|
||||
'--pid-file', 'not-created.pid', '--verbose', '--udp', '--udp-only',
|
||||
'--tcp-only', '--ipv6-first', '--ipv4-first', '--listen-address', '127.0.0.1']
|
||||
if name not in ('server', 'manager'):
|
||||
args += ['--server', '127.0.0.1', '--server-port', '8388']
|
||||
if name != 'manager':
|
||||
args += ['--listen-port', '1080']
|
||||
if name == 'server':
|
||||
args += ['--outbound-address', '127.0.0.1']
|
||||
if name != 'redir':
|
||||
args += ['--interface', 'lo']
|
||||
if name in ('server', 'manager'):
|
||||
args += ['--nameserver', '127.0.0.1']
|
||||
if name == 'tunnel':
|
||||
args += ['--destination', '127.0.0.1:80']
|
||||
if name == 'redir':
|
||||
args += ['--tproxy']
|
||||
result = self.run_cli(name, *args, '--help')
|
||||
self.assertEqual(result.returncode, 0, (name, result.stderr))
|
||||
self.assertIn('Usage:', result.stdout)
|
||||
|
||||
def test_usage_errors_are_stderr_exit_two_without_secrets(self):
|
||||
secret = 'private-argument-value'
|
||||
for name in self.binaries:
|
||||
for args in (['--unknown=' + secret], ['-Z' + secret], [secret],
|
||||
['--', secret], ['--password'], ['-k'], ['-A']):
|
||||
with self.subTest(name=name, arguments=args[:1]):
|
||||
result = self.run_cli(name, *args)
|
||||
self.assertEqual(result.returncode, 2)
|
||||
self.assertEqual(result.stdout, '')
|
||||
self.assertIn('--help', result.stderr)
|
||||
self.assertNotIn(secret, result.stderr)
|
||||
result = self.run_cli(name, '--password')
|
||||
self.assertIn('missing required argument', result.stderr)
|
||||
self.assertIn('--password', result.stderr)
|
||||
for name in ('server', 'manager'):
|
||||
if name in self.binaries:
|
||||
self.assertEqual(self.run_cli(name, '-l', '1080').returncode, 2)
|
||||
|
||||
def test_transport_and_address_family_override_configuration(self):
|
||||
cases = [(['--tcp-only', '--ipv4-first'], False, False),
|
||||
(['--udp-only', '--tcp-only', '--ipv6-first', '--ipv4-first'], False, False),
|
||||
(['--tcp-only', '--udp', '--ipv4-first', '--ipv6-first'], True, True)]
|
||||
for args, udp, ipv6 in cases:
|
||||
with self.subTest(args=args), tempfile.TemporaryDirectory() as directory:
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(('127.0.0.1', 0))
|
||||
port = reservation.getsockname()[1]
|
||||
config = Path(directory) / 'config.json'
|
||||
config.write_text(json.dumps({'server': '127.0.0.1', 'server_port': 9,
|
||||
'local_address': '127.0.0.1', 'local_port': port, 'password': 'test-password',
|
||||
'method': 'aes-128-gcm', 'mode': 'udp_only', 'ipv6_first': True}))
|
||||
with tempfile.TemporaryFile(mode='w+') as log:
|
||||
proc = subprocess.Popen([str(self.binaries['local']), '--config', str(config),
|
||||
'--listen-port', str(port), '--verbose', *args], stdout=log, stderr=log)
|
||||
try:
|
||||
deadline = time.monotonic() + 5
|
||||
connected = False
|
||||
while time.monotonic() < deadline and proc.poll() is None:
|
||||
try:
|
||||
with socket.create_connection(('127.0.0.1', port), timeout=0.1):
|
||||
connected = True
|
||||
break
|
||||
except OSError:
|
||||
time.sleep(0.02)
|
||||
self.assertTrue(connected, 'CLI TCP mode did not override UDP-only config')
|
||||
finally:
|
||||
if proc.poll() is None:
|
||||
proc.terminate()
|
||||
proc.wait(timeout=5)
|
||||
log.seek(0)
|
||||
output = log.read()
|
||||
self.assertEqual('udprelay enabled' in output, udp, output)
|
||||
self.assertEqual('resolving hostname to IPv6 address first' in output, ipv6, output)
|
||||
|
||||
def test_numeric_option_validation(self):
|
||||
for name in self.binaries:
|
||||
for value in ('0', '-1', 'abc', '2147483648'):
|
||||
result = self.run_cli(name, '--timeout', value)
|
||||
self.assertEqual(result.returncode, 2, (name, value))
|
||||
self.assertEqual(result.stdout, '')
|
||||
self.assertIn('positive integer', result.stderr)
|
||||
if name != 'manager':
|
||||
for value in ('0', '65536', '-1', 'abc'):
|
||||
result = self.run_cli(name, '--listen-port', value)
|
||||
self.assertEqual(result.returncode, 2, (name, value))
|
||||
self.assertIn('1 to 65535', result.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--bin-dir', type=Path, default=BIN_DIR)
|
||||
args = parser.parse_args()
|
||||
BIN_DIR = args.bin_dir.resolve()
|
||||
unittest.main(argv=[__file__])
|
||||
@@ -27,7 +27,7 @@ class CliDocsTests(unittest.TestCase):
|
||||
def change(self, filename, old, new):
|
||||
path = self.root / filename
|
||||
source = path.read_text(encoding='utf-8')
|
||||
self.assertIn(old, source)
|
||||
self.assertTrue(old in source, f'Missing fixture text in {filename}')
|
||||
path.write_text(source.replace(old, new), encoding='utf-8')
|
||||
|
||||
def test_current_documentation_matches_all_variants(self):
|
||||
@@ -58,7 +58,7 @@ class CliDocsTests(unittest.TestCase):
|
||||
DOCS.check(self.root)
|
||||
|
||||
def test_nonliteral_short_options_fail_closed(self):
|
||||
self.change('src/local.c', '"f:s:p:l:k:t:m:i:c:b:a:n:huUv6A"', 'SHORT_OPTIONS')
|
||||
self.change('src/local.c', '":f:s:p:l:k:t:m:i:c:b:a:n:huUv6A"', 'SHORT_OPTIONS')
|
||||
with self.assertRaisesRegex(ValueError, 'literal getopt_long'):
|
||||
DOCS.check(self.root)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user