dnomd343
3 years ago
commit
e3c4f9f95f
10 changed files with 4198 additions and 0 deletions
File diff suppressed because it is too large
@ -0,0 +1,293 @@ |
|||||
|
/*
|
||||
|
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#ifndef cJSON__h |
||||
|
#define cJSON__h |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" |
||||
|
{ |
||||
|
#endif |
||||
|
|
||||
|
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) |
||||
|
#define __WINDOWS__ |
||||
|
#endif |
||||
|
|
||||
|
#ifdef __WINDOWS__ |
||||
|
|
||||
|
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||
|
|
||||
|
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols |
||||
|
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) |
||||
|
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol |
||||
|
|
||||
|
For *nix builds that support visibility attribute, you can define similar behavior by |
||||
|
|
||||
|
setting default visibility to hidden by adding |
||||
|
-fvisibility=hidden (for gcc) |
||||
|
or |
||||
|
-xldscope=hidden (for sun cc) |
||||
|
to CFLAGS |
||||
|
|
||||
|
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
#define CJSON_CDECL __cdecl |
||||
|
#define CJSON_STDCALL __stdcall |
||||
|
|
||||
|
/* export symbols by default, this is necessary for copy pasting the C and header file */ |
||||
|
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) |
||||
|
#define CJSON_EXPORT_SYMBOLS |
||||
|
#endif |
||||
|
|
||||
|
#if defined(CJSON_HIDE_SYMBOLS) |
||||
|
#define CJSON_PUBLIC(type) type CJSON_STDCALL |
||||
|
#elif defined(CJSON_EXPORT_SYMBOLS) |
||||
|
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL |
||||
|
#elif defined(CJSON_IMPORT_SYMBOLS) |
||||
|
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL |
||||
|
#endif |
||||
|
#else /* !__WINDOWS__ */ |
||||
|
#define CJSON_CDECL |
||||
|
#define CJSON_STDCALL |
||||
|
|
||||
|
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) |
||||
|
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type |
||||
|
#else |
||||
|
#define CJSON_PUBLIC(type) type |
||||
|
#endif |
||||
|
#endif |
||||
|
|
||||
|
/* project version */ |
||||
|
#define CJSON_VERSION_MAJOR 1 |
||||
|
#define CJSON_VERSION_MINOR 7 |
||||
|
#define CJSON_VERSION_PATCH 15 |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
|
||||
|
/* cJSON Types: */ |
||||
|
#define cJSON_Invalid (0) |
||||
|
#define cJSON_False (1 << 0) |
||||
|
#define cJSON_True (1 << 1) |
||||
|
#define cJSON_NULL (1 << 2) |
||||
|
#define cJSON_Number (1 << 3) |
||||
|
#define cJSON_String (1 << 4) |
||||
|
#define cJSON_Array (1 << 5) |
||||
|
#define cJSON_Object (1 << 6) |
||||
|
#define cJSON_Raw (1 << 7) /* raw json */ |
||||
|
|
||||
|
#define cJSON_IsReference 256 |
||||
|
#define cJSON_StringIsConst 512 |
||||
|
|
||||
|
/* The cJSON structure: */ |
||||
|
typedef struct cJSON |
||||
|
{ |
||||
|
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ |
||||
|
struct cJSON *next; |
||||
|
struct cJSON *prev; |
||||
|
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ |
||||
|
struct cJSON *child; |
||||
|
|
||||
|
/* The type of the item, as above. */ |
||||
|
int type; |
||||
|
|
||||
|
/* The item's string, if type==cJSON_String and type == cJSON_Raw */ |
||||
|
char *valuestring; |
||||
|
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ |
||||
|
int valueint; |
||||
|
/* The item's number, if type==cJSON_Number */ |
||||
|
double valuedouble; |
||||
|
|
||||
|
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ |
||||
|
char *string; |
||||
|
} cJSON; |
||||
|
|
||||
|
typedef struct cJSON_Hooks |
||||
|
{ |
||||
|
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ |
||||
|
void *(CJSON_CDECL *malloc_fn)(size_t sz); |
||||
|
void (CJSON_CDECL *free_fn)(void *ptr); |
||||
|
} cJSON_Hooks; |
||||
|
|
||||
|
typedef int cJSON_bool; |
||||
|
|
||||
|
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
|
* This is to prevent stack overflows. */ |
||||
|
#ifndef CJSON_NESTING_LIMIT |
||||
|
#define CJSON_NESTING_LIMIT 1000 |
||||
|
#endif |
||||
|
|
||||
|
/* returns the version of cJSON as a string */ |
||||
|
CJSON_PUBLIC(const char*) cJSON_Version(void); |
||||
|
|
||||
|
/* Supply malloc, realloc and free functions to cJSON */ |
||||
|
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); |
||||
|
|
||||
|
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ |
||||
|
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); |
||||
|
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ |
||||
|
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); |
||||
|
|
||||
|
/* Render a cJSON entity to text for transfer/storage. */ |
||||
|
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); |
||||
|
/* Render a cJSON entity to text for transfer/storage without any formatting. */ |
||||
|
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); |
||||
|
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ |
||||
|
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); |
||||
|
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ |
||||
|
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); |
||||
|
/* Delete a cJSON entity and all subentities. */ |
||||
|
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); |
||||
|
|
||||
|
/* Returns the number of items in an array (or object). */ |
||||
|
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); |
||||
|
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); |
||||
|
/* Get item "string" from object. Case insensitive. */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); |
||||
|
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ |
||||
|
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); |
||||
|
|
||||
|
/* Check item type and return its value */ |
||||
|
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); |
||||
|
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); |
||||
|
|
||||
|
/* These functions check the type of an item */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); |
||||
|
|
||||
|
/* These calls create a cJSON item of the appropriate type. */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); |
||||
|
/* raw json */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); |
||||
|
|
||||
|
/* Create a string where valuestring references a string so
|
||||
|
* it will not be freed by cJSON_Delete */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); |
||||
|
/* Create an object/array that only references it's elements so
|
||||
|
* they will not be freed by cJSON_Delete */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); |
||||
|
|
||||
|
/* These utilities create an Array of count items.
|
||||
|
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); |
||||
|
|
||||
|
/* Append item to the specified array/object. */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); |
||||
|
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
|
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before |
||||
|
* writing to `item->string` */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); |
||||
|
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); |
||||
|
|
||||
|
/* Remove/Detach items from Arrays/Objects. */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); |
||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); |
||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); |
||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); |
||||
|
|
||||
|
/* Update array items. */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); |
||||
|
|
||||
|
/* Duplicate a cJSON item */ |
||||
|
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); |
||||
|
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
|
* need to be released. With recurse!=0, it will duplicate any children connected to the item. |
||||
|
* The item->next and ->prev pointers are always zero on return from Duplicate. */ |
||||
|
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
|
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ |
||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); |
||||
|
|
||||
|
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
|
* The input pointer json cannot point to a read-only address area, such as a string constant, |
||||
|
* but should point to a readable and writable address area. */ |
||||
|
CJSON_PUBLIC(void) cJSON_Minify(char *json); |
||||
|
|
||||
|
/* Helper functions for creating and adding items to an object at the same time.
|
||||
|
* They return the added item or NULL on failure. */ |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); |
||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); |
||||
|
|
||||
|
/* When assigning an integer value, it needs to be propagated to valuedouble too. */ |
||||
|
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) |
||||
|
/* helper for the cJSON_SetNumberValue macro */ |
||||
|
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); |
||||
|
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) |
||||
|
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ |
||||
|
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); |
||||
|
|
||||
|
/* Macro for iterating over an array or object */ |
||||
|
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) |
||||
|
|
||||
|
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ |
||||
|
CJSON_PUBLIC(void *) cJSON_malloc(size_t size); |
||||
|
CJSON_PUBLIC(void) cJSON_free(void *object); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
#endif |
@ -0,0 +1,419 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include "cJSON.h" |
||||
|
#include "common.h" |
||||
|
#include "network.h" |
||||
|
#include "process.h" |
||||
|
|
||||
|
char *server_addr, *client_addr; |
||||
|
char *server_port, *client_port; |
||||
|
char *password; |
||||
|
char *method; |
||||
|
char *timeout; |
||||
|
int fastopen; |
||||
|
char *plugin; |
||||
|
char *plugin_opts; |
||||
|
char *shadowsocks; |
||||
|
char **shadowsocks_opts; |
||||
|
|
||||
|
void args_dump(); |
||||
|
void args_init(); |
||||
|
void error_exit(char *msg); |
||||
|
char* int_to_string(int num); |
||||
|
void pack_shadowsocks_params(); |
||||
|
char* read_file(char *file_name); |
||||
|
void params_load(char *ss_default); |
||||
|
void json_decode(char *json_content); |
||||
|
void args_decode(int argc, char **argv); |
||||
|
void add_shadowsocks_option(char *option); |
||||
|
void extra_options_decode(char *extra_opts); |
||||
|
|
||||
|
void error_exit(char *msg) { // throw error message with exit-code 1
|
||||
|
printf("[Shadowsocks Bootstrap] ERROR: %s.\n", msg); |
||||
|
printf("[Shadowsocks Bootstrap] exit with error.\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
char* int_to_string(int num) { // int -> string
|
||||
|
if (num < 0) { |
||||
|
error_exit("number must be positive"); |
||||
|
} |
||||
|
int count = 0; |
||||
|
int temp = num; |
||||
|
while(temp != 0) { // check the number of digits
|
||||
|
temp /= 10; |
||||
|
++count; |
||||
|
} |
||||
|
char *str = (char*)malloc(count + 1); |
||||
|
sprintf(str, "%d", num); |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
void params_load(char *ss_default) { // load shadowsocks and plugin params
|
||||
|
if (shadowsocks == NULL) { |
||||
|
shadowsocks = ss_default; |
||||
|
} |
||||
|
shadowsocks_opts[0] = shadowsocks; // fill with file name
|
||||
|
if (plugin != NULL) { // with plugin
|
||||
|
char *rand_port = int_to_string(get_available_port(RANDOM_PORT_START, RANDOM_PORT_END)); |
||||
|
SS_REMOTE_HOST = server_addr; |
||||
|
SS_REMOTE_PORT = server_port; |
||||
|
SS_LOCAL_HOST = "127.0.0.1"; |
||||
|
SS_LOCAL_PORT = rand_port; |
||||
|
server_addr = SS_LOCAL_HOST; |
||||
|
server_port = SS_LOCAL_PORT; |
||||
|
SS_PLUGIN_OPTIONS = plugin_opts; |
||||
|
} |
||||
|
pack_shadowsocks_params(); |
||||
|
shadowsocks_file = shadowsocks; |
||||
|
shadowsocks_args = shadowsocks_opts; |
||||
|
if (plugin == NULL) { |
||||
|
plugin_file = NULL; |
||||
|
} else { |
||||
|
plugin_file = plugin; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void args_init() { // init arguments
|
||||
|
server_addr = client_addr = NULL; |
||||
|
server_port = client_port = NULL; |
||||
|
password = NULL; |
||||
|
method = NULL; |
||||
|
timeout = NULL; |
||||
|
fastopen = 0; |
||||
|
plugin = NULL; |
||||
|
plugin_opts = NULL; |
||||
|
shadowsocks = NULL; |
||||
|
shadowsocks_opts = (char**)malloc(sizeof(char*) * 2); |
||||
|
shadowsocks_opts[0] = ""; // reserved for program name
|
||||
|
shadowsocks_opts[1] = NULL; |
||||
|
} |
||||
|
|
||||
|
char* read_file(char *file_name) { // read file content
|
||||
|
FILE *pfile = fopen(file_name, "rb"); |
||||
|
if (pfile == NULL) { // open failed
|
||||
|
char *msg_prefix = "File `"; |
||||
|
char *msg_suffix = "` open failed"; |
||||
|
char *msg = (char*)malloc(strlen(msg_prefix) + strlen(file_name) + strlen(msg_suffix) + 1); |
||||
|
strcpy(msg, msg_prefix); |
||||
|
error_exit(strcat(strcat(msg, file_name), msg_suffix)); // merge error message
|
||||
|
} |
||||
|
fseek(pfile, 0, SEEK_END); |
||||
|
int file_length = ftell(pfile); // get file length
|
||||
|
char *file_content = (char*)malloc(file_length + 1); // malloc new memory
|
||||
|
if (file_content == NULL) { |
||||
|
error_exit("no enough memory"); // file too large
|
||||
|
} |
||||
|
rewind(pfile); |
||||
|
fread(file_content, 1, file_length, pfile); // read file stream
|
||||
|
file_content[file_length] = '\0'; // set end flag
|
||||
|
fclose(pfile); |
||||
|
return file_content; |
||||
|
} |
||||
|
|
||||
|
void extra_options_decode(char *extra_opts) { // decode shadowsocks extra options
|
||||
|
int num, i; |
||||
|
char *tmp = (char*)calloc(strlen(extra_opts) + 1, 1); // new memery and set as 0x00
|
||||
|
num = i = 0; |
||||
|
for (;;) { |
||||
|
if (extra_opts[num] == '\0' || extra_opts[num] == ' ') { // string end or find a space
|
||||
|
tmp[i] = '\0'; |
||||
|
if (i) { // ignore empty string
|
||||
|
add_shadowsocks_option(tmp); |
||||
|
} |
||||
|
if (extra_opts[num] == '\0') { // string end
|
||||
|
break; |
||||
|
} |
||||
|
num++; |
||||
|
i = 0; |
||||
|
continue; |
||||
|
} |
||||
|
if (extra_opts[num] == '\\') { // skip '\' char
|
||||
|
num++; |
||||
|
} |
||||
|
tmp[i++] = extra_opts[num++]; // copy char
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void add_shadowsocks_option(char *option) { // add shadowsocks options
|
||||
|
int opt_num = 0; |
||||
|
while(shadowsocks_opts[opt_num++] != NULL); // get options number
|
||||
|
shadowsocks_opts = (char**)realloc(shadowsocks_opts, sizeof(char**) * (opt_num + 1)); |
||||
|
shadowsocks_opts[opt_num - 1] = strcpy((char*)malloc(strlen(option) + 1), option); |
||||
|
shadowsocks_opts[opt_num] = NULL; // end sign
|
||||
|
} |
||||
|
|
||||
|
void pack_shadowsocks_params() { // packaging shadowsocks parameters
|
||||
|
if (server_addr != NULL) { |
||||
|
add_shadowsocks_option("-s"); |
||||
|
add_shadowsocks_option(server_addr); |
||||
|
} |
||||
|
if (client_addr != NULL) { |
||||
|
add_shadowsocks_option("-b"); |
||||
|
add_shadowsocks_option(client_addr); |
||||
|
} |
||||
|
if (server_port != NULL) { |
||||
|
add_shadowsocks_option("-p"); |
||||
|
add_shadowsocks_option(server_port); |
||||
|
} |
||||
|
if (client_port != NULL) { |
||||
|
add_shadowsocks_option("-l"); |
||||
|
add_shadowsocks_option(client_port); |
||||
|
} |
||||
|
if (password != NULL) { |
||||
|
add_shadowsocks_option("-k"); |
||||
|
add_shadowsocks_option(password); |
||||
|
} |
||||
|
if (method != NULL) { |
||||
|
add_shadowsocks_option("-m"); |
||||
|
add_shadowsocks_option(method); |
||||
|
} |
||||
|
if (timeout != NULL) { |
||||
|
add_shadowsocks_option("-t"); |
||||
|
add_shadowsocks_option(timeout); |
||||
|
} |
||||
|
if (fastopen) { |
||||
|
add_shadowsocks_option("--fast-open"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void json_decode(char *json_content) { // decode JSON content
|
||||
|
cJSON* json = NULL; |
||||
|
json = cJSON_Parse(json_content); |
||||
|
if (json == NULL) { |
||||
|
error_exit("JSON format error.\n"); |
||||
|
} |
||||
|
json = json->child; |
||||
|
while (json != NULL) { |
||||
|
if (!strcmp(json->string, "server")) { // server => server_addr
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`server` must be a string.\n"); |
||||
|
} |
||||
|
if (server_addr != NULL) { |
||||
|
free(server_addr); |
||||
|
} |
||||
|
server_addr = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "local_address")) { // local_address => client_addr
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`local_address` must be a string.\n"); |
||||
|
} |
||||
|
if (client_addr != NULL) { |
||||
|
free(client_addr); |
||||
|
} |
||||
|
client_addr = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "server_port")) { // server_port => server_port
|
||||
|
if (server_port != NULL) { |
||||
|
free(server_port); |
||||
|
} |
||||
|
if (cJSON_IsNumber(json)) { |
||||
|
server_port = int_to_string(json->valueint); |
||||
|
} else if (cJSON_IsString(json)) { |
||||
|
server_port = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else { |
||||
|
error_exit("`server_port` must be a number or string.\n"); |
||||
|
} |
||||
|
} else if (!strcmp(json->string, "local_port")) { // local_port => client_port
|
||||
|
if (client_port != NULL) { |
||||
|
free(client_port); |
||||
|
} |
||||
|
if (cJSON_IsNumber(json)) { |
||||
|
client_port = int_to_string(json->valueint); |
||||
|
} else if (cJSON_IsString(json)) { |
||||
|
client_port = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else { |
||||
|
error_exit("`local_port` must be a number or string.\n"); |
||||
|
} |
||||
|
} else if (!strcmp(json->string, "password")) { // password => password
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`password` must be a string.\n"); |
||||
|
} |
||||
|
if (password != NULL) { |
||||
|
free(password); |
||||
|
} |
||||
|
password = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "timeout")) { // timeout => timeout
|
||||
|
if (timeout != NULL) { |
||||
|
free(timeout); |
||||
|
} |
||||
|
if (cJSON_IsNumber(json)) { |
||||
|
timeout = int_to_string(json->valueint); |
||||
|
} else if (cJSON_IsString(json)) { |
||||
|
timeout = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else { |
||||
|
error_exit("`timeout` must be a number or string.\n"); |
||||
|
} |
||||
|
} else if (!strcmp(json->string, "method")) { // method => method
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`method` must be a string.\n"); |
||||
|
} |
||||
|
if (method != NULL) { |
||||
|
free(method); |
||||
|
} |
||||
|
method = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "fast_open")) { // fast_open => fastopen
|
||||
|
if (!cJSON_IsBool(json)) { |
||||
|
error_exit("`fast_open` must be a bool.\n"); |
||||
|
} |
||||
|
fastopen = json->valueint; |
||||
|
} else if (!strcmp(json->string, "plugin")) { // plugin => plugin
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`plugin` must be a string.\n"); |
||||
|
} |
||||
|
if (plugin != NULL) { |
||||
|
free(plugin); |
||||
|
} |
||||
|
plugin = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "plugin_opts")) { // plugin_opts => plugin_opts
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`plugin_opts` must be a string.\n"); |
||||
|
} |
||||
|
if (plugin_opts != NULL) { |
||||
|
free(plugin_opts); |
||||
|
} |
||||
|
plugin_opts = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "shadowsocks")) { // shadowsocks => shadowsocks
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`shadowsocks` must be a string.\n"); |
||||
|
} |
||||
|
if (shadowsocks != NULL) { |
||||
|
free(shadowsocks); |
||||
|
} |
||||
|
shadowsocks = strcpy((char*)malloc(strlen(json->valuestring) + 1), json->valuestring); |
||||
|
} else if (!strcmp(json->string, "extra_opts")) { // extra_opts => DECODE => shadowsocks_opts
|
||||
|
if (!cJSON_IsString(json)) { |
||||
|
error_exit("`extra_opts` must be a string.\n"); |
||||
|
} |
||||
|
extra_options_decode(json->valuestring); |
||||
|
} else { // unknow field => ERROR
|
||||
|
char *msg_prefix = "Unknow JSON field `"; |
||||
|
char *msg_suffix = "`.\n"; |
||||
|
char *msg = (char*)malloc(strlen(msg_prefix) + strlen(json->string) + strlen(msg_suffix) + 1); |
||||
|
strcpy(msg, msg_prefix); |
||||
|
error_exit(strcat(strcat(msg, json->string), msg_suffix)); |
||||
|
} |
||||
|
json = json->next; // next field
|
||||
|
} |
||||
|
cJSON_free(json); // free JSON struct
|
||||
|
} |
||||
|
|
||||
|
void args_decode(int argc, char **argv) { // decode the input parameters
|
||||
|
args_init(); |
||||
|
int i; |
||||
|
for (i = 1; i < argc; ++i) { |
||||
|
if (!strcmp(argv[i], "-c")) { // -c => CONFIG_JSON
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-c` require a parameter"); |
||||
|
} |
||||
|
json_decode(read_file(argv[++i])); |
||||
|
} else if (!strcmp(argv[i], "-s")) { // -s => server_addr
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-s` require a parameter"); |
||||
|
} |
||||
|
if (server_addr != NULL) { |
||||
|
free(server_addr); |
||||
|
} |
||||
|
server_addr = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-p")) { // -p => server_port
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-p` require a parameter"); |
||||
|
} |
||||
|
if (server_port != NULL) { |
||||
|
free(server_port); |
||||
|
} |
||||
|
server_port = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-b")) { // -b => client_addr
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-b` require a parameter"); |
||||
|
} |
||||
|
if (client_addr != NULL) { |
||||
|
free(client_addr); |
||||
|
} |
||||
|
client_addr = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-l")) { // -l => client_port
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-l` require a parameter"); |
||||
|
} |
||||
|
if (client_port != NULL) { |
||||
|
free(client_port); |
||||
|
} |
||||
|
client_port = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-k")) { // -k => password
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-k` require a parameter"); |
||||
|
} |
||||
|
if (password != NULL) { |
||||
|
free(password); |
||||
|
} |
||||
|
password = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-m")) { // -m => method
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-m` require a parameter"); |
||||
|
} |
||||
|
if (method != NULL) { |
||||
|
free(method); |
||||
|
} |
||||
|
method = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "-t")) { // -t => timeout
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`-t` require a parameter"); |
||||
|
} |
||||
|
if (timeout != NULL) { |
||||
|
free(timeout); |
||||
|
} |
||||
|
timeout = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "--fast-open")) { // --fast-open
|
||||
|
fastopen = 1; |
||||
|
} else if (!strcmp(argv[i], "--plugin")) { // --plugin => plugin
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`--plugin` require a parameter"); |
||||
|
} |
||||
|
if (plugin != NULL) { |
||||
|
free(plugin); |
||||
|
} |
||||
|
plugin = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "--plugin-opts")) { // --plugin-opts => plugin_opts
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`--plugin-opts` require a parameter"); |
||||
|
} |
||||
|
if (plugin_opts != NULL) { |
||||
|
free(plugin_opts); |
||||
|
} |
||||
|
plugin_opts = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else if (!strcmp(argv[i], "--shadowsocks")) { // --shadowsocks => shadowsocks
|
||||
|
if (i + 1 == argc) { |
||||
|
error_exit("`--shadowsocks` require a parameter"); |
||||
|
} |
||||
|
if (shadowsocks != NULL) { |
||||
|
free(shadowsocks); |
||||
|
} |
||||
|
shadowsocks = strcpy((char*)malloc(strlen(argv[i]) + 1), argv[++i]); |
||||
|
} else { // unknow option => archive
|
||||
|
add_shadowsocks_option(argv[i]); // archive unknow options
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void args_dump() { // show parameter's content
|
||||
|
printf("server_addr = %s\n", server_addr); |
||||
|
printf("client_addr = %s\n", client_addr); |
||||
|
printf("server_port = %s\n", server_port); |
||||
|
printf("client_port = %s\n", client_port); |
||||
|
printf("password = %s\n", password); |
||||
|
printf("method = %s\n", method); |
||||
|
printf("timeout = %s\n", timeout); |
||||
|
if (fastopen) { |
||||
|
printf("fastopen = true\n"); |
||||
|
} else { |
||||
|
printf("fastopen = false\n"); |
||||
|
} |
||||
|
printf("shadowsocks = %s\n", shadowsocks); |
||||
|
printf("plugin = %s\n", plugin); |
||||
|
printf("plugin_opts = %s\n", plugin_opts); |
||||
|
int num = 0; |
||||
|
printf("options:\n"); |
||||
|
while(shadowsocks_opts[num] != NULL) { |
||||
|
printf(" '%s'\n", shadowsocks_opts[num]); |
||||
|
num++; |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
#ifndef _COMMON_H_ |
||||
|
#define _COMMON_H_ |
||||
|
|
||||
|
#define RANDOM_PORT_START 41952 |
||||
|
#define RANDOM_PORT_END 65535 |
||||
|
|
||||
|
extern char *server_addr, *client_addr; |
||||
|
extern char *server_port, *client_port; |
||||
|
extern char *password; |
||||
|
extern char *method; |
||||
|
extern char *timeout; |
||||
|
extern int fastopen; |
||||
|
extern char *plugin; |
||||
|
extern char *plugin_opts; |
||||
|
extern char *shadowsocks; |
||||
|
extern char **shadowsocks_opts; |
||||
|
|
||||
|
void params_load(char *ss_default); |
||||
|
void args_decode(int argc, char **argv); |
||||
|
|
||||
|
#endif |
@ -0,0 +1,44 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <string.h> |
||||
|
#include "common.h" |
||||
|
#include "process.h" |
||||
|
|
||||
|
#define SHADOWSOCKS_DEFAULT "sslocal" |
||||
|
|
||||
|
char *help_msg = |
||||
|
"\
|
||||
|
\n\ |
||||
|
ss-bootstrap-local\n\ |
||||
|
\n\ |
||||
|
A simple program to make the original shadowsocks support SIP003 plugins.\n\ |
||||
|
\n\ |
||||
|
-s <server_host> Host name or IP address of your remote server.\n\ |
||||
|
-p <server_port> Port number of your remote server.\n\ |
||||
|
-b <local_address> Local address to bind.\n\ |
||||
|
-l <local_port> Port number of your local server.\n\ |
||||
|
\n\ |
||||
|
-c <config_file> Path to JSON config file.\n\ |
||||
|
-k <password> Password of your remote server.\n\ |
||||
|
-m <method> Encrypt method.\n\ |
||||
|
-t <timeout> Socket timeout in seconds.\n\ |
||||
|
--fast-open Enable TCP fast open (with Linux kernel 3.7+).\n\ |
||||
|
--plugin <name> Enable SIP003 plugin.\n\ |
||||
|
--plugin-opts <options> Set SIP003 plugin options.\n\ |
||||
|
--shadowsocks <sslocal> Set shadowsocks local program.\n\ |
||||
|
-h, --help Print this message.\n\ |
||||
|
\n\ |
||||
|
"; |
||||
|
|
||||
|
int main(int argc, char *argv[]) { |
||||
|
int i = 0; |
||||
|
for (i = 0; i < argc; ++i) { |
||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { |
||||
|
printf("%s", help_msg); // show help message
|
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
args_decode(argc, argv); |
||||
|
params_load(SHADOWSOCKS_DEFAULT); // default file name
|
||||
|
start_bootstrap(); |
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <unistd.h> |
||||
|
#include <sys/time.h> |
||||
|
#include <netinet/in.h> |
||||
|
#include "network.h" |
||||
|
|
||||
|
int get_random_num(int range_start, int range_end) { // create a random number in range
|
||||
|
struct timeval tp; |
||||
|
gettimeofday(&tp, NULL); |
||||
|
srand(tp.tv_usec); |
||||
|
return range_start + (rand() % (range_end - range_start + 1)); |
||||
|
} |
||||
|
|
||||
|
int check_port_available(unsigned short port) { // test a port is available or not
|
||||
|
struct sockaddr_in server_addr; |
||||
|
memset(&server_addr, 0, sizeof(server_addr)); // struct init
|
||||
|
server_addr.sin_family = AF_INET; // set as IP communication
|
||||
|
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // allow any connection
|
||||
|
server_addr.sin_port = htons(port); // set telnet port
|
||||
|
int server_sockfd = socket(AF_INET, SOCK_STREAM, 0); // socket created
|
||||
|
if (server_sockfd < 0) { // create failed
|
||||
|
return 0; |
||||
|
} |
||||
|
if (bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0) { // bind failed
|
||||
|
return 0; |
||||
|
} |
||||
|
if (close(server_sockfd) != 0) { // close failed
|
||||
|
return 0; |
||||
|
} |
||||
|
return 1; // port available
|
||||
|
} |
||||
|
|
||||
|
int get_available_port(unsigned short range_start, unsigned short range_end) { // get a available port
|
||||
|
unsigned short port; |
||||
|
for (;;) { // wait until a available port in range
|
||||
|
port = get_random_num(range_start, range_end); // get a random port in range
|
||||
|
if (check_port_available(port)) { // port available
|
||||
|
return (int)port; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
#ifndef _NETWORK_H_ |
||||
|
#define _NETWORK_H_ |
||||
|
|
||||
|
int get_available_port(unsigned short range_start, unsigned short range_end); |
||||
|
|
||||
|
#endif |
@ -0,0 +1,204 @@ |
|||||
|
#include <glib.h> |
||||
|
#include <stdio.h> |
||||
|
#include <unistd.h> |
||||
|
#include <sys/wait.h> |
||||
|
#include <sys/prctl.h> |
||||
|
#include "process.h" |
||||
|
|
||||
|
char *shadowsocks_file; |
||||
|
char **shadowsocks_args; |
||||
|
char *plugin_file; |
||||
|
char *SS_REMOTE_HOST; |
||||
|
char *SS_REMOTE_PORT; |
||||
|
char *SS_LOCAL_HOST; |
||||
|
char *SS_LOCAL_PORT; |
||||
|
char *SS_PLUGIN_OPTIONS; |
||||
|
|
||||
|
char **plugin_env; |
||||
|
char *plugin_arg[] = { NULL, NULL }; |
||||
|
|
||||
|
int exiting = 0; |
||||
|
|
||||
|
typedef struct exit_info { |
||||
|
int pid; |
||||
|
int exit_code; |
||||
|
int exit_signal; |
||||
|
} exit_info; |
||||
|
|
||||
|
GMainLoop* main_loop; |
||||
|
pid_t ss_pid = 0, plugin_pid = 0; |
||||
|
|
||||
|
void show_params(); |
||||
|
void process_exec(); |
||||
|
void get_sub_exit(); |
||||
|
void exit_with_child(); |
||||
|
void plugin_env_load(); |
||||
|
void show_exit_info(exit_info info); |
||||
|
exit_info get_exit_info(int status, pid_t pid); |
||||
|
|
||||
|
void show_exit_info(exit_info info) { // show info of child process death
|
||||
|
printf("(PID = %d) -> ", info.pid); |
||||
|
if (info.exit_code != -1) { // exit normally
|
||||
|
printf("exit code %d.\n", info.exit_code); |
||||
|
} else if (info.exit_signal != -1) { // abnormal exit
|
||||
|
printf("killed by signal %d.\n", info.exit_signal); |
||||
|
} else { |
||||
|
printf("unknow reason.\n"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exit_info get_exit_info(int status, pid_t pid) { // get why the child process death
|
||||
|
exit_info temp; |
||||
|
temp.pid = pid; |
||||
|
temp.exit_code = temp.exit_signal = -1; |
||||
|
if (WIFEXITED(status)) { // exit normally (with a exit-code)
|
||||
|
temp.exit_code = WEXITSTATUS(status); |
||||
|
} |
||||
|
if (WIFSIGNALED(status)) { // abnormal exit (with a signal)
|
||||
|
temp.exit_signal = WTERMSIG(status); |
||||
|
} |
||||
|
return temp; |
||||
|
} |
||||
|
|
||||
|
void get_sub_exit() { // catch child process die
|
||||
|
exit_info sub_exit_info; |
||||
|
int ss_ret, plugin_ret, ss_status, plugin_status; |
||||
|
if (exiting) { |
||||
|
int status; |
||||
|
waitpid(0, &status, 0); |
||||
|
return; |
||||
|
} |
||||
|
if (ss_pid != 0) { |
||||
|
ss_ret = waitpid(ss_pid, &ss_status, WNOHANG); // non-blocking
|
||||
|
if (ss_ret == -1) { |
||||
|
perror("[Shadowsocks Bootstrap] shadowsocks waitpid error"); |
||||
|
exit_with_child(); |
||||
|
} else if (ss_ret) { // ss exit
|
||||
|
sub_exit_info = get_exit_info(ss_status, ss_pid); |
||||
|
printf("[Shadowsocks Bootstrap] shadowsocks exit "); |
||||
|
show_exit_info(sub_exit_info); |
||||
|
exit_with_child(); |
||||
|
} |
||||
|
} |
||||
|
if (plugin_file != NULL && plugin_pid != 0) { // with plugin
|
||||
|
plugin_ret = waitpid(plugin_pid, &plugin_status, WNOHANG); // non-blocking
|
||||
|
if (plugin_ret == -1) { |
||||
|
perror("[Shadowsocks Bootstrap] plugin waitpid error"); |
||||
|
exit_with_child(); |
||||
|
} else if (plugin_ret) { // plugin exit
|
||||
|
sub_exit_info = get_exit_info(plugin_status, plugin_pid); |
||||
|
printf("[Shadowsocks Bootstrap] plugin exit "); |
||||
|
show_exit_info(sub_exit_info); |
||||
|
exit_with_child(); |
||||
|
} |
||||
|
} |
||||
|
g_main_loop_quit(main_loop); // exit main loop
|
||||
|
} |
||||
|
|
||||
|
void plugin_env_load() { // load plugin's environment variable
|
||||
|
char *remote_host = "SS_REMOTE_HOST="; |
||||
|
char *remote_port = "SS_REMOTE_PORT="; |
||||
|
char *local_host = "SS_LOCAL_HOST="; |
||||
|
char *local_port = "SS_LOCAL_PORT="; |
||||
|
char *plugin_options = "SS_PLUGIN_OPTIONS="; |
||||
|
plugin_env = (char**)malloc(sizeof(char*) * 6); |
||||
|
plugin_env[0] = (char*)malloc(strlen(remote_host) + strlen(SS_REMOTE_HOST) + 1); |
||||
|
plugin_env[1] = (char*)malloc(strlen(remote_port) + strlen(SS_REMOTE_PORT) + 1); |
||||
|
plugin_env[2] = (char*)malloc(strlen(local_host) + strlen(SS_LOCAL_HOST) + 1); |
||||
|
plugin_env[3] = (char*)malloc(strlen(local_port) + strlen(SS_LOCAL_PORT) + 1); |
||||
|
strcat(strcpy(plugin_env[0], remote_host), SS_REMOTE_HOST); |
||||
|
strcat(strcpy(plugin_env[1], remote_port), SS_REMOTE_PORT); |
||||
|
strcat(strcpy(plugin_env[2], local_host), SS_LOCAL_HOST); |
||||
|
strcat(strcpy(plugin_env[3], local_port), SS_LOCAL_PORT); |
||||
|
if (SS_PLUGIN_OPTIONS == NULL) { |
||||
|
plugin_env[4] = NULL; |
||||
|
} else { |
||||
|
plugin_env[4] = (char*)malloc(strlen(plugin_options) + strlen(SS_PLUGIN_OPTIONS) + 1); |
||||
|
strcat(strcpy(plugin_env[4], plugin_options), SS_PLUGIN_OPTIONS); |
||||
|
} |
||||
|
plugin_env[5] = NULL; |
||||
|
plugin_arg[0] = plugin_file; |
||||
|
} |
||||
|
|
||||
|
void process_exec() { // run shadowsocks main process and plugin (as child process)
|
||||
|
if ((ss_pid = fork()) < 0) { |
||||
|
perror("[Shadowsocks Bootstrap] fork error"); |
||||
|
exit_with_child(); |
||||
|
} else if (ss_pid == 0) { // child process
|
||||
|
prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father
|
||||
|
if (execvp(shadowsocks_args[0], shadowsocks_args) < 0) { |
||||
|
perror("[Shadowsocks Bootstrap] shadowsocks exec error"); |
||||
|
exit(2); |
||||
|
} |
||||
|
} |
||||
|
if (plugin_file == NULL) { // plugin no need
|
||||
|
return; |
||||
|
} |
||||
|
usleep(100 * 1000); // sleep 100ms (python always a little slower)
|
||||
|
if (exiting) { // cancel plugin exec
|
||||
|
return; |
||||
|
} |
||||
|
if ((plugin_pid = fork()) < 0) { |
||||
|
perror("[Shadowsocks Bootstrap] fork error"); |
||||
|
exit_with_child(); |
||||
|
} else if (plugin_pid == 0) { // child process
|
||||
|
prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father
|
||||
|
plugin_env_load(); |
||||
|
if (execvpe(plugin_file, plugin_arg, plugin_env) < 0) { |
||||
|
perror("[Shadowsocks Bootstrap] plugin exec error"); |
||||
|
exit(2); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void exit_with_child() { // exit and kill his child process
|
||||
|
if (exiting) { |
||||
|
for (;;) { |
||||
|
sleep(1); // block
|
||||
|
} |
||||
|
} |
||||
|
exiting = 1; |
||||
|
if (ss_pid != 0) { |
||||
|
kill(ss_pid, SIGKILL); |
||||
|
printf("[Shadowsocks Bootstrap] kill shadowsocks process.\n"); |
||||
|
} |
||||
|
if (plugin_file != NULL && plugin_pid != 0) { |
||||
|
kill(plugin_pid, SIGKILL); |
||||
|
printf("[Shadowsocks Bootstrap] kill plugin process.\n"); |
||||
|
} |
||||
|
int status; |
||||
|
printf("[Shadowsocks Bootstrap] wait for child process.\n"); |
||||
|
waitpid(0, &status, 0); // block
|
||||
|
printf("[Shadowsocks Bootstrap] exit.\n"); |
||||
|
exit(1); |
||||
|
} |
||||
|
|
||||
|
void show_params() { // show shadowsocks and plugin params
|
||||
|
int num = 0; |
||||
|
printf("[Shadowsocks Bootstrap]"); |
||||
|
while(shadowsocks_args[num] != NULL) { |
||||
|
printf(" %s", shadowsocks_args[num++]); |
||||
|
} |
||||
|
printf("\n"); |
||||
|
if (plugin_file == NULL) { |
||||
|
printf("[Shadowsocks Bootstrap] plugin no need.\n"); |
||||
|
return; |
||||
|
} |
||||
|
printf("[Shadowsocks Bootstrap] plugin -> %s\n", plugin_file); |
||||
|
printf("[Shadowsocks Bootstrap] SS_REMOTE_HOST -> %s\n", SS_REMOTE_HOST); |
||||
|
printf("[Shadowsocks Bootstrap] SS_REMOTE_PORT -> %s\n", SS_REMOTE_PORT); |
||||
|
printf("[Shadowsocks Bootstrap] SS_LOCAL_HOST -> %s\n", SS_LOCAL_HOST); |
||||
|
printf("[Shadowsocks Bootstrap] SS_LOCAL_PORT -> %s\n", SS_LOCAL_PORT); |
||||
|
printf("[Shadowsocks Bootstrap] SS_PLUGIN_OPTIONS -> %s\n", SS_PLUGIN_OPTIONS); |
||||
|
} |
||||
|
|
||||
|
void start_bootstrap() { // start shadowsocks and plugin (optional)
|
||||
|
show_params(); |
||||
|
main_loop = g_main_loop_new(NULL, FALSE); |
||||
|
signal(SIGINT, exit_with_child); // catch Ctrl + C (2)
|
||||
|
signal(SIGTERM, exit_with_child); // catch exit signal (15)
|
||||
|
signal(SIGCHLD, get_sub_exit); // callback when child process die
|
||||
|
process_exec(); // exec child process
|
||||
|
g_main_loop_run(main_loop); // into main loop for wait
|
||||
|
exit_with_child(); |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
#ifndef _PROCESS_H_ |
||||
|
#define _PROCESS_H_ |
||||
|
|
||||
|
extern char *shadowsocks_file; |
||||
|
extern char **shadowsocks_args; |
||||
|
extern char *plugin_file; |
||||
|
extern char *SS_REMOTE_HOST; |
||||
|
extern char *SS_REMOTE_PORT; |
||||
|
extern char *SS_LOCAL_HOST; |
||||
|
extern char *SS_LOCAL_PORT; |
||||
|
extern char *SS_PLUGIN_OPTIONS; |
||||
|
|
||||
|
void start_bootstrap(); |
||||
|
|
||||
|
#endif |
@ -0,0 +1,44 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <string.h> |
||||
|
#include "common.h" |
||||
|
#include "process.h" |
||||
|
|
||||
|
#define SHADOWSOCKS_DEFAULT "ssserver" |
||||
|
|
||||
|
char *help_msg = |
||||
|
"\
|
||||
|
\n\ |
||||
|
ss-bootstrap-server\n\ |
||||
|
\n\ |
||||
|
A simple program to make the original shadowsocks support SIP003 plugins.\n\ |
||||
|
\n\ |
||||
|
-s <server_host> Host name or IP address of your remote server.\n\ |
||||
|
-p <server_port> Port number of your remote server.\n\ |
||||
|
-b <local_address> Local address to bind.\n\ |
||||
|
-l <local_port> Port number of your local server.\n\ |
||||
|
\n\ |
||||
|
-c <config_file> Path to JSON config file.\n\ |
||||
|
-k <password> Password of your remote server.\n\ |
||||
|
-m <method> Encrypt method.\n\ |
||||
|
-t <timeout> Socket timeout in seconds.\n\ |
||||
|
--fast-open Enable TCP fast open (with Linux kernel 3.7+).\n\ |
||||
|
--plugin <name> Enable SIP003 plugin.\n\ |
||||
|
--plugin-opts <options> Set SIP003 plugin options.\n\ |
||||
|
--shadowsocks <ssservre> Set shadowsocks server program.\n\ |
||||
|
-h, --help Print this message.\n\ |
||||
|
\n\ |
||||
|
"; |
||||
|
|
||||
|
int main(int argc, char *argv[]) { |
||||
|
int i = 0; |
||||
|
for (i = 0; i < argc; ++i) { |
||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { |
||||
|
printf("%s", help_msg); // show help message
|
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
args_decode(argc, argv); |
||||
|
params_load(SHADOWSOCKS_DEFAULT); // default file name
|
||||
|
start_bootstrap(); |
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue