From 615588bd3992a396cf88691a07cd387c0b7f51e6 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Fri, 9 Aug 2019 15:17:53 +0800 Subject: [PATCH] Bump version --- CMakeLists.txt | 4 +- Changes | 8 + README.md | 2 +- config.json | 12 ++ configure.ac | 2 +- debian/changelog | 8 + jconf.c | 351 ++++++++++++++++++++++++++++++++++++++++++++ snap/snapcraft.yaml | 4 +- test.json | 21 +++ 9 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 config.json create mode 100644 jconf.c create mode 100644 test.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ec1586c6..9cca10d61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.2) set(PROJECT_NAME shadowsocks-libev) -set(RELEASE_DATE 2019-06-08) -set(PROJECT_VERSION "3.3.0") +set(RELEASE_DATE 2019-08-09) +set(PROJECT_VERSION "3.3.1") set(PROJECT_DESC "a lightweight secured socks5 proxy") set(PROJECT_URL "https://shadowsocks.org") set(PROJECT_ISSUES_URL "https://github.com/shadowsocks/shadowsocks-libev") diff --git a/Changes b/Changes index 14df61bbb..b6eb6192f 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,11 @@ +shadowsocks-libev (3.3.1-1) unstable; urgency=high + + * Fix a high CPU bug introduced in 3.3.0. (#2449) + * Fix MinGW build. (#2438) + * Minor bug fixes. (#2402, #2412, #2427, #2443) + + -- Max Lv Fri, 09 Aug 2019 15:02:34 +0800 + shadowsocks-libev (3.3.0-1) unstable; urgency=medium * Enlarge the socket buffer size to 16KB. diff --git a/README.md b/README.md index 85616005f..9e9ecc2a9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It is a port of [Shadowsocks](https://github.com/shadowsocks/shadowsocks) created by [@clowwindy](https://github.com/clowwindy), and maintained by [@madeye](https://github.com/madeye) and [@linusyang](https://github.com/linusyang). -Current version: 3.3.0 | [Changelog](debian/changelog) +Current version: 3.3.1 | [Changelog](debian/changelog) ## Features diff --git a/config.json b/config.json new file mode 100644 index 000000000..b30ad9298 --- /dev/null +++ b/config.json @@ -0,0 +1,12 @@ +{ + "server":"la.maxlv.net", + "server_port":1080, + "local_address":"0.0.0.0", + "local_port":1080, + "password":"CCCCCCCC", + "method":"aes-256-gcm", + "timeout":60, + "mode":"tcp_and_udp", + "plugin":"obfs-local", + "plugin_opts":"obfs=http;obfs-host=www.CCCCCCC.com" +} diff --git a/configure.ac b/configure.ac index 75e56b014..81c3c97e5 100755 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl -*- Autoconf -*- dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.67]) -AC_INIT([shadowsocks-libev], [3.3.0], [max.c.lv@gmail.com]) +AC_INIT([shadowsocks-libev], [3.3.1], [max.c.lv@gmail.com]) AC_CONFIG_SRCDIR([src/crypto.c]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR(auto) diff --git a/debian/changelog b/debian/changelog index 14df61bbb..b6eb6192f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +shadowsocks-libev (3.3.1-1) unstable; urgency=high + + * Fix a high CPU bug introduced in 3.3.0. (#2449) + * Fix MinGW build. (#2438) + * Minor bug fixes. (#2402, #2412, #2427, #2443) + + -- Max Lv Fri, 09 Aug 2019 15:02:34 +0800 + shadowsocks-libev (3.3.0-1) unstable; urgency=medium * Enlarge the socket buffer size to 16KB. diff --git a/jconf.c b/jconf.c new file mode 100644 index 000000000..bf36a5786 --- /dev/null +++ b/jconf.c @@ -0,0 +1,351 @@ +/* + * jconf.c - Parse the JSON format config file + * + * Copyright (C) 2013 - 2018, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include +#include +#include +#include + +#include "netutils.h" +#include "utils.h" +#include "jconf.h" +#include "json.h" +#include "string.h" + +#include + +#define check_json_value_type(value, expected_type, message) \ + do { \ + if ((value)->type != (expected_type)) \ + FATAL((message)); \ + } while (0) + +static char * +to_string(const json_value *value) +{ + if (value->type == json_string) { + return ss_strndup(value->u.string.ptr, value->u.string.length); + } else if (value->type == json_integer) { + return strdup(ss_itoa(value->u.integer)); + } else if (value->type == json_null) { + return NULL; + } else { + LOGE("%d", value->type); + FATAL("Invalid config format."); + } + return 0; +} + +void +free_addr(ss_addr_t *addr) +{ + ss_free(addr->host); + ss_free(addr->port); +} + +void +parse_addr(const char *str_in, ss_addr_t *addr) +{ + if (str_in == NULL) + return; + + int ipv6 = 0, ret = -1, n = 0, len; + char *pch; + char *str = strdup(str_in); + len = strlen(str_in); + + struct cork_ip ip; + if (cork_ip_init(&ip, str) != -1) { + addr->host = str; + addr->port = NULL; + return; + } + + pch = strchr(str, ':'); + while (pch != NULL) { + n++; + ret = pch - str; + pch = strchr(pch + 1, ':'); + } + + if (n > 1) { + ipv6 = 1; + if (str[ret - 1] != ']') { + ret = -1; + } + } + + if (ret == -1) { + if (ipv6) { + addr->host = ss_strndup(str + 1, strlen(str) - 2); + } else { + addr->host = strdup(str); + } + addr->port = NULL; + } else { + if (ipv6) { + addr->host = ss_strndup(str + 1, ret - 2); + } else { + addr->host = ss_strndup(str, ret); + } + if (ret < len - 1) + { + addr->port = strdup(str + ret + 1); + } else { + addr->port = NULL; + } + } + + free(str); +} + +static int +parse_dscp(char *str) +{ + size_t str_len = strlen(str); + + // Pre-defined values (EF, CSx, AFxy) + if (str_len == 2 && strcasecmp(str, "EF") == 0) { + return DSCP_EF; + } + + if (str_len == DSCP_CS_LEN && strncasecmp(str, "CS", 2) == 0) { + if (str[2] >= '0' && str[2] <= '7') { + // CSx = 8x + return (str[2] - '0') << 3; + } + } + + if (str_len == DSCP_AF_LEN && strncasecmp(str, "AF", 2) == 0) { + if (str[2] >= '1' && str[2] <= '4' && str[3] >= '1' && str[3] <= '3') { + // AFxy = 8x + 2y + return ((str[2] - '0') << 3) | ((str[3] - '0') << 1); + } + } + + // Manual hexadecimal mode (0xYZ) + char *endptr; + int dscp = (int)strtol(str, &endptr, 0); + if (*endptr == '\0' && dscp >= DSCP_MIN && dscp <= DSCP_MAX) { + return dscp; + } + + LOGE("Invalid DSCP value (%s)", str); + return DSCP_DEFAULT; +} + +jconf_t * +read_jconf(const char *file) +{ + static jconf_t conf; + + memset(&conf, 0, sizeof(jconf_t)); + + char *buf; + json_value *obj; + + FILE *f = fopen(file, "rb"); + if (f == NULL) { + FATAL("Invalid config path."); + } + + fseek(f, 0, SEEK_END); + long pos = ftell(f); + fseek(f, 0, SEEK_SET); + + if (pos < 0) { + FATAL("Invalid config path."); + } + + if (pos >= MAX_CONF_SIZE) { + FATAL("Too large config file."); + } + + buf = ss_malloc(pos + 1); + if (buf == NULL) { + FATAL("No enough memory."); + } + + int nread = fread(buf, pos, 1, f); + if (!nread) { + FATAL("Failed to read the config file."); + } + fclose(f); + + buf[pos] = '\0'; // end of string + + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + char error_buf[512]; + obj = json_parse_ex(&settings, buf, pos, error_buf); + + if (obj == NULL) { + FATAL(error_buf); + } + + if (obj->type == json_object) { + unsigned int i, j; + for (i = 0; i < obj->u.object.length; i++) { + char *name = obj->u.object.values[i].name; + json_value *value = obj->u.object.values[i].value; + if (strcmp(name, "server") == 0) { + if (value->type == json_array) { + for (j = 0; j < value->u.array.length; j++) { + if (j >= MAX_REMOTE_NUM) { + break; + } + json_value *v = value->u.array.values[j]; + char *addr_str = to_string(v); + parse_addr(addr_str, conf.remote_addr + j); + ss_free(addr_str); + conf.remote_num = j + 1; + } + } else if (value->type == json_string) { + parse_addr(to_string(value), conf.remote_addr); + conf.remote_num = 1; + } + } else if (strcmp(name, "port_password") == 0) { + if (value->type == json_object) { + for (j = 0; j < value->u.object.length; j++) { + if (j >= MAX_PORT_NUM) { + break; + } + json_value *v = value->u.object.values[j].value; + if (v->type == json_string) { + conf.port_password[j].port = ss_strndup(value->u.object.values[j].name, + value->u.object.values[j].name_length); + conf.port_password[j].password = to_string(v); + conf.port_password_num = j + 1; + } + } + } + } else if (strcmp(name, "server_port") == 0) { + conf.remote_port = to_string(value); + } else if (strcmp(name, "local_address") == 0) { + conf.local_addr = to_string(value); + } else if (strcmp(name, "local_port") == 0) { + conf.local_port = to_string(value); + } else if (strcmp(name, "password") == 0) { + conf.password = to_string(value); + } else if (strcmp(name, "key") == 0) { + conf.key = to_string(value); + } else if (strcmp(name, "method") == 0) { + conf.method = to_string(value); + } else if (strcmp(name, "timeout") == 0) { + conf.timeout = to_string(value); + } else if (strcmp(name, "user") == 0) { + conf.user = to_string(value); + } else if (strcmp(name, "plugin") == 0) { + conf.plugin = to_string(value); + if (conf.plugin && strlen(conf.plugin) == 0) { + ss_free(conf.plugin); + conf.plugin = NULL; + } + } else if (strcmp(name, "plugin_opts") == 0) { + conf.plugin_opts = to_string(value); + } else if (strcmp(name, "fast_open") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'fast_open' must be a boolean"); + conf.fast_open = value->u.boolean; + } else if (strcmp(name, "reuse_port") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'reuse_port' must be a boolean"); + conf.reuse_port = value->u.boolean; + } else if (strcmp(name, "auth") == 0) { + FATAL("One time auth has been deprecated. Try AEAD ciphers instead."); + } else if (strcmp(name, "nofile") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'nofile' must be an integer"); + conf.nofile = value->u.integer; + } else if (strcmp(name, "nameserver") == 0) { + conf.nameserver = to_string(value); + } else if (strcmp(name, "dscp") == 0) { + if (value->type == json_object) { + for (j = 0; j < value->u.object.length; j++) { + if (j >= MAX_DSCP_NUM) { + break; + } + json_value *v = value->u.object.values[j].value; + if (v->type == json_string) { + int dscp = parse_dscp(to_string(v)); + char *port = ss_strndup(value->u.object.values[j].name, + value->u.object.values[j].name_length); + conf.dscp[j].port = port; + conf.dscp[j].dscp = dscp; + conf.dscp_num = j + 1; + } + } + } + } else if (strcmp(name, "tunnel_address") == 0) { + conf.tunnel_address = to_string(value); + } else if (strcmp(name, "mode") == 0) { + char *mode_str = to_string(value); + + if (mode_str == NULL) + conf.mode = TCP_ONLY; + else if (strcmp(mode_str, "tcp_only") == 0) + conf.mode = TCP_ONLY; + else if (strcmp(mode_str, "tcp_and_udp") == 0) + conf.mode = TCP_AND_UDP; + else if (strcmp(mode_str, "udp_only") == 0) + conf.mode = UDP_ONLY; + else + LOGI("ignore unknown mode: %s, use tcp_only as fallback", + mode_str); + + ss_free(mode_str); + } else if (strcmp(name, "mtu") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'mtu' must be an integer"); + conf.mtu = value->u.integer; + } else if (strcmp(name, "mptcp") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'mptcp' must be a boolean"); + conf.mptcp = value->u.boolean; + } else if (strcmp(name, "ipv6_first") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; +#ifdef HAS_SYSLOG + } else if (strcmp(name, "use_syslog") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'use_syslog' must be a boolean"); + use_syslog = value->u.boolean; +#endif + } else if (strcmp(name, "no_delay") == 0) { + check_json_value_type( + value, json_boolean, + "invalid config file: option 'no_delay' must be a boolean"); + conf.no_delay = value->u.boolean; + } else if (strcmp(name, "workdir") == 0) { + conf.workdir = to_string(value); + } else if (strcmp(name, "acl") == 0) { + conf.acl = to_string(value); + } + } + } else { + FATAL("Invalid config file"); + } + + ss_free(buf); + json_value_free(obj); + return &conf; +} diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fdc3b4735..5b841eb7d 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ base: core18 name: shadowsocks-libev -version: 3.3.0-1 +version: 3.3.1-1 summary: libev port of shadowsocks description: | Shadowsocks-libev is a lightweight and secure SOCKS5 proxy for embedded @@ -40,7 +40,7 @@ passthrough: parts: shadowsocks-libev: plugin: autotools - source: https://github.com/shadowsocks/shadowsocks-libev/releases/download/v3.3.0/shadowsocks-libev-3.3.0.tar.gz + source: https://github.com/shadowsocks/shadowsocks-libev/releases/download/v3.3.1/shadowsocks-libev-3.3.1.tar.gz build-packages: - libpcre3-dev - asciidoc diff --git a/test.json b/test.json new file mode 100644 index 000000000..e21621e8f --- /dev/null +++ b/test.json @@ -0,0 +1,21 @@ +{ + "server":"0.0.0.0", + "port_password": { + "13800": "123456", + "13801": "123456", + "13802": "123456", + "13803": "123456", + "13804": "123456", + "13805": "123456", + "13806": "123456", + "13807": "123456", + "13808": "123456", + "13809": "123456", + "13810": "123456" + }, + "timeout": 300, + "method": "aes-192-gcm", + "mode":"tcp_and_udp", + "fast_open":true, + "reuse_port": true +}