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