1 /*
2 * jconf.c - Parse the JSON format config file
3 *
4 * Copyright (C) 2013 - 2019, Max Lv <max.c.lv@gmail.com>
5 *
6 * This file is part of the shadowsocks-libev.
7 * shadowsocks-libev is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * shadowsocks-libev is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with shadowsocks-libev; see the file COPYING. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include "netutils.h"
29 #include "utils.h"
30 #include "jconf.h"
31 #include "json.h"
32 #include "string.h"
33
34 #include <libcork/core.h>
35
36 #define check_json_value_type(value, expected_type, message) \
37 do { \
38 if ((value)->type != (expected_type)) \
39 FATAL((message)); \
40 } while (0)
41
42 static char *
to_string(const json_value * value)43 to_string(const json_value *value)
44 {
45 if (value->type == json_string) {
46 return ss_strndup(value->u.string.ptr, value->u.string.length);
47 } else if (value->type == json_integer) {
48 return strdup(ss_itoa(value->u.integer));
49 } else if (value->type == json_null) {
50 return NULL;
51 } else {
52 LOGE("%d", value->type);
53 FATAL("Invalid config format.");
54 }
55 return 0;
56 }
57
58 void
free_addr(ss_addr_t * addr)59 free_addr(ss_addr_t *addr)
60 {
61 ss_free(addr->host);
62 ss_free(addr->port);
63 }
64
65 void
parse_addr(const char * str_in,ss_addr_t * addr)66 parse_addr(const char *str_in, ss_addr_t *addr)
67 {
68 if (str_in == NULL)
69 return;
70
71 int ipv6 = 0, ret = -1, n = 0, len;
72 char *pch;
73 char *str = strdup(str_in);
74 len = strlen(str_in);
75
76 struct cork_ip ip;
77 if (cork_ip_init(&ip, str) != -1) {
78 addr->host = str;
79 addr->port = NULL;
80 return;
81 }
82
83 pch = strchr(str, ':');
84 while (pch != NULL) {
85 n++;
86 ret = pch - str;
87 pch = strchr(pch + 1, ':');
88 }
89
90 if (n > 1) {
91 ipv6 = 1;
92 if (str[ret - 1] != ']') {
93 ret = -1;
94 }
95 }
96
97 if (ret == -1) {
98 if (ipv6) {
99 addr->host = ss_strndup(str + 1, strlen(str) - 2);
100 } else {
101 addr->host = strdup(str);
102 }
103 addr->port = NULL;
104 } else {
105 if (ipv6) {
106 addr->host = ss_strndup(str + 1, ret - 2);
107 } else {
108 addr->host = ss_strndup(str, ret);
109 }
110 if (ret < len - 1) {
111 addr->port = strdup(str + ret + 1);
112 } else {
113 addr->port = NULL;
114 }
115 }
116
117 free(str);
118 }
119
120 static int
parse_dscp(char * str)121 parse_dscp(char *str)
122 {
123 size_t str_len = strlen(str);
124
125 // Pre-defined values (EF, CSx, AFxy)
126 if (str_len == 2 && strcasecmp(str, "EF") == 0) {
127 return DSCP_EF;
128 }
129
130 if (str_len == DSCP_CS_LEN && strncasecmp(str, "CS", 2) == 0) {
131 if (str[2] >= '0' && str[2] <= '7') {
132 // CSx = 8x
133 return (str[2] - '0') << 3;
134 }
135 }
136
137 if (str_len == DSCP_AF_LEN && strncasecmp(str, "AF", 2) == 0) {
138 if (str[2] >= '1' && str[2] <= '4' && str[3] >= '1' && str[3] <= '3') {
139 // AFxy = 8x + 2y
140 return ((str[2] - '0') << 3) | ((str[3] - '0') << 1);
141 }
142 }
143
144 // Manual hexadecimal mode (0xYZ)
145 char *endptr;
146 int dscp = (int)strtol(str, &endptr, 0);
147 if (*endptr == '\0' && dscp >= DSCP_MIN && dscp <= DSCP_MAX) {
148 return dscp;
149 }
150
151 LOGE("Invalid DSCP value (%s)", str);
152 return DSCP_DEFAULT;
153 }
154
155 jconf_t *
read_jconf(const char * file)156 read_jconf(const char *file)
157 {
158 static jconf_t conf;
159
160 memset(&conf, 0, sizeof(jconf_t));
161
162 char *buf;
163 json_value *obj;
164
165 FILE *f = fopen(file, "rb");
166 if (f == NULL) {
167 FATAL("Invalid config path.");
168 }
169
170 fseek(f, 0, SEEK_END);
171 long pos = ftell(f);
172 fseek(f, 0, SEEK_SET);
173
174 if (pos < 0) {
175 FATAL("Invalid config path.");
176 }
177
178 if (pos >= MAX_CONF_SIZE) {
179 FATAL("Too large config file.");
180 }
181
182 buf = ss_malloc(pos + 1);
183 if (buf == NULL) {
184 FATAL("No enough memory.");
185 }
186
187 int nread = fread(buf, pos, 1, f);
188 if (!nread) {
189 FATAL("Failed to read the config file.");
190 }
191 fclose(f);
192
193 buf[pos] = '\0'; // end of string
194
195 json_settings settings = { 0UL, 0, NULL, NULL, NULL };
196 char error_buf[512];
197 obj = json_parse_ex(&settings, buf, pos, error_buf);
198
199 if (obj == NULL) {
200 FATAL(error_buf);
201 }
202
203 if (obj->type == json_object) {
204 unsigned int i, j;
205 for (i = 0; i < obj->u.object.length; i++) {
206 char *name = obj->u.object.values[i].name;
207 json_value *value = obj->u.object.values[i].value;
208 if (strcmp(name, "server") == 0) {
209 if (value->type == json_array) {
210 for (j = 0; j < value->u.array.length; j++) {
211 if (j >= MAX_REMOTE_NUM) {
212 break;
213 }
214 json_value *v = value->u.array.values[j];
215 char *addr_str = to_string(v);
216 parse_addr(addr_str, conf.remote_addr + j);
217 ss_free(addr_str);
218 conf.remote_num = j + 1;
219 }
220 } else if (value->type == json_string) {
221 char* tmp_str = to_string(value);
222 parse_addr(tmp_str, conf.remote_addr);
223 ss_free(tmp_str);
224 conf.remote_num = 1;
225 }
226 } else if (strcmp(name, "port_password") == 0) {
227 if (value->type == json_object) {
228 for (j = 0; j < value->u.object.length; j++) {
229 if (j >= MAX_PORT_NUM) {
230 break;
231 }
232 json_value *v = value->u.object.values[j].value;
233 if (v->type == json_string) {
234 conf.port_password[j].port = ss_strndup(value->u.object.values[j].name,
235 value->u.object.values[j].name_length);
236 conf.port_password[j].password = to_string(v);
237 conf.port_password_num = j + 1;
238 }
239 }
240 }
241 } else if (strcmp(name, "server_port") == 0) {
242 conf.remote_port = to_string(value);
243 } else if (strcmp(name, "local_address") == 0) {
244 conf.local_addr = to_string(value);
245 } else if (strcmp(name, "local_ipv4_address") == 0) {
246 conf.local_addr_v4 = to_string(value);
247 } else if (strcmp(name, "local_ipv6_address") == 0) {
248 conf.local_addr_v6 = to_string(value);
249 } else if (strcmp(name, "local_port") == 0) {
250 conf.local_port = to_string(value);
251 } else if (strcmp(name, "password") == 0) {
252 conf.password = to_string(value);
253 } else if (strcmp(name, "key") == 0) {
254 conf.key = to_string(value);
255 } else if (strcmp(name, "method") == 0) {
256 conf.method = to_string(value);
257 } else if (strcmp(name, "timeout") == 0) {
258 conf.timeout = to_string(value);
259 } else if (strcmp(name, "user") == 0) {
260 conf.user = to_string(value);
261 } else if (strcmp(name, "plugin") == 0) {
262 conf.plugin = to_string(value);
263 if (conf.plugin && strlen(conf.plugin) == 0) {
264 ss_free(conf.plugin);
265 conf.plugin = NULL;
266 }
267 } else if (strcmp(name, "plugin_opts") == 0) {
268 conf.plugin_opts = to_string(value);
269 } else if (strcmp(name, "fast_open") == 0) {
270 check_json_value_type(value, json_boolean,
271 "invalid config file: option 'fast_open' must be a boolean");
272 conf.fast_open = value->u.boolean;
273 } else if (strcmp(name, "reuse_port") == 0) {
274 check_json_value_type(value, json_boolean,
275 "invalid config file: option 'reuse_port' must be a boolean");
276 conf.reuse_port = value->u.boolean;
277 } else if (strcmp(name, "auth") == 0) {
278 FATAL("One time auth has been deprecated. Try AEAD ciphers instead.");
279 } else if (strcmp(name, "nofile") == 0) {
280 check_json_value_type(value, json_integer,
281 "invalid config file: option 'nofile' must be an integer");
282 conf.nofile = value->u.integer;
283 } else if (strcmp(name, "nameserver") == 0) {
284 conf.nameserver = to_string(value);
285 } else if (strcmp(name, "dscp") == 0) {
286 if (value->type == json_object) {
287 for (j = 0; j < value->u.object.length; j++) {
288 if (j >= MAX_DSCP_NUM) {
289 break;
290 }
291 json_value *v = value->u.object.values[j].value;
292 if (v->type == json_string) {
293 int dscp = parse_dscp(to_string(v));
294 char *port = ss_strndup(value->u.object.values[j].name,
295 value->u.object.values[j].name_length);
296 conf.dscp[j].port = port;
297 conf.dscp[j].dscp = dscp;
298 conf.dscp_num = j + 1;
299 }
300 }
301 }
302 } else if (strcmp(name, "tunnel_address") == 0) {
303 conf.tunnel_address = to_string(value);
304 } else if (strcmp(name, "mode") == 0) {
305 char *mode_str = to_string(value);
306
307 if (mode_str == NULL)
308 conf.mode = TCP_ONLY;
309 else if (strcmp(mode_str, "tcp_only") == 0)
310 conf.mode = TCP_ONLY;
311 else if (strcmp(mode_str, "tcp_and_udp") == 0)
312 conf.mode = TCP_AND_UDP;
313 else if (strcmp(mode_str, "udp_only") == 0)
314 conf.mode = UDP_ONLY;
315 else
316 LOGI("ignore unknown mode: %s, use tcp_only as fallback",
317 mode_str);
318
319 ss_free(mode_str);
320 } else if (strcmp(name, "mtu") == 0) {
321 check_json_value_type(value, json_integer,
322 "invalid config file: option 'mtu' must be an integer");
323 conf.mtu = value->u.integer;
324 } else if (strcmp(name, "mptcp") == 0) {
325 check_json_value_type(value, json_boolean,
326 "invalid config file: option 'mptcp' must be a boolean");
327 conf.mptcp = value->u.boolean;
328 } else if (strcmp(name, "ipv6_first") == 0) {
329 check_json_value_type(value, json_boolean,
330 "invalid config file: option 'ipv6_first' must be a boolean");
331 conf.ipv6_first = value->u.boolean;
332 #ifdef HAS_SYSLOG
333 } else if (strcmp(name, "use_syslog") == 0) {
334 check_json_value_type(value, json_boolean,
335 "invalid config file: option 'use_syslog' must be a boolean");
336 use_syslog = value->u.boolean;
337 #endif
338 } else if (strcmp(name, "no_delay") == 0) {
339 check_json_value_type(
340 value, json_boolean,
341 "invalid config file: option 'no_delay' must be a boolean");
342 conf.no_delay = value->u.boolean;
343 } else if (strcmp(name, "tcp_tproxy") == 0) {
344 check_json_value_type(
345 value, json_boolean,
346 "invalid config file: option 'tcp_tproxy' must be a boolean");
347 conf.tcp_tproxy = value->u.boolean;
348 } else if (strcmp(name, "workdir") == 0) {
349 conf.workdir = to_string(value);
350 } else if (strcmp(name, "acl") == 0) {
351 conf.acl = to_string(value);
352 } else if (strcmp(name, "manager_address") == 0) {
353 conf.manager_address = to_string(value);
354 }
355 }
356 } else {
357 FATAL("Invalid config file");
358 }
359
360 ss_free(buf);
361 json_value_free(obj);
362 return &conf;
363 }
364