Redesign CLI flags, help, and argument validation

This commit is contained in:
Max Lv
2026-09-12 21:17:38 +08:00
parent 7bcb3d7dee
commit ea951e6bd1
17 changed files with 1166 additions and 244 deletions
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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