diff --git a/Makefile.am b/Makefile.am index 4178c504..870e8977 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS = ss -ss_SOURCES = rc4.c rc4.h md5.c md5.h encrypt.c encrypt.h local.c local.h socks5.h -AM_CFLAGS = -Wall -fno-strict-aliasing +ss_SOURCES = utils.c jconf.c json.c rc4.c rc4.h md5.c md5.h encrypt.c encrypt.h local.c local.h socks5.h +AM_CFLAGS = -O2 -Wall -fno-strict-aliasing ACLOCAL_AMFLAGS = -I m4 diff --git a/Makefile.in b/Makefile.in index 21287bea..21bfa389 100644 --- a/Makefile.in +++ b/Makefile.in @@ -55,8 +55,8 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) -am_ss_OBJECTS = rc4.$(OBJEXT) md5.$(OBJEXT) encrypt.$(OBJEXT) \ - local.$(OBJEXT) +am_ss_OBJECTS = utils.$(OBJEXT) jconf.$(OBJEXT) json.$(OBJEXT) \ + rc4.$(OBJEXT) md5.$(OBJEXT) encrypt.$(OBJEXT) local.$(OBJEXT) ss_OBJECTS = $(am_ss_OBJECTS) ss_LDADD = $(LDADD) DEFAULT_INCLUDES = -I.@am__isrc@ @@ -204,8 +204,8 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -ss_SOURCES = rc4.c rc4.h md5.c md5.h encrypt.c encrypt.h local.c local.h socks5.h -AM_CFLAGS = -Wall -fno-strict-aliasing +ss_SOURCES = utils.c jconf.c json.c rc4.c rc4.h md5.c md5.h encrypt.c encrypt.h local.c local.h socks5.h +AM_CFLAGS = -O2 -Wall -fno-strict-aliasing ACLOCAL_AMFLAGS = -I m4 all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -315,9 +315,12 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jconf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rc4.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/configure b/configure index 76ad1d92..8788be3f 100755 --- a/configure +++ b/configure @@ -11314,7 +11314,7 @@ fi # Checks for header files. -for ac_header in arpa/inet.h fcntl.h langinfo.h locale.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h unistd.h +for ac_header in stdint.h inttypes.h arpa/inet.h fcntl.h langinfo.h locale.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -11624,7 +11624,6 @@ done - ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF diff --git a/configure.ac b/configure.ac index f6e120cd..82bc2590 100755 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ AC_PROG_LIBTOOL AC_SEARCH_LIBS([ev_io_start], [ev], [ ], AC_MSG_ERROR([libev not found.])) # Checks for header files. -AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h locale.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h unistd.h]) +AC_CHECK_HEADERS([stdint.h inttypes.h arpa/inet.h fcntl.h langinfo.h locale.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h unistd.h]) AC_C_BIGENDIAN @@ -30,6 +30,5 @@ AC_CHECK_FUNCS([malloc memset socket]) AC_CONFIG_MACRO_DIR([m4]) - AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/encrypt.h b/encrypt.h index 87310b3c..6e9b0f14 100755 --- a/encrypt.h +++ b/encrypt.h @@ -1,11 +1,19 @@ #ifndef _ENCRYPT_H #define _ENCRYPT_H +#include "config.h" + #include #include #include #include +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + #include "md5.h" #include "rc4.h" @@ -36,7 +44,4 @@ void decrypt_ctx(char *buf, int len, struct rc4_state *ctx); void enc_ctx_init(struct rc4_state *ctx, int enc); void enc_conf_init(const char *pass, const char *method); -#define LOGD(...) ((void)fprintf(stdout, __VA_ARGS__)) -#define LOGE(...) ((void)fprintf(stderr, __VA_ARGS__)) - #endif // _ENCRYPT_H diff --git a/jconf.c b/jconf.c new file mode 100644 index 00000000..e783fa08 --- /dev/null +++ b/jconf.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +#include "utils.h" +#include "jconf.h" +#include "json.h" +#include "string.h" + +#define INT_DIGITS 19 /* enough for 64 bit integer */ + +static char *itoa(int i) +{ + /* Room for INT_DIGITS digits, - and '\0' */ + static char buf[INT_DIGITS + 2]; + char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */ + if (i >= 0) { + do { + *--p = '0' + (i % 10); + i /= 10; + } while (i != 0); + return p; + } + else { /* i < 0 */ + do { + *--p = '0' - (i % 10); + i /= 10; + } while (i != 0); + *--p = '-'; + } + return p; +} + +static char *to_string(const json_value *value) { + if (value->type == json_string) { + return strndup(value->u.string.ptr, value->u.string.length); + } else if (value->type == json_integer) { + return strdup(itoa(value->u.integer)); + } else if (value->type == json_null) { + return "null"; + } else { + LOGE("%d\n", value->type); + FATAL("Invalid config format.\n"); + } + return 0; +} + +static int to_int(const json_value *value) { + if (value->type == json_string) { + return atoi(value->u.string.ptr); + } else if (value->type == json_integer) { + return value->u.integer; + } else { + FATAL("Invalid config format.\n"); + } + return 0; +} + +jconf_t *read_jconf(const char* file) { + + static jconf_t conf; + + char *buf; + json_value *obj; + + FILE *f = fopen(file, "r"); + if (f == NULL) FATAL("Invalid config path.\n"); + + fseek(f, 0, SEEK_END); + long pos = ftell(f); + fseek(f, 0, SEEK_SET); + + if (pos >= MAX_CONF_SIZE) FATAL("Too large config file.\n"); + + buf = malloc(pos); + if (buf == NULL) FATAL("No enough memory.\n"); + + fread(buf, pos, 1, f); + fclose(f); + + obj = json_parse(buf); + if (obj->type == json_object) { + 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]; + conf.remote_host[j] = to_string(v); + conf.remote_num = j + 1; + } + } else if (value->type == json_string) { + conf.remote_host[0] = to_string(value); + conf.remote_num = 1; + } + } else if (strcmp(name, "server_port") == 0) { + conf.remote_port = 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, "method") == 0) { + conf.method = to_string(value); + } else if (strcmp(name, "timeout") == 0) { + conf.timeout = to_string(value); + } + } + } else { + FATAL("Invalid config file\n"); + } + + free(buf); + json_value_free(obj); + return &conf; + +} diff --git a/jconf.h b/jconf.h new file mode 100644 index 00000000..006d5988 --- /dev/null +++ b/jconf.h @@ -0,0 +1,19 @@ +#ifndef _JCONF_H +#define _JCONF_H + +#define MAX_REMOTE_NUM 10 +#define MAX_CONF_SIZE 512 * 1024 + +typedef struct { + int remote_num; + char *remote_host[MAX_REMOTE_NUM]; + char *remote_port; + char *local_port; + char *password; + char *method; + char *timeout; +} jconf_t; + +jconf_t *read_jconf(const char* file); + +#endif // _JCONF_H diff --git a/json.c b/json.c new file mode 100644 index 00000000..fd9c59f1 --- /dev/null +++ b/json.c @@ -0,0 +1,838 @@ + +/* vim: set et ts=3 sw=3 ft=c: + * + * Copyright (C) 2012 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "json.h" + +#ifdef _MSC_VER + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif +#endif + +#ifdef __cplusplus + const struct _json_value json_value_none; /* zero-d by ctor */ +#else + const struct _json_value json_value_none = { 0 }; +#endif + +#include +#include +#include +#include +#include + +typedef unsigned short json_uchar; + +static unsigned char hex_value (json_char c) +{ + if (c >= 'A' && c <= 'F') + return (c - 'A') + 10; + + if (c >= 'a' && c <= 'f') + return (c - 'a') + 10; + + if (c >= '0' && c <= '9') + return c - '0'; + + return 0xFF; +} + +typedef struct +{ + json_settings settings; + int first_pass; + + unsigned long used_memory; + + unsigned int uint_max; + unsigned long ulong_max; + +} json_state; + +static void * json_alloc (json_state * state, unsigned long size, int zero) +{ + void * mem; + + if ((state->ulong_max - state->used_memory) < size) + return 0; + + if (state->settings.max_memory + && (state->used_memory += size) > state->settings.max_memory) + { + return 0; + } + + if (! (mem = zero ? calloc (size, 1) : malloc (size))) + return 0; + + return mem; +} + +static int new_value + (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type) +{ + json_value * value; + int values_size; + + if (!state->first_pass) + { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) + *root = value; + + switch (value->type) + { + case json_array: + + if (! (value->u.array.values = (json_value **) json_alloc + (state, value->u.array.length * sizeof (json_value *), 0)) ) + { + return 0; + } + + value->u.array.length = 0; + break; + + case json_object: + + values_size = sizeof (*value->u.object.values) * value->u.object.length; + + if (! ((*(void **) &value->u.object.values) = json_alloc + (state, values_size + ((unsigned long) value->u.object.values), 0)) ) + { + return 0; + } + + value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; + + value->u.object.length = 0; + break; + + case json_string: + + if (! (value->u.string.ptr = (json_char *) json_alloc + (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) + { + return 0; + } + + value->u.string.length = 0; + break; + + default: + break; + }; + + return 1; + } + + value = (json_value *) json_alloc (state, sizeof (json_value), 1); + + if (!value) + return 0; + + if (!*root) + *root = value; + + value->type = type; + value->parent = *top; + + if (*alloc) + (*alloc)->_reserved.next_alloc = value; + + *alloc = *top = value; + + return 1; +} + +#define e_off \ + ((int) (i - cur_line_begin)) + +#define whitespace \ + case '\n': ++ cur_line; cur_line_begin = i; \ + case ' ': case '\t': case '\r' + +#define string_add(b) \ + do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); + +const static long + flag_next = 1, flag_reproc = 2, flag_need_comma = 4, flag_seek_value = 8, + flag_escaped = 16, flag_string = 32, flag_need_colon = 64, flag_done = 128, + flag_num_negative = 256, flag_num_zero = 512, flag_num_e = 1024, + flag_num_e_got_sign = 2048, flag_num_e_negative = 4096; + +json_value * json_parse_ex (json_settings * settings, const json_char * json, char * error_buf) +{ + json_char error [128]; + unsigned int cur_line; + const json_char * cur_line_begin, * i; + json_value * top, * root, * alloc = 0; + json_state state; + long flags; + long num_digits, num_fraction, num_e; + + error[0] = '\0'; + + memset (&state, 0, sizeof (json_state)); + memcpy (&state.settings, settings, sizeof (json_settings)); + + memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); + memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); + + state.uint_max -= 8; /* limit of how much can be added before next check */ + state.ulong_max -= 8; + + for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) + { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char * string; + unsigned int string_length; + + top = root = 0; + flags = flag_seek_value; + + cur_line = 1; + cur_line_begin = json; + + for (i = json ;; ++ i) + { + json_char b = *i; + + if (flags & flag_done) + { + if (!b) + break; + + switch (b) + { + whitespace: + continue; + + default: + sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b); + goto e_failed; + }; + } + + if (flags & flag_string) + { + if (!b) + { sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off); + goto e_failed; + } + + if (string_length > state.uint_max) + goto e_overflow; + + if (flags & flag_escaped) + { + flags &= ~ flag_escaped; + + switch (b) + { + case 'b': string_add ('\b'); break; + case 'f': string_add ('\f'); break; + case 'n': string_add ('\n'); break; + case 'r': string_add ('\r'); break; + case 't': string_add ('\t'); break; + case 'u': + + if ((uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF + || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF) + { + sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off); + goto e_failed; + } + + uc_b1 = uc_b1 * 16 + uc_b2; + uc_b2 = uc_b3 * 16 + uc_b4; + + uchar = ((json_char) uc_b1) * 256 + uc_b2; + + if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F)) + { + string_add ((json_char) uchar); + break; + } + + if (uchar <= 0x7FF) + { + if (state.first_pass) + string_length += 2; + else + { string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2); + string [string_length ++] = 0x80 | (uc_b2 & 0x3F); + } + + break; + } + + if (state.first_pass) + string_length += 3; + else + { string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4); + string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6); + string [string_length ++] = 0x80 | (uc_b2 & 0x3F); + } + + break; + + default: + string_add (b); + }; + + continue; + } + + if (b == '\\') + { + flags |= flag_escaped; + continue; + } + + if (b == '"') + { + if (!state.first_pass) + string [string_length] = 0; + + flags &= ~ flag_string; + string = 0; + + switch (top->type) + { + case json_string: + + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + + if (state.first_pass) + (*(json_char **) &top->u.object.values) += string_length + 1; + else + { + top->u.object.values [top->u.object.length].name + = (json_char *) top->_reserved.object_mem; + + (*(json_char **) &top->_reserved.object_mem) += string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + }; + } + else + { + string_add (b); + continue; + } + } + + if (flags & flag_seek_value) + { + switch (b) + { + whitespace: + continue; + + case ']': + + if (top->type == json_array) + flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; + else if (!state.settings.settings & json_relaxed_commas) + { sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off); + goto e_failed; + } + + break; + + default: + + if (flags & flag_need_comma) + { + if (b == ',') + { flags &= ~ flag_need_comma; + continue; + } + else + { sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b); + goto e_failed; + } + } + + if (flags & flag_need_colon) + { + if (b == ':') + { flags &= ~ flag_need_colon; + continue; + } + else + { sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b); + goto e_failed; + } + } + + flags &= ~ flag_seek_value; + + switch (b) + { + case '{': + + if (!new_value (&state, &top, &root, &alloc, json_object)) + goto e_alloc_failure; + + continue; + + case '[': + + if (!new_value (&state, &top, &root, &alloc, json_array)) + goto e_alloc_failure; + + flags |= flag_seek_value; + continue; + + case '"': + + if (!new_value (&state, &top, &root, &alloc, json_string)) + goto e_alloc_failure; + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + + if (*(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e') + goto e_unknown_value; + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + + if (*(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e') + goto e_unknown_value; + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + case 'n': + + if (*(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l') + goto e_unknown_value; + + if (!new_value (&state, &top, &root, &alloc, json_null)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + default: + + if (isdigit (b) || b == '-') + { + if (!new_value (&state, &top, &root, &alloc, json_integer)) + goto e_alloc_failure; + + if (!state.first_pass) + { + while (isdigit (b) || b == '+' || b == '-' + || b == 'e' || b == 'E' || b == '.') + { + b = *++ i; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~ (flag_num_negative | flag_num_e | + flag_num_e_got_sign | flag_num_e_negative | + flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') + { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } + else + { sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b); + goto e_failed; + } + }; + }; + } + else + { + switch (top->type) + { + case json_object: + + switch (b) + { + whitespace: + continue; + + case '"': + + if (flags & flag_need_comma && (!state.settings.settings & json_relaxed_commas)) + { + sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *) top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + + flags = (flags & ~ flag_need_comma) | flag_next; + break; + + case ',': + + if (flags & flag_need_comma) + { + flags &= ~ flag_need_comma; + break; + } + + default: + + sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b); + goto e_failed; + }; + + break; + + case json_integer: + case json_double: + + if (isdigit (b)) + { + ++ num_digits; + + if (top->type == json_integer || flags & flag_num_e) + { + if (! (flags & flag_num_e)) + { + if (flags & flag_num_zero) + { sprintf (error, "%d:%d: Unexpected `0` before `%c`", cur_line, e_off, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') + flags |= flag_num_zero; + } + else + { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + num_fraction = (num_fraction * 10) + (b - '0'); + continue; + } + + if (b == '+' || b == '-') + { + if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) + { + flags |= flag_num_e_got_sign; + + if (b == '-') + flags |= flag_num_e_negative; + + continue; + } + } + else if (b == '.' && top->type == json_integer) + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit before `.`", cur_line, e_off); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = top->u.integer; + + num_digits = 0; + continue; + } + + if (! (flags & flag_num_e)) + { + if (top->type == json_double) + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit after `.`", cur_line, e_off); + goto e_failed; + } + + top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits)); + } + + if (b == 'e' || b == 'E') + { + flags |= flag_num_e; + + if (top->type == json_integer) + { + top->type = json_double; + top->u.dbl = top->u.integer; + } + + num_digits = 0; + flags &= ~ flag_num_zero; + + continue; + } + } + else + { + if (!num_digits) + { sprintf (error, "%d:%d: Expected digit after `e`", cur_line, e_off); + goto e_failed; + } + + top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e)); + } + + if (flags & flag_num_negative) + { + if (top->type == json_integer) + top->u.integer = - top->u.integer; + else + top->u.dbl = - top->u.dbl; + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + }; + } + + if (flags & flag_reproc) + { + flags &= ~ flag_reproc; + -- i; + } + + if (flags & flag_next) + { + flags = (flags & ~ flag_next) | flag_need_comma; + + if (!top->parent) + { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) + flags |= flag_seek_value; + + if (!state.first_pass) + { + json_value * parent = top->parent; + + switch (parent->type) + { + case json_object: + + parent->u.object.values + [parent->u.object.length].value = top; + + break; + + case json_array: + + parent->u.array.values + [parent->u.array.length] = top; + + break; + + default: + break; + }; + } + + if ( (++ top->parent->u.array.length) > state.uint_max) + goto e_overflow; + + top = top->parent; + + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + + sprintf (error, "%d:%d: Unknown value", cur_line, e_off); + goto e_failed; + +e_alloc_failure: + + strcpy (error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + + sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off); + goto e_failed; + +e_failed: + + if (error_buf) + { + if (*error) + strcpy (error_buf, error); + else + strcpy (error_buf, "Unknown error"); + } + + if (state.first_pass) + alloc = root; + + while (alloc) + { + top = alloc->_reserved.next_alloc; + free (alloc); + alloc = top; + } + + if (!state.first_pass) + json_value_free (root); + + return 0; +} + +json_value * json_parse (const json_char * json) +{ + json_settings settings; + memset (&settings, 0, sizeof (json_settings)); + + return json_parse_ex (&settings, json, 0); +} + +void json_value_free (json_value * value) +{ + json_value * cur_value; + + if (!value) + return; + + value->parent = 0; + + while (value) + { + switch (value->type) + { + case json_array: + + if (!value->u.array.length) + { + free (value->u.array.values); + break; + } + + value = value->u.array.values [-- value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) + { + free (value->u.object.values); + break; + } + + value = value->u.object.values [-- value->u.object.length].value; + continue; + + case json_string: + + free (value->u.string.ptr); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + free (cur_value); + } +} + diff --git a/json.h b/json.h new file mode 100644 index 00000000..f0d10d1f --- /dev/null +++ b/json.h @@ -0,0 +1,222 @@ + +/* vim: set et ts=3 sw=3 ft=c: + * + * Copyright (C) 2012 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char + #define json_char char +#endif + +#ifdef __cplusplus + + #include + + extern "C" + { + +#endif + +typedef struct +{ + unsigned long max_memory; + int settings; + +} json_settings; + +#define json_relaxed_commas 1 + +typedef enum +{ + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null + +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_value +{ + struct _json_value * parent; + + json_type type; + + union + { + int boolean; + long integer; + double dbl; + + struct + { + unsigned int length; + json_char * ptr; /* null terminated */ + + } string; + + struct + { + unsigned int length; + + struct + { + json_char * name; + struct _json_value * value; + + } * values; + + } object; + + struct + { + unsigned int length; + struct _json_value ** values; + + } array; + + } u; + + union + { + struct _json_value * next_alloc; + void * object_mem; + + } _reserved; + + + /* Some C++ operator sugar */ + + #ifdef __cplusplus + + public: + + inline _json_value () + { memset (this, 0, sizeof (_json_value)); + } + + inline const struct _json_value &operator [] (int index) const + { + if (type != json_array || index < 0 + || ((unsigned int) index) >= u.array.length) + { + return json_value_none; + } + + return *u.array.values [index]; + } + + inline const struct _json_value &operator [] (const char * index) const + { + if (type != json_object) + return json_value_none; + + for (unsigned int i = 0; i < u.object.length; ++ i) + if (!strcmp (u.object.values [i].name, index)) + return *u.object.values [i].value; + + return json_value_none; + } + + inline operator const char * () const + { + switch (type) + { + case json_string: + return u.string.ptr; + + default: + return ""; + }; + } + + inline operator long () const + { + switch (type) + { + case json_integer: + return u.integer; + + case json_double: + return (long) u.dbl; + + default: + return 0; + }; + } + + inline operator bool () const + { + if (type != json_boolean) + return false; + + return u.boolean != 0; + } + + inline operator double () const + { + switch (type) + { + case json_integer: + return u.integer; + + case json_double: + return u.dbl; + + default: + return 0; + }; + } + + #endif + +} json_value; + +json_value * json_parse + (const json_char * json); + +json_value * json_parse_ex + (json_settings * settings, const json_char * json, char * error); + +void json_value_free (json_value *); + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif + + diff --git a/local.c b/local.c index 31d25a4c..0028b026 100755 --- a/local.c +++ b/local.c @@ -18,6 +18,7 @@ #include #include +#include "utils.h" #include "local.h" #include "socks5.h" @@ -576,70 +577,17 @@ static void accept_cb (EV_P_ ev_io *w, int revents) } } -static void print_usage() { - printf("usage: ss -s server_host -p server_port -l local_port\n"); - printf(" -k password [-m encrypt_method] [-f pid_file]\n"); - printf("\n"); - printf("options:\n"); - printf(" encrypt_method: table, rc4\n"); - printf(" pid_file: valid path to the pid file\n"); -} - -static void demonize(const char* path) { - - /* Our process ID and Session ID */ - pid_t pid, sid; - - /* Fork off the parent process */ - pid = fork(); - if (pid < 0) { - exit(EXIT_FAILURE); - } - - /* If we got a good PID, then - we can exit the parent process. */ - if (pid > 0) { - FILE *file = fopen(path, "w"); - fprintf(file, "%d", pid); - fclose(file); - exit(EXIT_SUCCESS); - } - - /* Change the file mode mask */ - umask(0); - - /* Open any logs here */ - - /* Create a new SID for the child process */ - sid = setsid(); - if (sid < 0) { - /* Log the failure */ - exit(EXIT_FAILURE); - } - - /* Change the current working directory */ - if ((chdir("/")) < 0) { - /* Log the failure */ - exit(EXIT_FAILURE); - } - - /* Close out the standard file descriptors */ - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - -} - int main (int argc, char **argv) { - char *port = NULL; + int i, c; + int pid_flags = 0; + char *local_port = NULL; char *password = NULL; - char *timeout = "10"; + char *timeout = NULL; char *method = NULL; - int c; - int f_flags = 0; - char *f_path = NULL; + char *pid_path = NULL; + char *conf_path = NULL; int remote_num = 0; char *remote_host[MAX_REMOTE_NUM]; @@ -647,7 +595,7 @@ int main (int argc, char **argv) opterr = 0; - while ((c = getopt (argc, argv, "f:s:p:l:k:t:m:")) != -1) { + while ((c = getopt (argc, argv, "f:s:p:l:k:t:m:c:")) != -1) { switch (c) { case 's': remote_host[remote_num++] = optarg; @@ -656,14 +604,14 @@ int main (int argc, char **argv) remote_port = optarg; break; case 'l': - port = optarg; + local_port = optarg; break; case 'k': password = optarg; break; case 'f': - f_flags = 1; - f_path = optarg; + pid_flags = 1; + pid_path = optarg; break; case 't': timeout = optarg; @@ -671,45 +619,62 @@ int main (int argc, char **argv) case 'm': method = optarg; break; + case 'c': + conf_path = optarg; + break; } } - if (remote_num == 0 || remote_port == NULL || - port == NULL || password == NULL) { - print_usage(); + if (opterr) { + usage(); exit(EXIT_FAILURE); } - if (f_flags) { - - if (f_path == NULL) { - print_usage(); - exit(EXIT_FAILURE); + if (conf_path != NULL) { + jconf_t *conf = read_jconf(conf_path); + if (remote_num == 0) { + remote_num = conf->remote_num; + for (i = 0; i < remote_num; i++) { + remote_host[i] = conf->remote_host[i]; + } } + if (remote_port == NULL) remote_port = conf->remote_port; + if (local_port == NULL) local_port = conf->local_port; + if (password == NULL) password = conf->password; + if (method == NULL) method = conf->method; + if (timeout == NULL) timeout = conf->timeout; + } + + if (remote_num == 0 || remote_port == NULL || + local_port == NULL || password == NULL) { + usage(); + exit(EXIT_FAILURE); + } - demonize(f_path); + if (timeout == NULL) timeout = "10"; + if (pid_flags) { + demonize(pid_path); } + // ignore SIGPIPE signal(SIGPIPE, SIG_IGN); // Setup keys - LOGD("calculating ciphers\n"); + LOGD("calculating ciphers...\n"); enc_conf_init(password, method); // Setup socket int listenfd; - listenfd = create_and_bind(port); + listenfd = create_and_bind(local_port); if (listenfd < 0) { - LOGE("bind() error..\n"); - return 1; + FATAL("bind() error..\n"); } if (listen(listenfd, SOMAXCONN) == -1) { - LOGE("listen() error.\n"); - return 1; + FATAL("listen() error.\n"); } setnonblocking(listenfd); - LOGD("server listening at port %s\n", port); + LOGD("server listening at port %s.\n", local_port); // Setup proxy context struct listen_ctx listen_ctx; @@ -725,7 +690,7 @@ int main (int argc, char **argv) struct ev_loop *loop = ev_default_loop(0); if (!loop) { - return 1; + FATAL("ev_loop error.\n"); } ev_io_init (&listen_ctx.io, accept_cb, listenfd, EV_READ); ev_io_start (loop, &listen_ctx.io); diff --git a/local.h b/local.h index 305b6e77..1d445013 100644 --- a/local.h +++ b/local.h @@ -3,8 +3,7 @@ #include #include "encrypt.h" - -#define MAX_REMOTE_NUM 10 +#include "jconf.h" struct listen_ctx { ev_io io; diff --git a/utils.c b/utils.c new file mode 100644 index 00000000..f591a1a0 --- /dev/null +++ b/utils.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include "utils.h" + +void FATAL(const char *msg) { + fprintf(stderr, "%s", msg); + exit(-1); +} + +void usage() { + printf("usage: ss -s server_host -p server_port -l local_port\n"); + printf(" -k password [-m encrypt_method] [-f pid_file]\n"); + printf("\n"); + printf("options:\n"); + printf(" encrypt_method: table, rc4\n"); + printf(" pid_file: valid path to the pid file\n"); +} + +void demonize(const char* path) { + + /* Our process ID and Session ID */ + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + /* If we got a good PID, then + we can exit the parent process. */ + if (pid > 0) { + FILE *file = fopen(path, "w"); + if (file == NULL) FATAL("Invalid pid file\n"); + + fprintf(file, "%d", pid); + fclose(file); + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask(0); + + /* Open any logs here */ + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + +} + diff --git a/utils.h b/utils.h new file mode 100644 index 00000000..1b68962c --- /dev/null +++ b/utils.h @@ -0,0 +1,11 @@ +#ifndef _UTILS_H +#define _UTILS_H + +#define LOGD(...) ((void)fprintf(stdout, __VA_ARGS__)) +#define LOGE(...) ((void)fprintf(stderr, __VA_ARGS__)) + +void FATAL(const char *msg); +void usage(void); +void demonize(const char* path); + +#endif // _UTILS_H