├── .gitignore ├── include ├── utils │ ├── dns.h │ ├── process.h │ ├── network.h │ ├── logger.h │ └── cJSON.h ├── sip003.h ├── load.h └── common.h ├── src ├── utils │ ├── CMakeLists.txt │ ├── logger.c │ ├── dns.c │ ├── process.c │ ├── network.c │ └── cJSON.c ├── CMakeLists.txt ├── local.c ├── server.c ├── common.c ├── sip003.c └── load.c ├── CMakeLists.txt ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /build/ 3 | /.idea/ 4 | /cmake-build-debug/ 5 | /cmake-build-release/ 6 | -------------------------------------------------------------------------------- /include/utils/dns.h: -------------------------------------------------------------------------------- 1 | #ifndef DNS_H_ 2 | #define DNS_H_ 3 | 4 | char* dns_resolve(const char *domain); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/utils/process.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_H_ 2 | #define PROCESS_H_ 3 | 4 | #include "sip003.h" 5 | 6 | void start_bootstrap(int local_mode, sip003 *service); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | 3 | aux_source_directory(${PROJECT_SOURCE_DIR}/src/utils utils_src) 4 | add_library(utils ${utils_src}) 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(shadowsocks-bootstrap) 3 | 4 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 5 | 6 | add_subdirectory(src) 7 | -------------------------------------------------------------------------------- /include/utils/network.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H_ 2 | #define NETWORK_H_ 3 | 4 | extern int PROXY_EXIT; 5 | 6 | int is_ip_addr(char *address); 7 | int get_available_port(unsigned short range_start, unsigned short range_end); 8 | void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include/sip003.h: -------------------------------------------------------------------------------- 1 | #ifndef SIP003_H_ 2 | #define SIP003_H_ 3 | 4 | #include "load.h" 5 | 6 | typedef struct { 7 | char *SS_REMOTE_HOST; 8 | char *SS_REMOTE_PORT; 9 | char *SS_LOCAL_HOST; 10 | char *SS_LOCAL_PORT; 11 | char *SS_PLUGIN_OPTIONS; 12 | char *plugin_file; 13 | char **shadowsocks_cmd; 14 | int is_udp_proxy; 15 | } sip003; 16 | 17 | sip003* load_sip003(char *ss_default, bootstrap *info); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/load.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_H_ 2 | #define LOAD_H_ 3 | 4 | typedef struct { 5 | int is_udp_proxy; 6 | char *server_addr, *client_addr; 7 | char *server_port, *client_port; 8 | char *password; 9 | char *method; 10 | char *timeout; 11 | int fastopen; 12 | char *plugin; 13 | char *plugin_opts; 14 | char *shadowsocks; 15 | char **shadowsocks_opts; 16 | } bootstrap; 17 | 18 | bootstrap* load_info(int argc, char **argv); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | 3 | include_directories(${PROJECT_SOURCE_DIR}/include) 4 | include_directories(${PROJECT_SOURCE_DIR}/include/utils) 5 | 6 | add_subdirectory(utils) 7 | 8 | add_executable(ss-bootstrap-local local.c common.c load.c sip003.c) 9 | target_link_libraries(ss-bootstrap-local utils pthread) 10 | 11 | add_executable(ss-bootstrap-server server.c common.c load.c sip003.c) 12 | target_link_libraries(ss-bootstrap-server utils pthread) 13 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H_ 2 | #define COMMON_H_ 3 | 4 | #define TRUE 1 5 | #define FALSE 0 6 | 7 | #define VERSION "1.0.2" 8 | 9 | #define RANDOM_PORT_START 41952 10 | #define RANDOM_PORT_END 65535 11 | 12 | void init(int argc, char **argv, const char *help_msg); 13 | 14 | char* int_to_string(int num); 15 | char* new_string(const char *str); 16 | char* read_file(const char *file_name); 17 | char* string_list_join(char **string_list); 18 | char** string_list_append(char **string_list, const char *data); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/utils/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H_ 2 | #define LOGGER_H_ 3 | 4 | #define LOG_PREFIX "Bootstrap" 5 | 6 | enum { 7 | LOG_DEBUG, 8 | LOG_INFO, 9 | LOG_WARN, 10 | LOG_ERROR, 11 | LOG_FATAL 12 | }; 13 | 14 | #define log_debug(...) log_printf(LOG_DEBUG, __VA_ARGS__) 15 | #define log_info(...) log_printf(LOG_INFO, __VA_ARGS__) 16 | #define log_warn(...) log_printf(LOG_WARN, __VA_ARGS__) 17 | #define log_error(...) log_printf(LOG_ERROR, __VA_ARGS__) 18 | #define log_fatal(...) log_printf(LOG_FATAL, __VA_ARGS__) 19 | 20 | extern int LOG_LEVEL; 21 | void log_perror(const char *fmt, ...); 22 | void log_printf(int level, const char *fmt, ...); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dnomd343 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/local.c: -------------------------------------------------------------------------------- 1 | #include "load.h" 2 | #include "logger.h" 3 | #include "common.h" 4 | #include "process.h" 5 | 6 | #define HELP_MSG "\n\ 7 | ss-bootstrap-local (%s)\n\ 8 | \n\ 9 | A simple program to make the original shadowsocks support SIP003 plugins.\n\ 10 | \n\ 11 | -s Host name or IP address of your remote server.\n\ 12 | -p Port number of your remote server.\n\ 13 | -b Local address to bind.\n\ 14 | -l Port number of your local server.\n\ 15 | \n\ 16 | -c Path to JSON config file.\n\ 17 | -k Password of your remote server.\n\ 18 | -m Encrypt method.\n\ 19 | -t Socket timeout in seconds.\n\ 20 | --fast-open Enable TCP fast open (with Linux kernel 3.7+).\n\ 21 | --plugin Enable SIP003 plugin.\n\ 22 | --plugin-opts Set SIP003 plugin options.\n\ 23 | --shadowsocks Set shadowsocks local program.\n\ 24 | --no-udp Do not use UDP proxy.\n\ 25 | --debug Enable debug mode.\n\ 26 | -h, --help Print this message.\n\ 27 | \n" 28 | 29 | int main(int argc, char *argv[]) { 30 | init(argc, argv, HELP_MSG); 31 | log_info("Shadowsocks bootstrap local (%s)", VERSION); 32 | bootstrap *info = load_info(argc, argv); 33 | start_bootstrap(TRUE, load_sip003("sslocal", info)); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | #include "load.h" 2 | #include "logger.h" 3 | #include "common.h" 4 | #include "process.h" 5 | 6 | #define HELP_MSG "\n\ 7 | ss-bootstrap-server (%s)\n\ 8 | \n\ 9 | A simple program to make the original shadowsocks support SIP003 plugins.\n\ 10 | \n\ 11 | -s Host name or IP address of your remote server.\n\ 12 | -p Port number of your remote server.\n\ 13 | -b Local address to bind.\n\ 14 | -l Port number of your local server.\n\ 15 | \n\ 16 | -c Path to JSON config file.\n\ 17 | -k Password of your remote server.\n\ 18 | -m Encrypt method.\n\ 19 | -t Socket timeout in seconds.\n\ 20 | --fast-open Enable TCP fast open (with Linux kernel 3.7+).\n\ 21 | --plugin Enable SIP003 plugin.\n\ 22 | --plugin-opts Set SIP003 plugin options.\n\ 23 | --shadowsocks Set shadowsocks server program.\n\ 24 | --no-udp Do not use UDP proxy.\n\ 25 | --debug Enable debug mode.\n\ 26 | -h, --help Print this message.\n\ 27 | \n" 28 | 29 | int main(int argc, char *argv[]) { 30 | init(argc, argv, HELP_MSG); 31 | log_info("Shadowsocks bootstrap server (%s)", VERSION); 32 | bootstrap *info = load_info(argc, argv); 33 | start_bootstrap(FALSE, load_sip003("ssserver", info)); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "logger.h" 7 | 8 | int LOG_LEVEL = LOG_INFO; // default log level 9 | 10 | static const char *log_string[] = { 11 | "[DEBUG]", 12 | "[INFO]", 13 | "[WARN]", 14 | "[ERROR]", 15 | "[FATAL]", 16 | }; 17 | 18 | static const char *log_color[] = { 19 | "\x1b[39m", // debug 20 | "\x1b[32m", // info 21 | "\x1b[33m", // warn 22 | "\x1b[31m", // error 23 | "\x1b[95m", // fatal 24 | }; 25 | 26 | void fprint_prefix() { // print log prefix and time info 27 | time_t _time; 28 | time(&_time); 29 | struct timeval tv; 30 | gettimeofday(&tv, NULL); 31 | struct tm *t = localtime(&_time); 32 | fprintf(stderr, "\x1b[36m[%s]\x1b[0m", LOG_PREFIX); 33 | fprintf(stderr, " \x1b[90m%04d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); 34 | fprintf(stderr, " %02d:%02d:%02d.%03ld\x1b[0m", t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec / 1000); 35 | } 36 | 37 | void log_printf(int level, const char *fmt, ...) { 38 | if (level < LOG_LEVEL) { // skip low log level 39 | return; 40 | } 41 | va_list ap; 42 | va_start(ap, fmt); 43 | fprint_prefix(); 44 | fprintf(stderr, " %s%s\x1b[0m ", log_color[level], log_string[level]); // show log level 45 | vfprintf(stderr, fmt, ap); // output log content 46 | fprintf(stderr, "\n"); // add LF after line 47 | fflush(stderr); 48 | va_end(ap); 49 | if (level == LOG_FATAL) { 50 | exit(1); 51 | } 52 | } 53 | 54 | void log_perror(const char *fmt, ...) { 55 | va_list ap; 56 | va_start(ap, fmt); 57 | fprint_prefix(); 58 | fprintf(stderr, " %s%s\x1b[0m ", log_color[LOG_ERROR], log_string[LOG_ERROR]); 59 | vfprintf(stderr, fmt, ap); // output log content 60 | fflush(stderr); 61 | perror(""); 62 | } 63 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "logger.h" 5 | #include "common.h" 6 | 7 | char* new_string(const char *str) { 8 | return strcpy((char*)malloc(strlen(str) + 1), str); 9 | } 10 | 11 | char* int_to_string(int num) { // int -> string 12 | if (num < 0) { 13 | log_fatal("Number must be positive"); 14 | } 15 | int count = 0; 16 | int temp = num; 17 | while (temp != 0) { // check the number of digits 18 | temp /= 10; 19 | ++count; 20 | } 21 | char *str = (char*)malloc(count + 1); 22 | sprintf(str, "%d", num); 23 | return str; 24 | } 25 | 26 | char** string_list_append(char **string_list, const char *data) { 27 | int num = 0; 28 | while(string_list[num++] != NULL); // get string list size 29 | string_list = (char**)realloc(string_list, sizeof(char*) * (num + 1)); 30 | string_list[num - 1] = new_string(data); 31 | string_list[num] = NULL; // list end sign 32 | return string_list; 33 | } 34 | 35 | char* string_list_join(char **string_list) { // combine string list -> `str_1` `str_2` `str_3` ... 36 | char *join_str = (char*)malloc(0); 37 | for (char **str = string_list; *str != NULL; ++str) { 38 | join_str = (char*)realloc(join_str, strlen(join_str) + strlen(*str) + 4); 39 | join_str = strcat(strcat(join_str, "`"), *str); 40 | join_str = strcat(join_str, "` "); 41 | } 42 | return join_str; 43 | } 44 | 45 | char* read_file(const char *file_name) { // read file content 46 | log_debug("Start read file -> %s", file_name); 47 | FILE *pfile = fopen(file_name, "rb"); 48 | if (pfile == NULL) { // file open failed 49 | log_fatal("File `%s` open failed", file_name); 50 | } 51 | fseek(pfile, 0, SEEK_END); 52 | long file_length = ftell(pfile); // get file length 53 | char *file_content = (char*)malloc(file_length + 1); // malloc new memory 54 | if (file_content == NULL) { 55 | log_fatal("No enough memory for reading file"); // file too large 56 | } 57 | rewind(pfile); 58 | fread(file_content, 1, file_length, pfile); // read file stream 59 | file_content[file_length] = '\0'; // set end flag 60 | fclose(pfile); 61 | log_debug("File `%s` read success ->\n%s", file_name, file_content); 62 | return file_content; 63 | } 64 | 65 | void init(int argc, char **argv, const char *help_msg) { 66 | if (argc <= 1) { // with only one argument 67 | printf(help_msg, VERSION); 68 | exit(0); 69 | } 70 | for (int i = 0; i < argc; ++i) { 71 | if (!strcmp(argv[i], "--debug")) { // include `--debug` 72 | LOG_LEVEL = LOG_DEBUG; 73 | } 74 | if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { // include `-h` or `--help` 75 | printf(help_msg, VERSION); // show help message 76 | exit(0); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/sip003.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "sip003.h" 3 | #include "logger.h" 4 | #include "common.h" 5 | #include "network.h" 6 | 7 | void dump_sip003(sip003 *service); 8 | void add_sip003_arg(bootstrap *info, char *key, char *value); 9 | 10 | void dump_sip003(sip003 *service) { // show shadowsocks and plugin params 11 | char *cmd_str = string_list_join(service->shadowsocks_cmd); 12 | log_info("Shadowsocks -> %s", cmd_str); 13 | free(cmd_str); 14 | if (service->plugin_file == NULL) { 15 | log_info("Plugin no need"); 16 | } else { 17 | log_info("Plugin -> `%s`", service->plugin_file); 18 | log_info("SS_REMOTE_HOST -> `%s`", service->SS_REMOTE_HOST); 19 | log_info("SS_REMOTE_PORT -> `%s`", service->SS_REMOTE_PORT); 20 | log_info("SS_LOCAL_HOST -> `%s`", service->SS_LOCAL_HOST); 21 | log_info("SS_LOCAL_PORT -> `%s`", service->SS_LOCAL_PORT); 22 | log_info("SS_PLUGIN_OPTIONS -> `%s`", service->SS_PLUGIN_OPTIONS); 23 | } 24 | } 25 | 26 | void add_sip003_arg(bootstrap *info, char *key, char *value) { 27 | if (value != NULL) { 28 | info->shadowsocks_opts = string_list_append(info->shadowsocks_opts, key); 29 | info->shadowsocks_opts = string_list_append(info->shadowsocks_opts, value); 30 | } 31 | } 32 | 33 | sip003* load_sip003(char *ss_default, bootstrap *info) { // load shadowsocks and plugin params 34 | sip003 *service = (sip003*)malloc(sizeof(sip003)); 35 | if (info->shadowsocks == NULL) { 36 | info->shadowsocks = ss_default; // load default shadowsocks name 37 | } 38 | info->shadowsocks_opts[0] = info->shadowsocks; // fill with file name 39 | 40 | service->plugin_file = NULL; 41 | service->is_udp_proxy = info->is_udp_proxy; 42 | if (info->plugin != NULL) { // with sip003 plugin 43 | char *rand_port = int_to_string(get_available_port(RANDOM_PORT_START, RANDOM_PORT_END)); 44 | service->SS_REMOTE_HOST = info->server_addr; 45 | service->SS_REMOTE_PORT = info->server_port; 46 | service->SS_LOCAL_HOST = "127.0.0.1"; 47 | service->SS_LOCAL_PORT = rand_port; 48 | service->SS_PLUGIN_OPTIONS = info->plugin_opts; 49 | info->server_addr = service->SS_LOCAL_HOST; 50 | info->server_port = service->SS_LOCAL_PORT; 51 | service->plugin_file = info->plugin; 52 | } 53 | 54 | add_sip003_arg(info, "-s", info->server_addr); 55 | add_sip003_arg(info, "-b", info->client_addr); 56 | add_sip003_arg(info, "-p", info->server_port); 57 | add_sip003_arg(info, "-l", info->client_port); 58 | add_sip003_arg(info, "-k", info->password); 59 | add_sip003_arg(info, "-m", info->method); 60 | add_sip003_arg(info, "-t", info->timeout); 61 | if (info->fastopen) { 62 | info->shadowsocks_opts = string_list_append(info->shadowsocks_opts, "--fast-open"); 63 | } 64 | service->shadowsocks_cmd = info->shadowsocks_opts; 65 | dump_sip003(service); 66 | return service; 67 | } 68 | -------------------------------------------------------------------------------- /src/utils/dns.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "logger.h" 8 | #include "dns.h" 9 | 10 | char** init_dns_result(); 11 | char** add_dns_result(char **dns_result, char *str); 12 | void free_dns_result(char **dns_result); 13 | char** ipv4_dns_resolve(const char *domain); 14 | char** ipv6_dns_resolve(const char *domain); 15 | 16 | char** init_dns_result() { // 初始化DNS解析存储结构 17 | char **dns_result = (char**)malloc(sizeof(char*)); 18 | dns_result[0] = NULL; 19 | return dns_result; 20 | } 21 | 22 | char** add_dns_result(char **dns_result, char *str) { // 添加DNS解析记录 23 | int num = 0; 24 | while(dns_result[num++] != NULL); // 获取原存储个数 25 | dns_result = (char**)realloc(dns_result, sizeof(char*) * (num + 1)); 26 | dns_result[num - 1] = strcpy((char*)malloc(strlen(str) + 1), str); 27 | dns_result[num] = NULL; // 结束标志 28 | return dns_result; 29 | } 30 | 31 | void free_dns_result(char **dns_result) { // 释放DNS解析结果 32 | int num = 0; 33 | while(dns_result[num] != NULL) { // 逐个释放 34 | free(dns_result[num++]); 35 | } 36 | } 37 | 38 | char** ipv4_dns_resolve(const char *domain) { // DNS解析IPv4地址 39 | char **result = init_dns_result(); 40 | char ip_str[16]; // IPv4地址字符串 (3 * 4 + 1 * 3 + 1) 41 | struct sockaddr_in *ipv4_addr; 42 | struct addrinfo *answer, hint, *p; 43 | bzero(&hint, sizeof(hint)); // 清空为0x00 44 | hint.ai_family = AF_INET; 45 | hint.ai_socktype = SOCK_STREAM; 46 | int ret = getaddrinfo(domain, NULL, &hint, &answer); // 发起解析 47 | if (ret != 0) { // 解析失败 48 | log_debug("IPv4 DNS resolve `%s`: %s", domain, gai_strerror(ret)); 49 | return result; // 返回空数据 50 | } 51 | for (p = answer; p != NULL; p = p->ai_next) { // 遍历解析结果 52 | ipv4_addr = (struct sockaddr_in*)(p->ai_addr); // 获取IPv4地址 53 | inet_ntop(AF_INET, &ipv4_addr->sin_addr, ip_str, sizeof(ip_str)); // 转化为字符串形式 54 | result = add_dns_result(result, ip_str); 55 | } 56 | freeaddrinfo(answer); // 释放解析结果 57 | return result; 58 | } 59 | 60 | char** ipv6_dns_resolve(const char *domain) { // DNS解析IPv6地址 61 | char **result = init_dns_result(); 62 | char ip_str[40]; // IPv6地址字符串 (4 * 8 + 1 * 7 + 1) 63 | struct sockaddr_in6 *ipv6_addr; 64 | struct addrinfo *answer, hint, *p; 65 | bzero(&hint, sizeof(hint)); // 清空为0x00 66 | hint.ai_family = AF_INET6; 67 | hint.ai_socktype = SOCK_STREAM; 68 | int ret = getaddrinfo(domain, NULL, &hint, &answer); // 发起解析 69 | if (ret != 0) { // 解析失败 70 | log_debug("IPv6 DNS resolve `%s`: %s", domain, gai_strerror(ret)); 71 | return result; // 返回空数据 72 | } 73 | for (p = answer; p != NULL; p = p->ai_next) { // 遍历解析结果 74 | ipv6_addr = (struct sockaddr_in6*)(p->ai_addr); // 获取IPv6地址 75 | inet_ntop(AF_INET6, &ipv6_addr->sin6_addr, ip_str, sizeof(ip_str)); // 转化为字符串形式 76 | result = add_dns_result(result, ip_str); 77 | } 78 | freeaddrinfo(answer); // 释放解析结果 79 | return result; 80 | } 81 | 82 | char* dns_resolve(const char *domain) { // DNS解析 返回首个IP地址 IPv4优先 83 | int num = 0; 84 | char **result = ipv4_dns_resolve(domain); // IPv4解析 85 | while(result[num++] != NULL); // num - 1 为IPv4地址数 86 | if (num - 1 != 0) { // 存在IPv4解析 87 | char *tmp = strcpy((char*)malloc(strlen(result[0]) + 1), result[0]); 88 | free_dns_result(result); // 释放IPv4结果 89 | return tmp; // 返回首个IPv4地址 90 | } 91 | free_dns_result(result); // 释放IPv4结果 92 | result = ipv6_dns_resolve(domain); 93 | num = 0; 94 | while(result[num++] != NULL); // num - 1 为IPv6地址数 95 | if (num - 1 == 0) { // 无IPv6解析 96 | free_dns_result(result); // 释放IPv6结果 97 | return NULL; 98 | } 99 | char *tmp = strcpy((char*)malloc(strlen(result[0]) + 1), result[0]); 100 | free_dns_result(result); // 释放IPv6结果 101 | return tmp; // 返回首个IPv6地址 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Shadowsocks Bootstrap 2 | 3 | > A simple program to make the original shadowsocks support SIP003 plugins. 4 | 5 | ### Quick Start 6 | 7 | As we all know, the [original shadowsocks](https://github.com/shadowsocks/shadowsocks) is written in python, this version does not support [SIP003](https://shadowsocks.org/en/wiki/Plugin.html) plugin, and [shadowsocks-libev](https://github.com/shadowsocks/shadowsocks-libev) or [shadowsocks-rust](https://github.com/shadowsocks/shadowsocks-rust) which support plugin lack some encryption methods (although they are insecure), so making the original shadowsocks support SIP003 plugin will be able to get better compatibility. 8 | 9 | The SIP003 plugin runs as a subroutine. In shadowsocks-bootstrap, both the shadowsocks program and the plugin will be executed as subroutines to assign ports and manage them. 10 | 11 | You can use shadowsocks-bootstrap just like shadowsocks, the only difference is the extra `--plugin` and `--plugin-opts` options. In addition, it will use `sslocal` or `ssserver` as shadowsocks program by default, if necessary you should specify a custom filename with `--shadowsocks` option. 12 | 13 | ``` 14 | A simple program to make the original shadowsocks support SIP003 plugins. 15 | 16 | -s Host name or IP address of your remote server. 17 | -p Port number of your remote server. 18 | -b Local address to bind. 19 | -l Port number of your local server. 20 | -c Path to JSON config file. 21 | -k Password of your remote server. 22 | -m Encrypt method. 23 | -t Socket timeout in seconds. 24 | --fast-open Enable TCP fast open (with Linux kernel 3.7+). 25 | --plugin Enable SIP003 plugin. 26 | --plugin-opts Set SIP003 plugin options. 27 | --shadowsocks Set shadowsocks local or server program. 28 | --no-udp Do not use UDP proxy. 29 | --debug Enable debug mode. 30 | -h, --help Print this message. 31 | ``` 32 | 33 | You can also run via a JSON config file: 34 | 35 | ``` 36 | { 37 | "server": "...", 38 | "server_port": 8388, 39 | "local_address": "127.0.0.1", 40 | "local_port": 1080, 41 | "password": "...", 42 | "timeout": 300, 43 | "method": "aes-256-cfb", 44 | "fast_open": false, 45 | "no_udp": false, 46 | "plugin": "...", 47 | "plugin_opts": "...", 48 | "shadowsocks": "...", 49 | "extra_opts": "..." 50 | } 51 | ``` 52 | 53 | Example: 54 | 55 | ```bash 56 | shell> ss-bootstrap-server -s 0.0.0.0 -p 12345 -k dnomd343 -m aes-256-ctr --shadowsocks ss-python-server --plugin obfs-server --plugin-opts "obfs=http" 57 | shell> ss-bootstrap-local -s 127.0.0.1 -p 12345 -b 0.0.0.0 -l 1080 -k dnomd343 -m aes-256-ctr --shadowsocks ss-python-local --plugin obfs-local --plugin-opts "obfs=http;obfs-host=www.bing.com" 58 | ``` 59 | 60 | ### Compile 61 | 62 | > You need to install `gcc` , `make` and `cmake` at first. 63 | 64 |
65 | 66 | Examples 67 | 68 |
69 | 70 | ```bash 71 | # Alpine 72 | apk add build-base make cmake git 73 | git clone https://github.com/dnomd343/shadowsocks-bootstrap.git 74 | cd shadowsocks-bootstrap/ 75 | mkdir build && cd build/ 76 | cmake .. && make 77 | mv ../bin/* /usr/bin/ 78 | ``` 79 | 80 | ```bash 81 | # Ubuntu 82 | sudo apt update 83 | sudo apt install build-essential cmake git 84 | git clone https://github.com/dnomd343/shadowsocks-bootstrap.git 85 | cd shadowsocks-bootstrap/ 86 | mkdir build && cd build/ 87 | cmake .. && make 88 | sudo mv ../bin/* /usr/local/bin/ 89 | ``` 90 | 91 | ```bash 92 | # CentOS 93 | sudo yum update 94 | sudo yum groupinstall "Development Tools" 95 | sudo yum install cmake 96 | git clone https://github.com/dnomd343/shadowsocks-bootstrap.git 97 | cd shadowsocks-bootstrap/ 98 | mkdir build && cd build/ 99 | cmake .. && make 100 | sudo mv ../bin/* /usr/local/bin/ 101 | ``` 102 | 103 |
104 | 105 | ### License 106 | 107 | MIT ©2022 [@dnomd343](https://github.com/dnomd343) 108 | -------------------------------------------------------------------------------- /src/utils/process.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE // NOLINT 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "network.h" 11 | #include "process.h" 12 | #include "common.h" 13 | #include "logger.h" 14 | #include "dns.h" 15 | 16 | typedef struct { 17 | int pid; 18 | int exit_code; 19 | int exit_signal; 20 | } exit_info; 21 | 22 | char *plugin; 23 | pid_t ss_pid = 0, plugin_pid = 0; 24 | int exiting = FALSE; // sub process exiting 25 | int exited = FALSE; // all sub process exited 26 | 27 | void error_exit(); 28 | void normal_exit(); 29 | void get_sub_exit(); 30 | void kill_sub_process(); 31 | void show_exit_info(exit_info info, char *prefix); 32 | exit_info get_exit_info(int status, pid_t pid); 33 | char** load_plugin_env(sip003 *service); 34 | void process_exec(sip003 *service); 35 | 36 | void normal_exit() { // exit normally 37 | kill_sub_process(); 38 | log_info("Exit complete"); 39 | exit(0); 40 | } 41 | 42 | void error_exit() { // exit with error 43 | kill_sub_process(); 44 | log_info("Exit with error"); 45 | exit(1); 46 | } 47 | 48 | void show_exit_info(exit_info info, char *prefix) { // show info of child process death 49 | if (info.exit_code != -1) { // exit normally 50 | log_warn("%s (PID = %d) -> exit code %d", prefix, info.pid, info.exit_code); 51 | } else if (info.exit_signal != -1) { // abnormal exit 52 | log_warn("%s (PID = %d) -> killed by signal %d", prefix, info.pid, info.exit_signal); 53 | } else { 54 | log_warn("%s (PID = %d) -> unknown reason", prefix, info.pid); 55 | } 56 | } 57 | 58 | exit_info get_exit_info(int status, pid_t pid) { // get why the child process death 59 | exit_info temp; 60 | temp.pid = pid; 61 | temp.exit_code = temp.exit_signal = -1; 62 | if (WIFEXITED(status)) { // exit normally (with an exit-code) 63 | temp.exit_code = WEXITSTATUS(status); 64 | } 65 | if (WIFSIGNALED(status)) { // abnormal exit (with a signal) 66 | temp.exit_signal = WTERMSIG(status); 67 | } 68 | return temp; 69 | } 70 | 71 | void kill_sub_process() { // kill child process 72 | while (exiting) { 73 | sleep(1); // exit process already working -> block 74 | } 75 | exiting = TRUE; // exit process flag 76 | PROXY_EXIT = TRUE; // udp proxy cancel 77 | if (ss_pid != 0) { 78 | kill(ss_pid, SIGKILL); 79 | log_info("Kill shadowsocks process"); 80 | } 81 | if (plugin != NULL && plugin_pid != 0) { 82 | kill(plugin_pid, SIGKILL); 83 | log_info("Kill plugin process"); 84 | } 85 | int status; 86 | log_info("Wait child process exit"); 87 | waitpid(0, &status, 0); // block wait 88 | } 89 | 90 | void get_sub_exit() { // catch child process die 91 | exit_info sub_exit_info; 92 | int ss_ret, plugin_ret, ss_status, plugin_status; 93 | if (exiting) { 94 | int status; 95 | waitpid(0, &status, 0); 96 | return; 97 | } 98 | if (ss_pid != 0) { 99 | ss_ret = waitpid(ss_pid, &ss_status, WNOHANG); // non-blocking 100 | if (ss_ret == -1) { 101 | log_perror("Shadowsocks waitpid error -> "); 102 | error_exit(); 103 | } else if (ss_ret) { // ss exit 104 | sub_exit_info = get_exit_info(ss_status, ss_pid); 105 | show_exit_info(sub_exit_info, "Shadowsocks exit"); 106 | error_exit(); 107 | } 108 | } 109 | if (plugin != NULL && plugin_pid != 0) { // with plugin 110 | plugin_ret = waitpid(plugin_pid, &plugin_status, WNOHANG); // non-blocking 111 | if (plugin_ret == -1) { 112 | log_perror("Plugin waitpid error -> "); 113 | error_exit(); 114 | } else if (plugin_ret) { // plugin exit 115 | sub_exit_info = get_exit_info(plugin_status, plugin_pid); 116 | show_exit_info(sub_exit_info, "Plugin exit"); 117 | error_exit(); 118 | } 119 | } 120 | exited = TRUE; 121 | } 122 | 123 | char** load_plugin_env(sip003 *service) { // load plugin's environment variable 124 | char *remote_host = "SS_REMOTE_HOST="; 125 | char *remote_port = "SS_REMOTE_PORT="; 126 | char *local_host = "SS_LOCAL_HOST="; 127 | char *local_port = "SS_LOCAL_PORT="; 128 | char *plugin_options = "SS_PLUGIN_OPTIONS="; 129 | char **plugin_env = (char**)malloc(sizeof(char*) * 6); 130 | plugin_env[0] = (char*)malloc(strlen(remote_host) + strlen(service->SS_REMOTE_HOST) + 1); 131 | plugin_env[1] = (char*)malloc(strlen(remote_port) + strlen(service->SS_REMOTE_PORT) + 1); 132 | plugin_env[2] = (char*)malloc(strlen(local_host) + strlen(service->SS_LOCAL_HOST) + 1); 133 | plugin_env[3] = (char*)malloc(strlen(local_port) + strlen(service->SS_LOCAL_PORT) + 1); 134 | strcat(strcpy(plugin_env[0], remote_host), service->SS_REMOTE_HOST); 135 | strcat(strcpy(plugin_env[1], remote_port), service->SS_REMOTE_PORT); 136 | strcat(strcpy(plugin_env[2], local_host), service->SS_LOCAL_HOST); 137 | strcat(strcpy(plugin_env[3], local_port), service->SS_LOCAL_PORT); 138 | if (service->SS_PLUGIN_OPTIONS == NULL) { 139 | plugin_env[4] = NULL; 140 | } else { 141 | plugin_env[4] = (char*)malloc(strlen(plugin_options) + strlen(service->SS_PLUGIN_OPTIONS) + 1); 142 | strcat(strcpy(plugin_env[4], plugin_options), service->SS_PLUGIN_OPTIONS); 143 | } 144 | plugin_env[5] = NULL; 145 | return plugin_env; 146 | } 147 | 148 | void process_exec(sip003 *service) { // run shadowsocks main process and plugin 149 | if ((ss_pid = fork()) < 0) { 150 | log_perror("Shadowsocks fork error -> "); 151 | error_exit(); 152 | } else if (ss_pid == 0) { // child process 153 | prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father 154 | if (execvp(service->shadowsocks_cmd[0], service->shadowsocks_cmd) < 0) { 155 | log_perror("Shadowsocks exec error -> "); 156 | exit(2); 157 | } 158 | } 159 | plugin = service->plugin_file; 160 | if (plugin == NULL) { // plugin no need 161 | return; 162 | } 163 | usleep(100 * 1000); // sleep 100ms (python always a little slower) 164 | if (exiting) { // cancel plugin exec 165 | return; 166 | } 167 | if ((plugin_pid = fork()) < 0) { 168 | log_perror("Plugin fork error -> "); 169 | error_exit(); 170 | } else if (plugin_pid == 0) { // child process 171 | prctl(PR_SET_PDEATHSIG, SIGKILL); // child die with his father 172 | char **plugin_env = load_plugin_env(service); 173 | char *plugin_arg[] = { plugin, NULL }; 174 | if (execvpe(plugin, plugin_arg, plugin_env) < 0) { 175 | log_perror("Plugin exec error -> "); 176 | exit(2); 177 | } 178 | } 179 | } 180 | 181 | void start_bootstrap(int local_mode, sip003 *service) { // start shadowsocks (and plugin) 182 | log_info("Start shadowsocks bootstrap"); 183 | signal(SIGINT, normal_exit); // catch Ctrl + C (2) 184 | signal(SIGTERM, normal_exit); // catch exit signal (15) 185 | signal(SIGCHLD, get_sub_exit); // callback when child process die 186 | process_exec(service); // exec child process 187 | 188 | if (service->plugin_file == NULL || !service->is_udp_proxy) { // start udp proxy when using plugin 189 | log_info("UDP Proxy no need"); 190 | } else { 191 | char *remote_ip; 192 | if (is_ip_addr(service->SS_REMOTE_HOST)) { // remote_host -> ip address 193 | remote_ip = service->SS_REMOTE_HOST; 194 | } else { // remote_host -> domain 195 | log_info("DNS Resolve: %s", service->SS_REMOTE_HOST); 196 | remote_ip = dns_resolve(service->SS_REMOTE_HOST); // dns resolve 197 | if (remote_ip == NULL) { // no result 198 | log_warn("DNS record not found"); 199 | } else { // dns resolve success 200 | log_info("%s => %s", service->SS_REMOTE_HOST, remote_ip); 201 | } 202 | } 203 | if (remote_ip == NULL) { // resolve error 204 | log_warn("Skip UDP Proxy"); 205 | } else { // udp proxy 206 | if (local_mode) { // local mode 207 | proxy(remote_ip, atoi(service->SS_REMOTE_PORT), service->SS_LOCAL_HOST, atoi(service->SS_LOCAL_PORT)); // NOLINT 208 | } else { // server mode 209 | proxy(service->SS_LOCAL_HOST, atoi(service->SS_LOCAL_PORT), remote_ip, atoi(service->SS_REMOTE_PORT)); // NOLINT 210 | } 211 | } 212 | } 213 | while (!exited) { 214 | pause(); 215 | } 216 | normal_exit(); 217 | } 218 | -------------------------------------------------------------------------------- /src/load.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | #include "logger.h" 5 | #include "cJSON.h" 6 | #include "load.h" 7 | 8 | void init_info(bootstrap *info); 9 | void dump_info(bootstrap *info); 10 | void json_decode(char *json_content, bootstrap *info); 11 | char** add_extra_opts(char **opts, char *extra_opts_str); 12 | int add_field(char *field, char **target, char ***arg, char ***arg_limit); 13 | 14 | void init_info(bootstrap *info) { 15 | info->is_udp_proxy = TRUE; // enabled udp proxy 16 | info->server_addr = info->client_addr = NULL; 17 | info->server_port = info->client_port = NULL; 18 | info->password = NULL; 19 | info->method = NULL; 20 | info->timeout = NULL; 21 | info->fastopen = FALSE; 22 | info->plugin = NULL; 23 | info->plugin_opts = NULL; 24 | info->shadowsocks = NULL; 25 | info->shadowsocks_opts = (char**)malloc(sizeof(char*) * 2); // two arguments 26 | info->shadowsocks_opts[0] = ""; // reserved for program name 27 | info->shadowsocks_opts[1] = NULL; 28 | } 29 | 30 | void dump_info(bootstrap *info) { 31 | if (info->is_udp_proxy) { 32 | log_debug("is_udp_proxy = true"); 33 | } else { 34 | log_debug("is_udp_proxy = false"); 35 | } 36 | log_debug("server_addr = %s", info->server_addr); 37 | log_debug("client_addr = %s", info->client_addr); 38 | log_debug("server_port = %s", info->server_port); 39 | log_debug("client_port = %s", info->client_port); 40 | log_debug("password = %s", info->password); 41 | log_debug("method = %s", info->method); 42 | log_debug("timeout = %s", info->timeout); 43 | if (info->fastopen) { 44 | log_debug("fastopen = true"); 45 | } else { 46 | log_debug("fastopen = false"); 47 | } 48 | log_debug("plugin = %s", info->plugin); 49 | log_debug("plugin_opts = %s", info->plugin_opts); 50 | log_debug("shadowsocks = %s", info->shadowsocks); 51 | 52 | char *opts_str = string_list_join(info->shadowsocks_opts + 1); 53 | log_debug("shadowsocks_opts: %s", opts_str); // combine output 54 | free(opts_str); 55 | } 56 | 57 | char** add_extra_opts(char **opts, char *extra_opts_str) { // split shadowsocks extra options 58 | log_debug("Split extra options -> `%s`", extra_opts_str); 59 | char *tmp = (char*)calloc(strlen(extra_opts_str) + 1, 1); // new memory and set as 0x00 60 | for (int i = 0, num = 0;; ++num) { 61 | if (extra_opts_str[num] == '\0' || extra_opts_str[num] == ' ') { // string end or find a space 62 | tmp[i] = '\0'; 63 | if (i) { // ignore empty string 64 | i = 0; 65 | opts = string_list_append(opts, tmp); 66 | } 67 | if (extra_opts_str[num] == '\0') { // string end 68 | break; 69 | } 70 | continue; 71 | } 72 | if (extra_opts_str[num] == '\\') { // skip '\' char 73 | ++num; 74 | } 75 | tmp[i++] = extra_opts_str[num]; // copy char 76 | } 77 | return opts; 78 | } 79 | 80 | void json_decode(char *json_content, bootstrap *info) { // decode JSON content 81 | cJSON* json = cJSON_Parse(json_content); 82 | if (json == NULL) { 83 | log_fatal("JSON format error"); 84 | } 85 | json = json->child; 86 | while (json != NULL) { 87 | if (!strcmp(json->string, "no_udp")) { // no_udp => without udp proxy 88 | if (!cJSON_IsBool(json)) { 89 | log_fatal("`no_udp` must be bool"); 90 | } 91 | if (json->valueint) { // is_udp_proxy = ~(json->valueint) 92 | info->is_udp_proxy = FALSE; 93 | } else { 94 | info->is_udp_proxy = TRUE; 95 | } 96 | } else if (!strcmp(json->string, "server")) { // server => server_addr 97 | if (!cJSON_IsString(json)) { 98 | log_fatal("`server` must be string"); 99 | } 100 | if (info->server_addr != NULL) { 101 | free(info->server_addr); 102 | } 103 | info->server_addr = new_string(json->valuestring); 104 | } else if (!strcmp(json->string, "local_address")) { // local_address => client_addr 105 | if (!cJSON_IsString(json)) { 106 | log_fatal("`local_address` must be string"); 107 | } 108 | if (info->client_addr != NULL) { 109 | free(info->client_addr); 110 | } 111 | info->client_addr = new_string(json->valuestring); 112 | } else if (!strcmp(json->string, "server_port")) { // server_port => server_port 113 | if (info->server_port != NULL) { 114 | free(info->server_port); 115 | } 116 | if (cJSON_IsNumber(json)) { 117 | info->server_port = int_to_string(json->valueint); 118 | } else if (cJSON_IsString(json)) { 119 | info->server_port = new_string(json->valuestring); 120 | } else { 121 | log_fatal("`server_port` must be number or string"); 122 | } 123 | } else if (!strcmp(json->string, "local_port")) { // local_port => client_port 124 | if (info->client_port != NULL) { 125 | free(info->client_port); 126 | } 127 | if (cJSON_IsNumber(json)) { 128 | info->client_port = int_to_string(json->valueint); 129 | } else if (cJSON_IsString(json)) { 130 | info->client_port = new_string(json->valuestring); 131 | } else { 132 | log_fatal("`local_port` must be number or string"); 133 | } 134 | } else if (!strcmp(json->string, "password")) { // password => password 135 | if (!cJSON_IsString(json)) { 136 | log_fatal("`password` must be string"); 137 | } 138 | if (info->password != NULL) { 139 | free(info->password); 140 | } 141 | info->password = new_string(json->valuestring); 142 | } else if (!strcmp(json->string, "timeout")) { // timeout => timeout 143 | if (info->timeout != NULL) { 144 | free(info->timeout); 145 | } 146 | if (cJSON_IsNumber(json)) { 147 | info->timeout = int_to_string(json->valueint); 148 | } else if (cJSON_IsString(json)) { 149 | info->timeout = new_string(json->valuestring); 150 | } else { 151 | log_fatal("`timeout` must be number or string"); 152 | } 153 | } else if (!strcmp(json->string, "method")) { // method => method 154 | if (!cJSON_IsString(json)) { 155 | log_fatal("`method` must be string"); 156 | } 157 | if (info->method != NULL) { 158 | free(info->method); 159 | } 160 | info->method = new_string(json->valuestring); 161 | } else if (!strcmp(json->string, "fast_open")) { // fast_open => fastopen 162 | if (!cJSON_IsBool(json)) { 163 | log_fatal("`fast_open` must be bool"); 164 | } 165 | info->fastopen = json->valueint; 166 | } else if (!strcmp(json->string, "plugin")) { // plugin => plugin 167 | if (!cJSON_IsString(json)) { 168 | log_fatal("`plugin` must be string"); 169 | } 170 | if (info->plugin != NULL) { 171 | free(info->plugin); 172 | } 173 | info->plugin = new_string(json->valuestring); 174 | } else if (!strcmp(json->string, "plugin_opts")) { // plugin_opts => plugin_opts 175 | if (!cJSON_IsString(json)) { 176 | log_fatal("`plugin_opts` must be string"); 177 | } 178 | if (info->plugin_opts != NULL) { 179 | free(info->plugin_opts); 180 | } 181 | info->plugin_opts = new_string(json->valuestring); 182 | } else if (!strcmp(json->string, "shadowsocks")) { // shadowsocks => shadowsocks 183 | if (!cJSON_IsString(json)) { 184 | log_fatal("`shadowsocks` must be string"); 185 | } 186 | if (info->shadowsocks != NULL) { 187 | free(info->shadowsocks); 188 | } 189 | info->shadowsocks = new_string(json->valuestring); 190 | } else if (!strcmp(json->string, "extra_opts")) { // extra_opts => DECODE => shadowsocks_opts 191 | if (!cJSON_IsString(json)) { 192 | log_fatal("`extra_opts` must be string"); 193 | } 194 | info->shadowsocks_opts = add_extra_opts(info->shadowsocks_opts, json->valuestring); 195 | } else { // unknown field => ERROR 196 | log_fatal("Unknown JSON field `%s`", json->string); 197 | } 198 | json = json->next; // next field 199 | } 200 | cJSON_free(json); // free JSON struct 201 | } 202 | 203 | int add_field(char *field, char **target, char ***arg, char ***arg_limit) { 204 | if (strcmp(**arg, field) != 0) { // field not match 205 | return FALSE; 206 | } 207 | if (++(*arg) == *arg_limit) { // without next argument 208 | log_fatal("`%s` require a parameter", field); 209 | } 210 | if (*target != NULL) { 211 | free(*target); // override target field 212 | } 213 | *target = new_string(**arg); 214 | return TRUE; 215 | } 216 | 217 | bootstrap* load_info(int argc, char **argv) { // load info from input parameters 218 | bootstrap *info = (bootstrap*)malloc(sizeof(bootstrap)); 219 | char **arg_limit = argv + argc; 220 | 221 | log_debug("Start to load input arguments"); 222 | char *arg_str = (char*)malloc(0); 223 | for (char **arg = argv + 1; arg < arg_limit; ++arg) { 224 | arg_str = (char*)realloc(arg_str, strlen(arg_str) + strlen(*arg) + 4); 225 | arg_str = strcat(strcat(strcat(arg_str, "`"), *arg), "` "); 226 | } 227 | log_debug("Input arguments -> %s", arg_str); 228 | 229 | init_info(info); 230 | for (char **arg = argv + 1; arg < arg_limit; ++arg) { 231 | if (!strcmp(*arg, "--debug")) { // skip debug flag 232 | continue; 233 | } else if (!strcmp(*arg, "--fast-open")) { // --fast-open => fastopen 234 | info->fastopen = TRUE; 235 | } else if (!strcmp(*arg, "--no-udp")) { // --no-udp => is_udp_proxy 236 | info->is_udp_proxy = FALSE; 237 | } else if (!strcmp(*arg, "-c")) { // -c => CONFIG_JSON 238 | if (++arg == arg_limit) { 239 | log_fatal("Miss json file after `-c` flag"); 240 | } 241 | char *json_content = read_file(*arg); 242 | json_decode(json_content, info); // decode json content 243 | free(json_content); 244 | } else if ( 245 | !add_field("-s", &info->server_addr, &arg, &arg_limit) && 246 | !add_field("-p", &info->server_port, &arg, &arg_limit) && 247 | !add_field("-b", &info->client_addr, &arg, &arg_limit) && 248 | !add_field("-l", &info->client_port, &arg, &arg_limit) && 249 | !add_field("-k", &info->password, &arg, &arg_limit) && 250 | !add_field("-m", &info->method, &arg, &arg_limit) && 251 | !add_field("-t", &info->timeout, &arg, &arg_limit) && 252 | !add_field("--plugin", &info->plugin, &arg, &arg_limit) && 253 | !add_field("--plugin-opts", &info->plugin_opts, &arg, &arg_limit) && 254 | !add_field("--shadowsocks", &info->shadowsocks, &arg, &arg_limit) 255 | ) { // archive unknown options 256 | info->shadowsocks_opts = string_list_append(info->shadowsocks_opts, *arg); 257 | } 258 | } 259 | if (info->server_addr == NULL) { // default server address (bind address in server mode) 260 | info->server_addr = "127.0.0.1"; 261 | } 262 | dump_info(info); 263 | return info; 264 | } 265 | -------------------------------------------------------------------------------- /src/utils/network.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "network.h" 8 | #include "common.h" 9 | #include "logger.h" 10 | 11 | #define TIMEOUT 15 12 | #define BUFFER_SIZE 4096 13 | 14 | int PROXY_EXIT; 15 | 16 | int get_random_num(int range_start, int range_end); 17 | int check_port_available(unsigned int port, int is_udp, int is_ipv6); 18 | int is_ipv4_addr(char *address); 19 | int is_ipv6_addr(char *address); 20 | int create_ipv4_udp_sock(char *address, int port); 21 | int create_ipv6_udp_sock(char *address, int port); 22 | long ipv4_receive(int fd, char *buffer, int buffer_size, int timeout, struct sockaddr_in sa); 23 | long ipv6_receive(int fd, char *buffer, int buffer_size, int timeout, struct sockaddr_in6 sa); 24 | long ipv4_send_and_receive(char *ipv4_server_ip, int ipv4_server_port, char *send_buffer, long send_len, char *recv_buffer); 25 | long ipv6_send_and_receive(char *ipv6_server_ip, int ipv6_server_port, char *send_buffer, long send_len, char *recv_buffer); 26 | long send_and_receive(char *server_ip, int server_port, char *send_buffer, long send_len, char *recv_buffer); 27 | void ipv4_proxy(void *ipv4_info); 28 | void ipv6_proxy(void *ipv6_info); 29 | 30 | typedef struct ipv4_proxy_info { 31 | char *server_ip; 32 | int server_port; 33 | struct sockaddr_in ipv4_client_addr; 34 | int ipv4_client_fd; 35 | char *buffer; 36 | long len; 37 | } ipv4_proxy_info; 38 | 39 | typedef struct ipv6_proxy_info { 40 | char *server_ip; 41 | int server_port; 42 | struct sockaddr_in6 ipv6_client_addr; 43 | int ipv6_client_fd; 44 | char *buffer; 45 | long len; 46 | } ipv6_proxy_info; 47 | 48 | int get_random_num(int range_start, int range_end) { // create a random number in range 49 | struct timeval tp; 50 | gettimeofday(&tp, NULL); 51 | srand(tp.tv_usec); 52 | return range_start + (rand() % (range_end - range_start + 1)); // NOLINT (randomness is enough for us) 53 | } 54 | 55 | int check_port_available(unsigned int port, int is_udp, int is_ipv6) { // test a port is available or not 56 | int ipv4_tcp_sock, ipv4_udp_sock; 57 | int ipv6_tcp_sock, ipv6_udp_sock; 58 | struct sockaddr_in ipv4_tcp_addr, ipv4_udp_addr; 59 | struct sockaddr_in6 ipv6_tcp_addr, ipv6_udp_addr; 60 | 61 | ipv4_tcp_sock = socket(AF_INET, SOCK_STREAM, 0); 62 | bzero(&ipv4_tcp_addr, sizeof(ipv4_tcp_addr)); 63 | ipv4_tcp_addr.sin_family = AF_INET; 64 | ipv4_tcp_addr.sin_port = htons(port); 65 | ipv4_tcp_addr.sin_addr.s_addr = INADDR_ANY; 66 | if (bind(ipv4_tcp_sock, (struct sockaddr*)&ipv4_tcp_addr, sizeof(ipv4_tcp_addr)) < 0) { 67 | return FALSE; 68 | } 69 | close(ipv4_tcp_sock); 70 | 71 | if (is_udp) { // udp check 72 | ipv4_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); 73 | bzero(&ipv4_udp_addr, sizeof(ipv4_udp_addr)); 74 | ipv4_udp_addr.sin_family = AF_INET; 75 | ipv4_udp_addr.sin_port = htons(port); 76 | ipv4_udp_addr.sin_addr.s_addr = INADDR_ANY; 77 | if (bind(ipv4_udp_sock, (struct sockaddr*)&ipv4_udp_addr, sizeof(ipv4_udp_addr)) < 0) { 78 | return FALSE; 79 | } 80 | close(ipv4_udp_sock); 81 | } 82 | 83 | if (!is_ipv6) { // ipv6 ignore 84 | return TRUE; 85 | } 86 | 87 | ipv6_tcp_sock = socket(AF_INET6, SOCK_STREAM, 0); 88 | bzero(&ipv6_tcp_addr, sizeof(ipv6_tcp_addr)); 89 | ipv6_tcp_addr.sin6_family = AF_INET6; 90 | ipv6_tcp_addr.sin6_port = htons(port); 91 | ipv6_tcp_addr.sin6_addr = in6addr_any; 92 | if (bind(ipv6_tcp_sock, (struct sockaddr*)&ipv6_tcp_addr, sizeof(ipv6_tcp_addr)) < 0) { 93 | return FALSE; 94 | } 95 | close(ipv6_tcp_sock); 96 | 97 | if (is_udp) { // udp check 98 | ipv6_udp_sock = socket(AF_INET6, SOCK_DGRAM, 0); 99 | bzero(&ipv6_udp_addr, sizeof(ipv6_udp_addr)); 100 | ipv6_udp_addr.sin6_family = AF_INET6; 101 | ipv6_udp_addr.sin6_port = htons(port); 102 | ipv6_udp_addr.sin6_addr = in6addr_any; 103 | if (bind(ipv6_udp_sock, (struct sockaddr*)&ipv6_udp_addr, sizeof(ipv6_udp_addr)) < 0) { 104 | return FALSE; 105 | } 106 | close(ipv6_udp_sock); 107 | } 108 | 109 | return TRUE; 110 | } 111 | 112 | int get_available_port(unsigned short range_start, unsigned short range_end) { // get a available port 113 | unsigned short port; 114 | for (;;) { // wait until a available port in range 115 | port = get_random_num(range_start, range_end); // get a random port in range 116 | if (check_port_available(port, TRUE, TRUE)) { // port available 117 | return (int)port; 118 | } 119 | } 120 | } 121 | 122 | int is_ipv4_addr(char *address) { // 判断是否为IPv4地址 123 | if (inet_addr(address) == -1) { 124 | return FALSE; 125 | } 126 | return TRUE; 127 | } 128 | 129 | int is_ipv6_addr(char *address) { // 判断是否为IPv6地址 130 | char buf[sizeof(struct in6_addr)]; 131 | if (inet_pton(AF_INET6, address, buf) <= 0) { 132 | return FALSE; 133 | } 134 | return TRUE; 135 | } 136 | 137 | int is_ip_addr(char *address) { // 判断是否为IP地址 138 | if (is_ipv4_addr(address) || is_ipv6_addr(address)) { 139 | return TRUE; 140 | } 141 | return FALSE; 142 | } 143 | 144 | int create_ipv4_udp_sock(char *address, int port) { // 创建并绑定IPv4 UDP端口 145 | struct sockaddr_in ipv4_udp_addr; 146 | int ipv4_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); // IPv4 UDP模式 147 | bzero(&ipv4_udp_addr, sizeof(ipv4_udp_addr)); // 清空为0x00 148 | ipv4_udp_addr.sin_family = AF_INET; 149 | ipv4_udp_addr.sin_port = htons(port); // 监听端口 150 | if (address == NULL) { 151 | ipv4_udp_addr.sin_addr.s_addr = INADDR_ANY; // 监听0.0.0.0 152 | } else { 153 | ipv4_udp_addr.sin_addr.s_addr = inet_addr(address); // 监听地址 154 | } 155 | if (bind(ipv4_udp_sock, (struct sockaddr*)&ipv4_udp_addr, sizeof(ipv4_udp_addr)) < 0) { // 绑定接口 156 | log_perror("IPv4 UDP Sock bind error -> "); 157 | return -1; // 端口被占用 158 | } 159 | return ipv4_udp_sock; 160 | } 161 | 162 | int create_ipv6_udp_sock(char *address, int port) { // 创建并绑定IPv6 UDP端口 163 | struct sockaddr_in6 ipv6_udp_addr; 164 | int ipv6_udp_sock = socket(AF_INET6, SOCK_DGRAM, 0); // IPv6 UDP模式 165 | bzero(&ipv6_udp_addr, sizeof(ipv6_udp_addr)); // 清空为0x00 166 | ipv6_udp_addr.sin6_family = AF_INET6; 167 | ipv6_udp_addr.sin6_port = htons(port); // 监听端口 168 | if (address == NULL) { 169 | ipv6_udp_addr.sin6_addr = in6addr_any; // 监听:: 170 | } else { 171 | inet_pton(AF_INET6, address, &ipv6_udp_addr.sin6_addr); // 监听地址 172 | } 173 | if (bind(ipv6_udp_sock, (struct sockaddr*)&ipv6_udp_addr, sizeof(ipv6_udp_addr)) < 0) { // 绑定接口 174 | log_perror("IPv6 UDP Sock bind error -> "); 175 | return -1; // 端口被占用 176 | } 177 | return ipv6_udp_sock; 178 | } 179 | 180 | long ipv4_receive(int fd, char *buffer, int buffer_size, int timeout, struct sockaddr_in sa) { // IPv4接收 超时处理 181 | socklen_t sa_len = sizeof(sa); 182 | if (timeout == 0) { // 永久等待 183 | return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); 184 | } 185 | fd_set rfds; 186 | struct timeval tv; 187 | tv.tv_sec = timeout; // 超时时间 单位s 188 | tv.tv_usec = 0; 189 | FD_ZERO(&rfds); 190 | FD_SET(fd, &rfds); 191 | select(fd + 1, &rfds, (fd_set*)0, (fd_set*)0, &tv); 192 | if (FD_ISSET(fd, &rfds)) { 193 | return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); 194 | } 195 | return -1; // 接收超时 196 | } 197 | 198 | long ipv6_receive(int fd, char *buffer, int buffer_size, int timeout, struct sockaddr_in6 sa) { // IPv6接收 超时处理 199 | socklen_t sa_len = sizeof(sa); 200 | if (timeout == 0) { // 永久等待 201 | return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); 202 | } 203 | fd_set rfds; 204 | struct timeval tv; 205 | tv.tv_sec = timeout; // 超时时间 单位s 206 | tv.tv_usec = 0; 207 | FD_ZERO(&rfds); 208 | FD_SET(fd, &rfds); 209 | select(fd + 1, &rfds, (fd_set*)0, (fd_set*)0, &tv); 210 | if (FD_ISSET(fd, &rfds)) { 211 | return recvfrom(fd, buffer, buffer_size, 0, (struct sockaddr*)&sa, &sa_len); 212 | } 213 | return -1; // 接收超时 214 | } 215 | 216 | long ipv4_send_and_receive(char *ipv4_server_ip, int ipv4_server_port, char *send_buffer, long send_len, char *recv_buffer) { // IPv4下发送并接收 217 | struct sockaddr_in ipv4_server_addr; 218 | int ipv4_server_fd = socket(AF_INET, SOCK_DGRAM, 0); // 通讯描述符 219 | bzero(&ipv4_server_addr, sizeof(ipv4_server_addr)); // 清空为0x00 220 | ipv4_server_addr.sin_family = AF_INET; 221 | ipv4_server_addr.sin_port = htons(ipv4_server_port); // 目标端口 222 | ipv4_server_addr.sin_addr.s_addr = inet_addr(ipv4_server_ip); // 目标IP 223 | if (sendto(ipv4_server_fd, send_buffer, send_len, 0, (struct sockaddr*)&ipv4_server_addr, sizeof(ipv4_server_addr)) < 0) { // 发送缓冲区数据 224 | log_perror("IPv4 UDP send failed -> "); 225 | } 226 | long recv_len = ipv4_receive(ipv4_server_fd, recv_buffer, BUFFER_SIZE, TIMEOUT, ipv4_server_addr); // 接收数据到缓冲区 227 | close(ipv4_server_fd); // 关闭描述符 228 | return recv_len; // 返回接收长度 229 | } 230 | 231 | long ipv6_send_and_receive(char *ipv6_server_ip, int ipv6_server_port, char *send_buffer, long send_len, char *recv_buffer) { // IPv6下发送并接收 232 | struct sockaddr_in6 ipv6_server_addr; 233 | int ipv6_server_fd = socket(AF_INET6, SOCK_DGRAM, 0); // 通讯描述符 234 | bzero(&ipv6_server_addr, sizeof(ipv6_server_addr)); // 清空为0x00 235 | ipv6_server_addr.sin6_family = AF_INET6; 236 | ipv6_server_addr.sin6_port = htons(ipv6_server_port); // 目标端口 237 | inet_pton(AF_INET6, ipv6_server_ip, &ipv6_server_addr.sin6_addr); // 目标IP 238 | if (sendto(ipv6_server_fd, send_buffer, send_len, 0, (struct sockaddr*)&ipv6_server_addr, sizeof(ipv6_server_addr)) < 0) { // 发送缓冲区数据 239 | log_perror("IPv6 UDP send failed -> "); 240 | } 241 | long recv_len = ipv6_receive(ipv6_server_fd, recv_buffer, BUFFER_SIZE, TIMEOUT, ipv6_server_addr); // 接收数据到缓冲区 242 | close(ipv6_server_fd); // 关闭描述符 243 | return recv_len; // 返回接收长度 244 | } 245 | 246 | long send_and_receive(char *server_ip, int server_port, char *send_buffer, long send_len, char *recv_buffer) { // IPv4 / IPv6 双栈发送并接收 247 | if (is_ipv6_addr(server_ip)) { // IPv6 server 248 | return ipv6_send_and_receive(server_ip, server_port, send_buffer, send_len, recv_buffer); 249 | } else { // IPv4 (server_ip must be IPv4 or IPv6) 250 | return ipv4_send_and_receive(server_ip, server_port, send_buffer, send_len, recv_buffer); 251 | } 252 | } 253 | 254 | void ipv4_proxy(void *ipv4_info) { // 代理IPv4客户端 255 | ipv4_proxy_info *info = (ipv4_proxy_info*)ipv4_info; 256 | char *recv_buffer = (char*)malloc(BUFFER_SIZE); // 申请接收缓冲区内存 257 | long recv_len = send_and_receive(info->server_ip, info->server_port, info->buffer, info->len, recv_buffer); // 服务端交互 258 | if (recv_len < 0) { // 服务端超时 259 | log_warn("UDP Proxy: server return timeout"); 260 | } else { 261 | if (sendto(info->ipv4_client_fd, recv_buffer, recv_len, 0, (struct sockaddr*)&(info->ipv4_client_addr), sizeof(info->ipv4_client_addr)) < 0) { // 服务端数据返回给客户端 262 | log_perror("IPv4 UDP return failed -> "); 263 | } else { 264 | log_info("UDP Proxy: ↑ %ld bytes ↓ %ld bytes", info->len, recv_len); 265 | } 266 | } 267 | free(recv_buffer); // 释放接收缓冲区内存 268 | free(info->buffer); // 释放发送缓冲区内存 269 | free(ipv4_info); // 释放线程传参结构体 270 | } 271 | 272 | void ipv6_proxy(void *ipv6_info) { // 代理IPv6客户端 273 | ipv6_proxy_info *info = (ipv6_proxy_info*)ipv6_info; 274 | char *recv_buffer = (char*)malloc(BUFFER_SIZE); // 申请接收缓冲区内存 275 | long recv_len = send_and_receive(info->server_ip, info->server_port, info->buffer, info->len, recv_buffer); // 服务端交互 276 | if (recv_len < 0) { // 服务端超时 277 | log_warn("UDP Proxy: Server return timeout"); 278 | } else { 279 | if (sendto(info->ipv6_client_fd, recv_buffer, recv_len, 0, (struct sockaddr*)&(info->ipv6_client_addr), sizeof(info->ipv6_client_addr)) < 0) { // 服务端数据返回给客户端 280 | log_perror("IPv6 UDP return failed -> "); 281 | } else { 282 | log_info("UDP Proxy: ↑ %ld bytes ↓ %ld bytes", info->len, recv_len); 283 | } 284 | } 285 | free(recv_buffer); // 释放接收缓冲区内存 286 | free(info->buffer); // 释放发送缓冲区内存 287 | free(ipv6_info); // 释放线程传参结构体 288 | } 289 | 290 | void proxy(char *server_ip, int server_port, char *listen_ip, int listen_port) { // 代理UDP请求 291 | pthread_t tid; 292 | long recv_len; 293 | char recv_buffer[BUFFER_SIZE]; // 接收缓冲区 294 | int ipv4_client_fd, ipv6_client_fd; 295 | struct sockaddr_in ipv4_client_addr; 296 | struct sockaddr_in6 ipv6_client_addr; 297 | socklen_t ipv4_client_addr_len = sizeof(ipv4_client_addr); 298 | socklen_t ipv6_client_addr_len = sizeof(ipv6_client_addr); 299 | int bind_error_flag = FALSE; 300 | int is_listen_ipv6 = is_ipv6_addr(listen_ip); // 判断监听地址是否为IPv6 301 | if (!is_listen_ipv6) { // IPv4客户端 302 | ipv4_client_fd = create_ipv4_udp_sock(listen_ip, listen_port); // 监听端口描述符 303 | if (ipv4_client_fd == -1) { // 端口监听失败 304 | bind_error_flag = TRUE; 305 | } 306 | } else { // IPv6客户端 307 | ipv6_client_fd = create_ipv6_udp_sock(listen_ip, listen_port); // 监听端口描述符 308 | if (ipv6_client_fd == -1) { // 端口监听失败 309 | bind_error_flag = TRUE; 310 | } 311 | } 312 | if (bind_error_flag) { // 端口被占用 313 | log_warn("The UDP port seems to be occupied by the SIP003 plugin"); 314 | log_warn("UDP communication of the agent will not work properly"); 315 | return; 316 | } 317 | PROXY_EXIT = FALSE; // 重置退出标识 318 | log_info("UDP Proxy: %s:%d -> %s:%d", listen_ip, listen_port, server_ip, server_port); 319 | for (;;) { 320 | if (!is_listen_ipv6) { // IPv4客户端 321 | recv_len = recvfrom(ipv4_client_fd, recv_buffer, BUFFER_SIZE, 0, (struct sockaddr*)&ipv4_client_addr, &ipv4_client_addr_len); 322 | char *proxy_buffer = (char *) malloc(recv_len); 323 | memcpy(proxy_buffer, recv_buffer, recv_len); // 复制缓冲区数据 324 | ipv4_proxy_info *info = (ipv4_proxy_info *) malloc(sizeof(ipv4_proxy_info)); 325 | info->server_ip = server_ip; 326 | info->server_port = server_port; 327 | info->ipv4_client_addr = ipv4_client_addr; 328 | info->ipv4_client_fd = ipv4_client_fd; 329 | info->buffer = proxy_buffer; 330 | info->len = recv_len; 331 | pthread_create(&tid, NULL, (void*)ipv4_proxy, (void*)info); // 新线程代理请求 332 | } else { // IPv6客户端 333 | recv_len = recvfrom(ipv6_client_fd, recv_buffer, BUFFER_SIZE, 0, (struct sockaddr*)&ipv6_client_addr, &ipv6_client_addr_len); 334 | char *proxy_buffer = (char*)malloc(recv_len); 335 | memcpy(proxy_buffer, recv_buffer, recv_len); // 复制缓冲区数据 336 | ipv6_proxy_info *info = (ipv6_proxy_info*)malloc(sizeof(ipv6_proxy_info)); 337 | info->server_ip = server_ip; 338 | info->server_port = server_port; 339 | info->ipv6_client_addr = ipv6_client_addr; 340 | info->ipv6_client_fd = ipv6_client_fd; 341 | info->buffer = proxy_buffer; 342 | info->len = recv_len; 343 | pthread_create(&tid, NULL, (void*)ipv6_proxy, (void*)info); // 新线程代理请求 344 | } 345 | if (PROXY_EXIT) { 346 | break; // 退出代理 347 | } 348 | } 349 | sleep(TIMEOUT); // 等待线程结束 350 | if (!is_listen_ipv6) { // IPv4客户端 351 | close(ipv4_client_fd); // 关闭监听 352 | } else { // IPv6客户端 353 | close(ipv6_client_fd); // 关闭监听 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /include/utils/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) 32 | #define __WINDOWS__ 33 | #endif 34 | 35 | #ifdef __WINDOWS__ 36 | 37 | /* 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: 38 | 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol 42 | 43 | For *nix builds that support visibility attribute, you can define similar behavior by 44 | 45 | setting default visibility to hidden by adding 46 | -fvisibility=hidden (for gcc) 47 | or 48 | -xldscope=hidden (for sun cc) 49 | to CFLAGS 50 | 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does 52 | 53 | */ 54 | 55 | #define CJSON_CDECL __cdecl 56 | #define CJSON_STDCALL __stdcall 57 | 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) 60 | #define CJSON_EXPORT_SYMBOLS 61 | #endif 62 | 63 | #if defined(CJSON_HIDE_SYMBOLS) 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL 65 | #elif defined(CJSON_EXPORT_SYMBOLS) 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL 67 | #elif defined(CJSON_IMPORT_SYMBOLS) 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL 69 | #endif 70 | #else /* !__WINDOWS__ */ 71 | #define CJSON_CDECL 72 | #define CJSON_STDCALL 73 | 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type 76 | #else 77 | #define CJSON_PUBLIC(type) type 78 | #endif 79 | #endif 80 | 81 | /* project version */ 82 | #define CJSON_VERSION_MAJOR 1 83 | #define CJSON_VERSION_MINOR 7 84 | #define CJSON_VERSION_PATCH 15 85 | 86 | #include 87 | 88 | /* cJSON Types: */ 89 | #define cJSON_Invalid (0) 90 | #define cJSON_False (1 << 0) 91 | #define cJSON_True (1 << 1) 92 | #define cJSON_NULL (1 << 2) 93 | #define cJSON_Number (1 << 3) 94 | #define cJSON_String (1 << 4) 95 | #define cJSON_Array (1 << 5) 96 | #define cJSON_Object (1 << 6) 97 | #define cJSON_Raw (1 << 7) /* raw json */ 98 | 99 | #define cJSON_IsReference 256 100 | #define cJSON_StringIsConst 512 101 | 102 | /* The cJSON structure: */ 103 | typedef struct cJSON 104 | { 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 106 | struct cJSON *next; 107 | struct cJSON *prev; 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 109 | struct cJSON *child; 110 | 111 | /* The type of the item, as above. */ 112 | int type; 113 | 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ 115 | char *valuestring; 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 117 | int valueint; 118 | /* The item's number, if type==cJSON_Number */ 119 | double valuedouble; 120 | 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 122 | char *string; 123 | } cJSON; 124 | 125 | typedef struct cJSON_Hooks 126 | { 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); 129 | void (CJSON_CDECL *free_fn)(void *ptr); 130 | } cJSON_Hooks; 131 | 132 | typedef int cJSON_bool; 133 | 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. 135 | * This is to prevent stack overflows. */ 136 | #ifndef CJSON_NESTING_LIMIT 137 | #define CJSON_NESTING_LIMIT 1000 138 | #endif 139 | 140 | /* returns the version of cJSON as a string */ 141 | CJSON_PUBLIC(const char*) cJSON_Version(void); 142 | 143 | /* Supply malloc, realloc and free functions to cJSON */ 144 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); 145 | 146 | /* 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. */ 147 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ 148 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); 149 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); 150 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 151 | /* 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(). */ 152 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); 153 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); 154 | 155 | /* Render a cJSON entity to text for transfer/storage. */ 156 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); 157 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ 158 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); 159 | /* 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 */ 160 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); 161 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ 162 | /* 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 */ 163 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); 164 | /* Delete a cJSON entity and all subentities. */ 165 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); 166 | 167 | /* Returns the number of items in an array (or object). */ 168 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); 169 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ 170 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); 171 | /* Get item "string" from object. Case insensitive. */ 172 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); 173 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); 174 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); 175 | /* 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. */ 176 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); 177 | 178 | /* Check item type and return its value */ 179 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); 180 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); 181 | 182 | /* These functions check the type of an item */ 183 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 184 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 185 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 186 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 187 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 188 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 190 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 191 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 192 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); 193 | 194 | /* These calls create a cJSON item of the appropriate type. */ 195 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 196 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 197 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 198 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 199 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 200 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 201 | /* raw json */ 202 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 203 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 204 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 205 | 206 | /* Create a string where valuestring references a string so 207 | * it will not be freed by cJSON_Delete */ 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 209 | /* Create an object/array that only references it's elements so 210 | * they will not be freed by cJSON_Delete */ 211 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 212 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 213 | 214 | /* These utilities create an Array of count items. 215 | * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ 216 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 217 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 218 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 219 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); 220 | 221 | /* Append item to the specified array/object. */ 222 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); 223 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 224 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. 225 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before 226 | * writing to `item->string` */ 227 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); 228 | /* 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. */ 229 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 230 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 231 | 232 | /* Remove/Detach items from Arrays/Objects. */ 233 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); 234 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); 235 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); 236 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); 237 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); 238 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); 239 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); 240 | 241 | /* Update array items. */ 242 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 243 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); 244 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 245 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 246 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); 247 | 248 | /* Duplicate a cJSON item */ 249 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); 250 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 251 | * need to be released. With recurse!=0, it will duplicate any children connected to the item. 252 | * The item->next and ->prev pointers are always zero on return from Duplicate. */ 253 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. 254 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ 255 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); 256 | 257 | /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. 258 | * The input pointer json cannot point to a read-only address area, such as a string constant, 259 | * but should point to a readable and writable address area. */ 260 | CJSON_PUBLIC(void) cJSON_Minify(char *json); 261 | 262 | /* Helper functions for creating and adding items to an object at the same time. 263 | * They return the added item or NULL on failure. */ 264 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); 265 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); 266 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); 267 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); 268 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); 269 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); 270 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); 271 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); 272 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); 273 | 274 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 275 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) 276 | /* helper for the cJSON_SetNumberValue macro */ 277 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 278 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 279 | /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ 280 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); 281 | 282 | /* Macro for iterating over an array or object */ 283 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) 284 | 285 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ 286 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); 287 | CJSON_PUBLIC(void) cJSON_free(void *object); 288 | 289 | #ifdef __cplusplus 290 | } 291 | #endif 292 | 293 | #endif 294 | -------------------------------------------------------------------------------- /src/utils/cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | /* disable warnings about old C89 functions in MSVC */ 27 | #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) 28 | #define _CRT_SECURE_NO_DEPRECATE 29 | #endif 30 | 31 | #ifdef __GNUC__ 32 | #pragma GCC visibility push(default) 33 | #endif 34 | #if defined(_MSC_VER) 35 | #pragma warning (push) 36 | /* disable warning about single line comments in system headers */ 37 | #pragma warning (disable : 4001) 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #ifdef ENABLE_LOCALES 49 | #include 50 | #endif 51 | 52 | #if defined(_MSC_VER) 53 | #pragma warning (pop) 54 | #endif 55 | #ifdef __GNUC__ 56 | #pragma GCC visibility pop 57 | #endif 58 | 59 | #include "cJSON.h" 60 | 61 | /* define our own boolean type */ 62 | #ifdef true 63 | #undef true 64 | #endif 65 | #define true ((cJSON_bool)1) 66 | 67 | #ifdef false 68 | #undef false 69 | #endif 70 | #define false ((cJSON_bool)0) 71 | 72 | /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ 73 | #ifndef isinf 74 | #define isinf(d) (isnan((d - d)) && !isnan(d)) 75 | #endif 76 | #ifndef isnan 77 | #define isnan(d) (d != d) 78 | #endif 79 | 80 | #ifndef NAN 81 | #ifdef _WIN32 82 | #define NAN sqrt(-1.0) 83 | #else 84 | #define NAN 0.0/0.0 85 | #endif 86 | #endif 87 | 88 | typedef struct { 89 | const unsigned char *json; 90 | size_t position; 91 | } error; 92 | static error global_error = { NULL, 0 }; 93 | 94 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) 95 | { 96 | return (const char*) (global_error.json + global_error.position); 97 | } 98 | 99 | CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) 100 | { 101 | if (!cJSON_IsString(item)) 102 | { 103 | return NULL; 104 | } 105 | 106 | return item->valuestring; 107 | } 108 | 109 | CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) 110 | { 111 | if (!cJSON_IsNumber(item)) 112 | { 113 | return (double) NAN; 114 | } 115 | 116 | return item->valuedouble; 117 | } 118 | 119 | /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ 120 | #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) 121 | #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. 122 | #endif 123 | 124 | CJSON_PUBLIC(const char*) cJSON_Version(void) 125 | { 126 | static char version[15]; 127 | sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); 128 | 129 | return version; 130 | } 131 | 132 | /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ 133 | static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) 134 | { 135 | if ((string1 == NULL) || (string2 == NULL)) 136 | { 137 | return 1; 138 | } 139 | 140 | if (string1 == string2) 141 | { 142 | return 0; 143 | } 144 | 145 | for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) 146 | { 147 | if (*string1 == '\0') 148 | { 149 | return 0; 150 | } 151 | } 152 | 153 | return tolower(*string1) - tolower(*string2); 154 | } 155 | 156 | typedef struct internal_hooks 157 | { 158 | void *(CJSON_CDECL *allocate)(size_t size); 159 | void (CJSON_CDECL *deallocate)(void *pointer); 160 | void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); 161 | } internal_hooks; 162 | 163 | #if defined(_MSC_VER) 164 | /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ 165 | static void * CJSON_CDECL internal_malloc(size_t size) 166 | { 167 | return malloc(size); 168 | } 169 | static void CJSON_CDECL internal_free(void *pointer) 170 | { 171 | free(pointer); 172 | } 173 | static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) 174 | { 175 | return realloc(pointer, size); 176 | } 177 | #else 178 | #define internal_malloc malloc 179 | #define internal_free free 180 | #define internal_realloc realloc 181 | #endif 182 | 183 | /* strlen of character literals resolved at compile time */ 184 | #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) 185 | 186 | static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; 187 | 188 | static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) 189 | { 190 | size_t length = 0; 191 | unsigned char *copy = NULL; 192 | 193 | if (string == NULL) 194 | { 195 | return NULL; 196 | } 197 | 198 | length = strlen((const char*)string) + sizeof(""); 199 | copy = (unsigned char*)hooks->allocate(length); 200 | if (copy == NULL) 201 | { 202 | return NULL; 203 | } 204 | memcpy(copy, string, length); 205 | 206 | return copy; 207 | } 208 | 209 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) 210 | { 211 | if (hooks == NULL) 212 | { 213 | /* Reset hooks */ 214 | global_hooks.allocate = malloc; 215 | global_hooks.deallocate = free; 216 | global_hooks.reallocate = realloc; 217 | return; 218 | } 219 | 220 | global_hooks.allocate = malloc; 221 | if (hooks->malloc_fn != NULL) 222 | { 223 | global_hooks.allocate = hooks->malloc_fn; 224 | } 225 | 226 | global_hooks.deallocate = free; 227 | if (hooks->free_fn != NULL) 228 | { 229 | global_hooks.deallocate = hooks->free_fn; 230 | } 231 | 232 | /* use realloc only if both free and malloc are used */ 233 | global_hooks.reallocate = NULL; 234 | if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) 235 | { 236 | global_hooks.reallocate = realloc; 237 | } 238 | } 239 | 240 | /* Internal constructor. */ 241 | static cJSON *cJSON_New_Item(const internal_hooks * const hooks) 242 | { 243 | cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); 244 | if (node) 245 | { 246 | memset(node, '\0', sizeof(cJSON)); 247 | } 248 | 249 | return node; 250 | } 251 | 252 | /* Delete a cJSON structure. */ 253 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) 254 | { 255 | cJSON *next = NULL; 256 | while (item != NULL) 257 | { 258 | next = item->next; 259 | if (!(item->type & cJSON_IsReference) && (item->child != NULL)) 260 | { 261 | cJSON_Delete(item->child); 262 | } 263 | if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) 264 | { 265 | global_hooks.deallocate(item->valuestring); 266 | } 267 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 268 | { 269 | global_hooks.deallocate(item->string); 270 | } 271 | global_hooks.deallocate(item); 272 | item = next; 273 | } 274 | } 275 | 276 | /* get the decimal point character of the current locale */ 277 | static unsigned char get_decimal_point(void) 278 | { 279 | #ifdef ENABLE_LOCALES 280 | struct lconv *lconv = localeconv(); 281 | return (unsigned char) lconv->decimal_point[0]; 282 | #else 283 | return '.'; 284 | #endif 285 | } 286 | 287 | typedef struct 288 | { 289 | const unsigned char *content; 290 | size_t length; 291 | size_t offset; 292 | size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ 293 | internal_hooks hooks; 294 | } parse_buffer; 295 | 296 | /* check if the given size is left to read in a given parse buffer (starting with 1) */ 297 | #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) 298 | /* check if the buffer can be accessed at the given index (starting with 0) */ 299 | #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) 300 | #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) 301 | /* get a pointer to the buffer at the position */ 302 | #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) 303 | 304 | /* Parse the input text to generate a number, and populate the result into item. */ 305 | static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) 306 | { 307 | double number = 0; 308 | unsigned char *after_end = NULL; 309 | unsigned char number_c_string[64]; 310 | unsigned char decimal_point = get_decimal_point(); 311 | size_t i = 0; 312 | 313 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 314 | { 315 | return false; 316 | } 317 | 318 | /* copy the number into a temporary buffer and replace '.' with the decimal point 319 | * of the current locale (for strtod) 320 | * This also takes care of '\0' not necessarily being available for marking the end of the input */ 321 | for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) 322 | { 323 | switch (buffer_at_offset(input_buffer)[i]) 324 | { 325 | case '0': 326 | case '1': 327 | case '2': 328 | case '3': 329 | case '4': 330 | case '5': 331 | case '6': 332 | case '7': 333 | case '8': 334 | case '9': 335 | case '+': 336 | case '-': 337 | case 'e': 338 | case 'E': 339 | number_c_string[i] = buffer_at_offset(input_buffer)[i]; 340 | break; 341 | 342 | case '.': 343 | number_c_string[i] = decimal_point; 344 | break; 345 | 346 | default: 347 | goto loop_end; 348 | } 349 | } 350 | loop_end: 351 | number_c_string[i] = '\0'; 352 | 353 | number = strtod((const char*)number_c_string, (char**)&after_end); 354 | if (number_c_string == after_end) 355 | { 356 | return false; /* parse_error */ 357 | } 358 | 359 | item->valuedouble = number; 360 | 361 | /* use saturation in case of overflow */ 362 | if (number >= INT_MAX) 363 | { 364 | item->valueint = INT_MAX; 365 | } 366 | else if (number <= (double)INT_MIN) 367 | { 368 | item->valueint = INT_MIN; 369 | } 370 | else 371 | { 372 | item->valueint = (int)number; 373 | } 374 | 375 | item->type = cJSON_Number; 376 | 377 | input_buffer->offset += (size_t)(after_end - number_c_string); 378 | return true; 379 | } 380 | 381 | /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ 382 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) 383 | { 384 | if (number >= INT_MAX) 385 | { 386 | object->valueint = INT_MAX; 387 | } 388 | else if (number <= (double)INT_MIN) 389 | { 390 | object->valueint = INT_MIN; 391 | } 392 | else 393 | { 394 | object->valueint = (int)number; 395 | } 396 | 397 | return object->valuedouble = number; 398 | } 399 | 400 | CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) 401 | { 402 | char *copy = NULL; 403 | /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ 404 | if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) 405 | { 406 | return NULL; 407 | } 408 | if (strlen(valuestring) <= strlen(object->valuestring)) 409 | { 410 | strcpy(object->valuestring, valuestring); 411 | return object->valuestring; 412 | } 413 | copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); 414 | if (copy == NULL) 415 | { 416 | return NULL; 417 | } 418 | if (object->valuestring != NULL) 419 | { 420 | cJSON_free(object->valuestring); 421 | } 422 | object->valuestring = copy; 423 | 424 | return copy; 425 | } 426 | 427 | typedef struct 428 | { 429 | unsigned char *buffer; 430 | size_t length; 431 | size_t offset; 432 | size_t depth; /* current nesting depth (for formatted printing) */ 433 | cJSON_bool noalloc; 434 | cJSON_bool format; /* is this print a formatted print */ 435 | internal_hooks hooks; 436 | } printbuffer; 437 | 438 | /* realloc printbuffer if necessary to have at least "needed" bytes more */ 439 | static unsigned char* ensure(printbuffer * const p, size_t needed) 440 | { 441 | unsigned char *newbuffer = NULL; 442 | size_t newsize = 0; 443 | 444 | if ((p == NULL) || (p->buffer == NULL)) 445 | { 446 | return NULL; 447 | } 448 | 449 | if ((p->length > 0) && (p->offset >= p->length)) 450 | { 451 | /* make sure that offset is valid */ 452 | return NULL; 453 | } 454 | 455 | if (needed > INT_MAX) 456 | { 457 | /* sizes bigger than INT_MAX are currently not supported */ 458 | return NULL; 459 | } 460 | 461 | needed += p->offset + 1; 462 | if (needed <= p->length) 463 | { 464 | return p->buffer + p->offset; 465 | } 466 | 467 | if (p->noalloc) { 468 | return NULL; 469 | } 470 | 471 | /* calculate new buffer size */ 472 | if (needed > (INT_MAX / 2)) 473 | { 474 | /* overflow of int, use INT_MAX if possible */ 475 | if (needed <= INT_MAX) 476 | { 477 | newsize = INT_MAX; 478 | } 479 | else 480 | { 481 | return NULL; 482 | } 483 | } 484 | else 485 | { 486 | newsize = needed * 2; 487 | } 488 | 489 | if (p->hooks.reallocate != NULL) 490 | { 491 | /* reallocate with realloc if available */ 492 | newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); 493 | if (newbuffer == NULL) 494 | { 495 | p->hooks.deallocate(p->buffer); 496 | p->length = 0; 497 | p->buffer = NULL; 498 | 499 | return NULL; 500 | } 501 | } 502 | else 503 | { 504 | /* otherwise reallocate manually */ 505 | newbuffer = (unsigned char*)p->hooks.allocate(newsize); 506 | if (!newbuffer) 507 | { 508 | p->hooks.deallocate(p->buffer); 509 | p->length = 0; 510 | p->buffer = NULL; 511 | 512 | return NULL; 513 | } 514 | 515 | memcpy(newbuffer, p->buffer, p->offset + 1); 516 | p->hooks.deallocate(p->buffer); 517 | } 518 | p->length = newsize; 519 | p->buffer = newbuffer; 520 | 521 | return newbuffer + p->offset; 522 | } 523 | 524 | /* calculate the new length of the string in a printbuffer and update the offset */ 525 | static void update_offset(printbuffer * const buffer) 526 | { 527 | const unsigned char *buffer_pointer = NULL; 528 | if ((buffer == NULL) || (buffer->buffer == NULL)) 529 | { 530 | return; 531 | } 532 | buffer_pointer = buffer->buffer + buffer->offset; 533 | 534 | buffer->offset += strlen((const char*)buffer_pointer); 535 | } 536 | 537 | /* securely comparison of floating-point variables */ 538 | static cJSON_bool compare_double(double a, double b) 539 | { 540 | double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); 541 | return (fabs(a - b) <= maxVal * DBL_EPSILON); 542 | } 543 | 544 | /* Render the number nicely from the given item into a string. */ 545 | static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) 546 | { 547 | unsigned char *output_pointer = NULL; 548 | double d = item->valuedouble; 549 | int length = 0; 550 | size_t i = 0; 551 | unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ 552 | unsigned char decimal_point = get_decimal_point(); 553 | double test = 0.0; 554 | 555 | if (output_buffer == NULL) 556 | { 557 | return false; 558 | } 559 | 560 | /* This checks for NaN and Infinity */ 561 | if (isnan(d) || isinf(d)) 562 | { 563 | length = sprintf((char*)number_buffer, "null"); 564 | } 565 | else 566 | { 567 | /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ 568 | length = sprintf((char*)number_buffer, "%1.15g", d); 569 | 570 | /* Check whether the original double can be recovered */ 571 | if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) 572 | { 573 | /* If not, print with 17 decimal places of precision */ 574 | length = sprintf((char*)number_buffer, "%1.17g", d); 575 | } 576 | } 577 | 578 | /* sprintf failed or buffer overrun occurred */ 579 | if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) 580 | { 581 | return false; 582 | } 583 | 584 | /* reserve appropriate space in the output */ 585 | output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); 586 | if (output_pointer == NULL) 587 | { 588 | return false; 589 | } 590 | 591 | /* copy the printed number to the output and replace locale 592 | * dependent decimal point with '.' */ 593 | for (i = 0; i < ((size_t)length); i++) 594 | { 595 | if (number_buffer[i] == decimal_point) 596 | { 597 | output_pointer[i] = '.'; 598 | continue; 599 | } 600 | 601 | output_pointer[i] = number_buffer[i]; 602 | } 603 | output_pointer[i] = '\0'; 604 | 605 | output_buffer->offset += (size_t)length; 606 | 607 | return true; 608 | } 609 | 610 | /* parse 4 digit hexadecimal number */ 611 | static unsigned parse_hex4(const unsigned char * const input) 612 | { 613 | unsigned int h = 0; 614 | size_t i = 0; 615 | 616 | for (i = 0; i < 4; i++) 617 | { 618 | /* parse digit */ 619 | if ((input[i] >= '0') && (input[i] <= '9')) 620 | { 621 | h += (unsigned int) input[i] - '0'; 622 | } 623 | else if ((input[i] >= 'A') && (input[i] <= 'F')) 624 | { 625 | h += (unsigned int) 10 + input[i] - 'A'; 626 | } 627 | else if ((input[i] >= 'a') && (input[i] <= 'f')) 628 | { 629 | h += (unsigned int) 10 + input[i] - 'a'; 630 | } 631 | else /* invalid */ 632 | { 633 | return 0; 634 | } 635 | 636 | if (i < 3) 637 | { 638 | /* shift left to make place for the next nibble */ 639 | h = h << 4; 640 | } 641 | } 642 | 643 | return h; 644 | } 645 | 646 | /* converts a UTF-16 literal to UTF-8 647 | * A literal can be one or two sequences of the form \uXXXX */ 648 | static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) 649 | { 650 | long unsigned int codepoint = 0; 651 | unsigned int first_code = 0; 652 | const unsigned char *first_sequence = input_pointer; 653 | unsigned char utf8_length = 0; 654 | unsigned char utf8_position = 0; 655 | unsigned char sequence_length = 0; 656 | unsigned char first_byte_mark = 0; 657 | 658 | if ((input_end - first_sequence) < 6) 659 | { 660 | /* input ends unexpectedly */ 661 | goto fail; 662 | } 663 | 664 | /* get the first utf16 sequence */ 665 | first_code = parse_hex4(first_sequence + 2); 666 | 667 | /* check that the code is valid */ 668 | if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) 669 | { 670 | goto fail; 671 | } 672 | 673 | /* UTF16 surrogate pair */ 674 | if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) 675 | { 676 | const unsigned char *second_sequence = first_sequence + 6; 677 | unsigned int second_code = 0; 678 | sequence_length = 12; /* \uXXXX\uXXXX */ 679 | 680 | if ((input_end - second_sequence) < 6) 681 | { 682 | /* input ends unexpectedly */ 683 | goto fail; 684 | } 685 | 686 | if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) 687 | { 688 | /* missing second half of the surrogate pair */ 689 | goto fail; 690 | } 691 | 692 | /* get the second utf16 sequence */ 693 | second_code = parse_hex4(second_sequence + 2); 694 | /* check that the code is valid */ 695 | if ((second_code < 0xDC00) || (second_code > 0xDFFF)) 696 | { 697 | /* invalid second half of the surrogate pair */ 698 | goto fail; 699 | } 700 | 701 | 702 | /* calculate the unicode codepoint from the surrogate pair */ 703 | codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); 704 | } 705 | else 706 | { 707 | sequence_length = 6; /* \uXXXX */ 708 | codepoint = first_code; 709 | } 710 | 711 | /* encode as UTF-8 712 | * takes at maximum 4 bytes to encode: 713 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 714 | if (codepoint < 0x80) 715 | { 716 | /* normal ascii, encoding 0xxxxxxx */ 717 | utf8_length = 1; 718 | } 719 | else if (codepoint < 0x800) 720 | { 721 | /* two bytes, encoding 110xxxxx 10xxxxxx */ 722 | utf8_length = 2; 723 | first_byte_mark = 0xC0; /* 11000000 */ 724 | } 725 | else if (codepoint < 0x10000) 726 | { 727 | /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 728 | utf8_length = 3; 729 | first_byte_mark = 0xE0; /* 11100000 */ 730 | } 731 | else if (codepoint <= 0x10FFFF) 732 | { 733 | /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 734 | utf8_length = 4; 735 | first_byte_mark = 0xF0; /* 11110000 */ 736 | } 737 | else 738 | { 739 | /* invalid unicode codepoint */ 740 | goto fail; 741 | } 742 | 743 | /* encode as utf8 */ 744 | for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) 745 | { 746 | /* 10xxxxxx */ 747 | (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); 748 | codepoint >>= 6; 749 | } 750 | /* encode first byte */ 751 | if (utf8_length > 1) 752 | { 753 | (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); 754 | } 755 | else 756 | { 757 | (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); 758 | } 759 | 760 | *output_pointer += utf8_length; 761 | 762 | return sequence_length; 763 | 764 | fail: 765 | return 0; 766 | } 767 | 768 | /* Parse the input text into an unescaped cinput, and populate item. */ 769 | static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) 770 | { 771 | const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; 772 | const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; 773 | unsigned char *output_pointer = NULL; 774 | unsigned char *output = NULL; 775 | 776 | /* not a string */ 777 | if (buffer_at_offset(input_buffer)[0] != '\"') 778 | { 779 | goto fail; 780 | } 781 | 782 | { 783 | /* calculate approximate size of the output (overestimate) */ 784 | size_t allocation_length = 0; 785 | size_t skipped_bytes = 0; 786 | while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) 787 | { 788 | /* is escape sequence */ 789 | if (input_end[0] == '\\') 790 | { 791 | if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) 792 | { 793 | /* prevent buffer overflow when last input character is a backslash */ 794 | goto fail; 795 | } 796 | skipped_bytes++; 797 | input_end++; 798 | } 799 | input_end++; 800 | } 801 | if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) 802 | { 803 | goto fail; /* string ended unexpectedly */ 804 | } 805 | 806 | /* This is at most how much we need for the output */ 807 | allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 808 | output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); 809 | if (output == NULL) 810 | { 811 | goto fail; /* allocation failure */ 812 | } 813 | } 814 | 815 | output_pointer = output; 816 | /* loop through the string literal */ 817 | while (input_pointer < input_end) 818 | { 819 | if (*input_pointer != '\\') 820 | { 821 | *output_pointer++ = *input_pointer++; 822 | } 823 | /* escape sequence */ 824 | else 825 | { 826 | unsigned char sequence_length = 2; 827 | if ((input_end - input_pointer) < 1) 828 | { 829 | goto fail; 830 | } 831 | 832 | switch (input_pointer[1]) 833 | { 834 | case 'b': 835 | *output_pointer++ = '\b'; 836 | break; 837 | case 'f': 838 | *output_pointer++ = '\f'; 839 | break; 840 | case 'n': 841 | *output_pointer++ = '\n'; 842 | break; 843 | case 'r': 844 | *output_pointer++ = '\r'; 845 | break; 846 | case 't': 847 | *output_pointer++ = '\t'; 848 | break; 849 | case '\"': 850 | case '\\': 851 | case '/': 852 | *output_pointer++ = input_pointer[1]; 853 | break; 854 | 855 | /* UTF-16 literal */ 856 | case 'u': 857 | sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); 858 | if (sequence_length == 0) 859 | { 860 | /* failed to convert UTF16-literal to UTF-8 */ 861 | goto fail; 862 | } 863 | break; 864 | 865 | default: 866 | goto fail; 867 | } 868 | input_pointer += sequence_length; 869 | } 870 | } 871 | 872 | /* zero terminate the output */ 873 | *output_pointer = '\0'; 874 | 875 | item->type = cJSON_String; 876 | item->valuestring = (char*)output; 877 | 878 | input_buffer->offset = (size_t) (input_end - input_buffer->content); 879 | input_buffer->offset++; 880 | 881 | return true; 882 | 883 | fail: 884 | if (output != NULL) 885 | { 886 | input_buffer->hooks.deallocate(output); 887 | } 888 | 889 | if (input_pointer != NULL) 890 | { 891 | input_buffer->offset = (size_t)(input_pointer - input_buffer->content); 892 | } 893 | 894 | return false; 895 | } 896 | 897 | /* Render the cstring provided to an escaped version that can be printed. */ 898 | static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) 899 | { 900 | const unsigned char *input_pointer = NULL; 901 | unsigned char *output = NULL; 902 | unsigned char *output_pointer = NULL; 903 | size_t output_length = 0; 904 | /* numbers of additional characters needed for escaping */ 905 | size_t escape_characters = 0; 906 | 907 | if (output_buffer == NULL) 908 | { 909 | return false; 910 | } 911 | 912 | /* empty string */ 913 | if (input == NULL) 914 | { 915 | output = ensure(output_buffer, sizeof("\"\"")); 916 | if (output == NULL) 917 | { 918 | return false; 919 | } 920 | strcpy((char*)output, "\"\""); 921 | 922 | return true; 923 | } 924 | 925 | /* set "flag" to 1 if something needs to be escaped */ 926 | for (input_pointer = input; *input_pointer; input_pointer++) 927 | { 928 | switch (*input_pointer) 929 | { 930 | case '\"': 931 | case '\\': 932 | case '\b': 933 | case '\f': 934 | case '\n': 935 | case '\r': 936 | case '\t': 937 | /* one character escape sequence */ 938 | escape_characters++; 939 | break; 940 | default: 941 | if (*input_pointer < 32) 942 | { 943 | /* UTF-16 escape sequence uXXXX */ 944 | escape_characters += 5; 945 | } 946 | break; 947 | } 948 | } 949 | output_length = (size_t)(input_pointer - input) + escape_characters; 950 | 951 | output = ensure(output_buffer, output_length + sizeof("\"\"")); 952 | if (output == NULL) 953 | { 954 | return false; 955 | } 956 | 957 | /* no characters have to be escaped */ 958 | if (escape_characters == 0) 959 | { 960 | output[0] = '\"'; 961 | memcpy(output + 1, input, output_length); 962 | output[output_length + 1] = '\"'; 963 | output[output_length + 2] = '\0'; 964 | 965 | return true; 966 | } 967 | 968 | output[0] = '\"'; 969 | output_pointer = output + 1; 970 | /* copy the string */ 971 | for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) 972 | { 973 | if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) 974 | { 975 | /* normal character, copy */ 976 | *output_pointer = *input_pointer; 977 | } 978 | else 979 | { 980 | /* character needs to be escaped */ 981 | *output_pointer++ = '\\'; 982 | switch (*input_pointer) 983 | { 984 | case '\\': 985 | *output_pointer = '\\'; 986 | break; 987 | case '\"': 988 | *output_pointer = '\"'; 989 | break; 990 | case '\b': 991 | *output_pointer = 'b'; 992 | break; 993 | case '\f': 994 | *output_pointer = 'f'; 995 | break; 996 | case '\n': 997 | *output_pointer = 'n'; 998 | break; 999 | case '\r': 1000 | *output_pointer = 'r'; 1001 | break; 1002 | case '\t': 1003 | *output_pointer = 't'; 1004 | break; 1005 | default: 1006 | /* escape and print as unicode codepoint */ 1007 | sprintf((char*)output_pointer, "u%04x", *input_pointer); 1008 | output_pointer += 4; 1009 | break; 1010 | } 1011 | } 1012 | } 1013 | output[output_length + 1] = '\"'; 1014 | output[output_length + 2] = '\0'; 1015 | 1016 | return true; 1017 | } 1018 | 1019 | /* Invoke print_string_ptr (which is useful) on an item. */ 1020 | static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) 1021 | { 1022 | return print_string_ptr((unsigned char*)item->valuestring, p); 1023 | } 1024 | 1025 | /* Predeclare these prototypes. */ 1026 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); 1027 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); 1028 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); 1029 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); 1030 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); 1031 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); 1032 | 1033 | /* Utility to jump whitespace and cr/lf */ 1034 | static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) 1035 | { 1036 | if ((buffer == NULL) || (buffer->content == NULL)) 1037 | { 1038 | return NULL; 1039 | } 1040 | 1041 | if (cannot_access_at_index(buffer, 0)) 1042 | { 1043 | return buffer; 1044 | } 1045 | 1046 | while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) 1047 | { 1048 | buffer->offset++; 1049 | } 1050 | 1051 | if (buffer->offset == buffer->length) 1052 | { 1053 | buffer->offset--; 1054 | } 1055 | 1056 | return buffer; 1057 | } 1058 | 1059 | /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ 1060 | static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) 1061 | { 1062 | if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) 1063 | { 1064 | return NULL; 1065 | } 1066 | 1067 | if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) 1068 | { 1069 | buffer->offset += 3; 1070 | } 1071 | 1072 | return buffer; 1073 | } 1074 | 1075 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) 1076 | { 1077 | size_t buffer_length; 1078 | 1079 | if (NULL == value) 1080 | { 1081 | return NULL; 1082 | } 1083 | 1084 | /* Adding null character size due to require_null_terminated. */ 1085 | buffer_length = strlen(value) + sizeof(""); 1086 | 1087 | return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); 1088 | } 1089 | 1090 | /* Parse an object - create a new root, and populate. */ 1091 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) 1092 | { 1093 | parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; 1094 | cJSON *item = NULL; 1095 | 1096 | /* reset error position */ 1097 | global_error.json = NULL; 1098 | global_error.position = 0; 1099 | 1100 | if (value == NULL || 0 == buffer_length) 1101 | { 1102 | goto fail; 1103 | } 1104 | 1105 | buffer.content = (const unsigned char*)value; 1106 | buffer.length = buffer_length; 1107 | buffer.offset = 0; 1108 | buffer.hooks = global_hooks; 1109 | 1110 | item = cJSON_New_Item(&global_hooks); 1111 | if (item == NULL) /* memory fail */ 1112 | { 1113 | goto fail; 1114 | } 1115 | 1116 | if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) 1117 | { 1118 | /* parse failure. ep is set. */ 1119 | goto fail; 1120 | } 1121 | 1122 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 1123 | if (require_null_terminated) 1124 | { 1125 | buffer_skip_whitespace(&buffer); 1126 | if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') 1127 | { 1128 | goto fail; 1129 | } 1130 | } 1131 | if (return_parse_end) 1132 | { 1133 | *return_parse_end = (const char*)buffer_at_offset(&buffer); 1134 | } 1135 | 1136 | return item; 1137 | 1138 | fail: 1139 | if (item != NULL) 1140 | { 1141 | cJSON_Delete(item); 1142 | } 1143 | 1144 | if (value != NULL) 1145 | { 1146 | error local_error; 1147 | local_error.json = (const unsigned char*)value; 1148 | local_error.position = 0; 1149 | 1150 | if (buffer.offset < buffer.length) 1151 | { 1152 | local_error.position = buffer.offset; 1153 | } 1154 | else if (buffer.length > 0) 1155 | { 1156 | local_error.position = buffer.length - 1; 1157 | } 1158 | 1159 | if (return_parse_end != NULL) 1160 | { 1161 | *return_parse_end = (const char*)local_error.json + local_error.position; 1162 | } 1163 | 1164 | global_error = local_error; 1165 | } 1166 | 1167 | return NULL; 1168 | } 1169 | 1170 | /* Default options for cJSON_Parse */ 1171 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) 1172 | { 1173 | return cJSON_ParseWithOpts(value, 0, 0); 1174 | } 1175 | 1176 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) 1177 | { 1178 | return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); 1179 | } 1180 | 1181 | #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) 1182 | 1183 | static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) 1184 | { 1185 | static const size_t default_buffer_size = 256; 1186 | printbuffer buffer[1]; 1187 | unsigned char *printed = NULL; 1188 | 1189 | memset(buffer, 0, sizeof(buffer)); 1190 | 1191 | /* create buffer */ 1192 | buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); 1193 | buffer->length = default_buffer_size; 1194 | buffer->format = format; 1195 | buffer->hooks = *hooks; 1196 | if (buffer->buffer == NULL) 1197 | { 1198 | goto fail; 1199 | } 1200 | 1201 | /* print the value */ 1202 | if (!print_value(item, buffer)) 1203 | { 1204 | goto fail; 1205 | } 1206 | update_offset(buffer); 1207 | 1208 | /* check if reallocate is available */ 1209 | if (hooks->reallocate != NULL) 1210 | { 1211 | printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); 1212 | if (printed == NULL) { 1213 | goto fail; 1214 | } 1215 | buffer->buffer = NULL; 1216 | } 1217 | else /* otherwise copy the JSON over to a new buffer */ 1218 | { 1219 | printed = (unsigned char*) hooks->allocate(buffer->offset + 1); 1220 | if (printed == NULL) 1221 | { 1222 | goto fail; 1223 | } 1224 | memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); 1225 | printed[buffer->offset] = '\0'; /* just to be sure */ 1226 | 1227 | /* free the buffer */ 1228 | hooks->deallocate(buffer->buffer); 1229 | } 1230 | 1231 | return printed; 1232 | 1233 | fail: 1234 | if (buffer->buffer != NULL) 1235 | { 1236 | hooks->deallocate(buffer->buffer); 1237 | } 1238 | 1239 | if (printed != NULL) 1240 | { 1241 | hooks->deallocate(printed); 1242 | } 1243 | 1244 | return NULL; 1245 | } 1246 | 1247 | /* Render a cJSON item/entity/structure to text. */ 1248 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) 1249 | { 1250 | return (char*)print(item, true, &global_hooks); 1251 | } 1252 | 1253 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) 1254 | { 1255 | return (char*)print(item, false, &global_hooks); 1256 | } 1257 | 1258 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) 1259 | { 1260 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1261 | 1262 | if (prebuffer < 0) 1263 | { 1264 | return NULL; 1265 | } 1266 | 1267 | p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); 1268 | if (!p.buffer) 1269 | { 1270 | return NULL; 1271 | } 1272 | 1273 | p.length = (size_t)prebuffer; 1274 | p.offset = 0; 1275 | p.noalloc = false; 1276 | p.format = fmt; 1277 | p.hooks = global_hooks; 1278 | 1279 | if (!print_value(item, &p)) 1280 | { 1281 | global_hooks.deallocate(p.buffer); 1282 | return NULL; 1283 | } 1284 | 1285 | return (char*)p.buffer; 1286 | } 1287 | 1288 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) 1289 | { 1290 | printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; 1291 | 1292 | if ((length < 0) || (buffer == NULL)) 1293 | { 1294 | return false; 1295 | } 1296 | 1297 | p.buffer = (unsigned char*)buffer; 1298 | p.length = (size_t)length; 1299 | p.offset = 0; 1300 | p.noalloc = true; 1301 | p.format = format; 1302 | p.hooks = global_hooks; 1303 | 1304 | return print_value(item, &p); 1305 | } 1306 | 1307 | /* Parser core - when encountering text, process appropriately. */ 1308 | static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) 1309 | { 1310 | if ((input_buffer == NULL) || (input_buffer->content == NULL)) 1311 | { 1312 | return false; /* no input */ 1313 | } 1314 | 1315 | /* parse the different types of values */ 1316 | /* null */ 1317 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) 1318 | { 1319 | item->type = cJSON_NULL; 1320 | input_buffer->offset += 4; 1321 | return true; 1322 | } 1323 | /* false */ 1324 | if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) 1325 | { 1326 | item->type = cJSON_False; 1327 | input_buffer->offset += 5; 1328 | return true; 1329 | } 1330 | /* true */ 1331 | if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) 1332 | { 1333 | item->type = cJSON_True; 1334 | item->valueint = 1; 1335 | input_buffer->offset += 4; 1336 | return true; 1337 | } 1338 | /* string */ 1339 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) 1340 | { 1341 | return parse_string(item, input_buffer); 1342 | } 1343 | /* number */ 1344 | if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) 1345 | { 1346 | return parse_number(item, input_buffer); 1347 | } 1348 | /* array */ 1349 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) 1350 | { 1351 | return parse_array(item, input_buffer); 1352 | } 1353 | /* object */ 1354 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) 1355 | { 1356 | return parse_object(item, input_buffer); 1357 | } 1358 | 1359 | return false; 1360 | } 1361 | 1362 | /* Render a value to text. */ 1363 | static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) 1364 | { 1365 | unsigned char *output = NULL; 1366 | 1367 | if ((item == NULL) || (output_buffer == NULL)) 1368 | { 1369 | return false; 1370 | } 1371 | 1372 | switch ((item->type) & 0xFF) 1373 | { 1374 | case cJSON_NULL: 1375 | output = ensure(output_buffer, 5); 1376 | if (output == NULL) 1377 | { 1378 | return false; 1379 | } 1380 | strcpy((char*)output, "null"); 1381 | return true; 1382 | 1383 | case cJSON_False: 1384 | output = ensure(output_buffer, 6); 1385 | if (output == NULL) 1386 | { 1387 | return false; 1388 | } 1389 | strcpy((char*)output, "false"); 1390 | return true; 1391 | 1392 | case cJSON_True: 1393 | output = ensure(output_buffer, 5); 1394 | if (output == NULL) 1395 | { 1396 | return false; 1397 | } 1398 | strcpy((char*)output, "true"); 1399 | return true; 1400 | 1401 | case cJSON_Number: 1402 | return print_number(item, output_buffer); 1403 | 1404 | case cJSON_Raw: 1405 | { 1406 | size_t raw_length = 0; 1407 | if (item->valuestring == NULL) 1408 | { 1409 | return false; 1410 | } 1411 | 1412 | raw_length = strlen(item->valuestring) + sizeof(""); 1413 | output = ensure(output_buffer, raw_length); 1414 | if (output == NULL) 1415 | { 1416 | return false; 1417 | } 1418 | memcpy(output, item->valuestring, raw_length); 1419 | return true; 1420 | } 1421 | 1422 | case cJSON_String: 1423 | return print_string(item, output_buffer); 1424 | 1425 | case cJSON_Array: 1426 | return print_array(item, output_buffer); 1427 | 1428 | case cJSON_Object: 1429 | return print_object(item, output_buffer); 1430 | 1431 | default: 1432 | return false; 1433 | } 1434 | } 1435 | 1436 | /* Build an array from input text. */ 1437 | static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) 1438 | { 1439 | cJSON *head = NULL; /* head of the linked list */ 1440 | cJSON *current_item = NULL; 1441 | 1442 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1443 | { 1444 | return false; /* to deeply nested */ 1445 | } 1446 | input_buffer->depth++; 1447 | 1448 | if (buffer_at_offset(input_buffer)[0] != '[') 1449 | { 1450 | /* not an array */ 1451 | goto fail; 1452 | } 1453 | 1454 | input_buffer->offset++; 1455 | buffer_skip_whitespace(input_buffer); 1456 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) 1457 | { 1458 | /* empty array */ 1459 | goto success; 1460 | } 1461 | 1462 | /* check if we skipped to the end of the buffer */ 1463 | if (cannot_access_at_index(input_buffer, 0)) 1464 | { 1465 | input_buffer->offset--; 1466 | goto fail; 1467 | } 1468 | 1469 | /* step back to character in front of the first element */ 1470 | input_buffer->offset--; 1471 | /* loop through the comma separated array elements */ 1472 | do 1473 | { 1474 | /* allocate next item */ 1475 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1476 | if (new_item == NULL) 1477 | { 1478 | goto fail; /* allocation failure */ 1479 | } 1480 | 1481 | /* attach next item to list */ 1482 | if (head == NULL) 1483 | { 1484 | /* start the linked list */ 1485 | current_item = head = new_item; 1486 | } 1487 | else 1488 | { 1489 | /* add to the end and advance */ 1490 | current_item->next = new_item; 1491 | new_item->prev = current_item; 1492 | current_item = new_item; 1493 | } 1494 | 1495 | /* parse next value */ 1496 | input_buffer->offset++; 1497 | buffer_skip_whitespace(input_buffer); 1498 | if (!parse_value(current_item, input_buffer)) 1499 | { 1500 | goto fail; /* failed to parse value */ 1501 | } 1502 | buffer_skip_whitespace(input_buffer); 1503 | } 1504 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1505 | 1506 | if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') 1507 | { 1508 | goto fail; /* expected end of array */ 1509 | } 1510 | 1511 | success: 1512 | input_buffer->depth--; 1513 | 1514 | if (head != NULL) { 1515 | head->prev = current_item; 1516 | } 1517 | 1518 | item->type = cJSON_Array; 1519 | item->child = head; 1520 | 1521 | input_buffer->offset++; 1522 | 1523 | return true; 1524 | 1525 | fail: 1526 | if (head != NULL) 1527 | { 1528 | cJSON_Delete(head); 1529 | } 1530 | 1531 | return false; 1532 | } 1533 | 1534 | /* Render an array to text */ 1535 | static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) 1536 | { 1537 | unsigned char *output_pointer = NULL; 1538 | size_t length = 0; 1539 | cJSON *current_element = item->child; 1540 | 1541 | if (output_buffer == NULL) 1542 | { 1543 | return false; 1544 | } 1545 | 1546 | /* Compose the output array. */ 1547 | /* opening square bracket */ 1548 | output_pointer = ensure(output_buffer, 1); 1549 | if (output_pointer == NULL) 1550 | { 1551 | return false; 1552 | } 1553 | 1554 | *output_pointer = '['; 1555 | output_buffer->offset++; 1556 | output_buffer->depth++; 1557 | 1558 | while (current_element != NULL) 1559 | { 1560 | if (!print_value(current_element, output_buffer)) 1561 | { 1562 | return false; 1563 | } 1564 | update_offset(output_buffer); 1565 | if (current_element->next) 1566 | { 1567 | length = (size_t) (output_buffer->format ? 2 : 1); 1568 | output_pointer = ensure(output_buffer, length + 1); 1569 | if (output_pointer == NULL) 1570 | { 1571 | return false; 1572 | } 1573 | *output_pointer++ = ','; 1574 | if(output_buffer->format) 1575 | { 1576 | *output_pointer++ = ' '; 1577 | } 1578 | *output_pointer = '\0'; 1579 | output_buffer->offset += length; 1580 | } 1581 | current_element = current_element->next; 1582 | } 1583 | 1584 | output_pointer = ensure(output_buffer, 2); 1585 | if (output_pointer == NULL) 1586 | { 1587 | return false; 1588 | } 1589 | *output_pointer++ = ']'; 1590 | *output_pointer = '\0'; 1591 | output_buffer->depth--; 1592 | 1593 | return true; 1594 | } 1595 | 1596 | /* Build an object from the text. */ 1597 | static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) 1598 | { 1599 | cJSON *head = NULL; /* linked list head */ 1600 | cJSON *current_item = NULL; 1601 | 1602 | if (input_buffer->depth >= CJSON_NESTING_LIMIT) 1603 | { 1604 | return false; /* to deeply nested */ 1605 | } 1606 | input_buffer->depth++; 1607 | 1608 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) 1609 | { 1610 | goto fail; /* not an object */ 1611 | } 1612 | 1613 | input_buffer->offset++; 1614 | buffer_skip_whitespace(input_buffer); 1615 | if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) 1616 | { 1617 | goto success; /* empty object */ 1618 | } 1619 | 1620 | /* check if we skipped to the end of the buffer */ 1621 | if (cannot_access_at_index(input_buffer, 0)) 1622 | { 1623 | input_buffer->offset--; 1624 | goto fail; 1625 | } 1626 | 1627 | /* step back to character in front of the first element */ 1628 | input_buffer->offset--; 1629 | /* loop through the comma separated array elements */ 1630 | do 1631 | { 1632 | /* allocate next item */ 1633 | cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); 1634 | if (new_item == NULL) 1635 | { 1636 | goto fail; /* allocation failure */ 1637 | } 1638 | 1639 | /* attach next item to list */ 1640 | if (head == NULL) 1641 | { 1642 | /* start the linked list */ 1643 | current_item = head = new_item; 1644 | } 1645 | else 1646 | { 1647 | /* add to the end and advance */ 1648 | current_item->next = new_item; 1649 | new_item->prev = current_item; 1650 | current_item = new_item; 1651 | } 1652 | 1653 | /* parse the name of the child */ 1654 | input_buffer->offset++; 1655 | buffer_skip_whitespace(input_buffer); 1656 | if (!parse_string(current_item, input_buffer)) 1657 | { 1658 | goto fail; /* failed to parse name */ 1659 | } 1660 | buffer_skip_whitespace(input_buffer); 1661 | 1662 | /* swap valuestring and string, because we parsed the name */ 1663 | current_item->string = current_item->valuestring; 1664 | current_item->valuestring = NULL; 1665 | 1666 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) 1667 | { 1668 | goto fail; /* invalid object */ 1669 | } 1670 | 1671 | /* parse the value */ 1672 | input_buffer->offset++; 1673 | buffer_skip_whitespace(input_buffer); 1674 | if (!parse_value(current_item, input_buffer)) 1675 | { 1676 | goto fail; /* failed to parse value */ 1677 | } 1678 | buffer_skip_whitespace(input_buffer); 1679 | } 1680 | while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 1681 | 1682 | if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) 1683 | { 1684 | goto fail; /* expected end of object */ 1685 | } 1686 | 1687 | success: 1688 | input_buffer->depth--; 1689 | 1690 | if (head != NULL) { 1691 | head->prev = current_item; 1692 | } 1693 | 1694 | item->type = cJSON_Object; 1695 | item->child = head; 1696 | 1697 | input_buffer->offset++; 1698 | return true; 1699 | 1700 | fail: 1701 | if (head != NULL) 1702 | { 1703 | cJSON_Delete(head); 1704 | } 1705 | 1706 | return false; 1707 | } 1708 | 1709 | /* Render an object to text. */ 1710 | static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) 1711 | { 1712 | unsigned char *output_pointer = NULL; 1713 | size_t length = 0; 1714 | cJSON *current_item = item->child; 1715 | 1716 | if (output_buffer == NULL) 1717 | { 1718 | return false; 1719 | } 1720 | 1721 | /* Compose the output: */ 1722 | length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ 1723 | output_pointer = ensure(output_buffer, length + 1); 1724 | if (output_pointer == NULL) 1725 | { 1726 | return false; 1727 | } 1728 | 1729 | *output_pointer++ = '{'; 1730 | output_buffer->depth++; 1731 | if (output_buffer->format) 1732 | { 1733 | *output_pointer++ = '\n'; 1734 | } 1735 | output_buffer->offset += length; 1736 | 1737 | while (current_item) 1738 | { 1739 | if (output_buffer->format) 1740 | { 1741 | size_t i; 1742 | output_pointer = ensure(output_buffer, output_buffer->depth); 1743 | if (output_pointer == NULL) 1744 | { 1745 | return false; 1746 | } 1747 | for (i = 0; i < output_buffer->depth; i++) 1748 | { 1749 | *output_pointer++ = '\t'; 1750 | } 1751 | output_buffer->offset += output_buffer->depth; 1752 | } 1753 | 1754 | /* print key */ 1755 | if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) 1756 | { 1757 | return false; 1758 | } 1759 | update_offset(output_buffer); 1760 | 1761 | length = (size_t) (output_buffer->format ? 2 : 1); 1762 | output_pointer = ensure(output_buffer, length); 1763 | if (output_pointer == NULL) 1764 | { 1765 | return false; 1766 | } 1767 | *output_pointer++ = ':'; 1768 | if (output_buffer->format) 1769 | { 1770 | *output_pointer++ = '\t'; 1771 | } 1772 | output_buffer->offset += length; 1773 | 1774 | /* print value */ 1775 | if (!print_value(current_item, output_buffer)) 1776 | { 1777 | return false; 1778 | } 1779 | update_offset(output_buffer); 1780 | 1781 | /* print comma if not last */ 1782 | length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); 1783 | output_pointer = ensure(output_buffer, length + 1); 1784 | if (output_pointer == NULL) 1785 | { 1786 | return false; 1787 | } 1788 | if (current_item->next) 1789 | { 1790 | *output_pointer++ = ','; 1791 | } 1792 | 1793 | if (output_buffer->format) 1794 | { 1795 | *output_pointer++ = '\n'; 1796 | } 1797 | *output_pointer = '\0'; 1798 | output_buffer->offset += length; 1799 | 1800 | current_item = current_item->next; 1801 | } 1802 | 1803 | output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); 1804 | if (output_pointer == NULL) 1805 | { 1806 | return false; 1807 | } 1808 | if (output_buffer->format) 1809 | { 1810 | size_t i; 1811 | for (i = 0; i < (output_buffer->depth - 1); i++) 1812 | { 1813 | *output_pointer++ = '\t'; 1814 | } 1815 | } 1816 | *output_pointer++ = '}'; 1817 | *output_pointer = '\0'; 1818 | output_buffer->depth--; 1819 | 1820 | return true; 1821 | } 1822 | 1823 | /* Get Array size/item / object item. */ 1824 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) 1825 | { 1826 | cJSON *child = NULL; 1827 | size_t size = 0; 1828 | 1829 | if (array == NULL) 1830 | { 1831 | return 0; 1832 | } 1833 | 1834 | child = array->child; 1835 | 1836 | while(child != NULL) 1837 | { 1838 | size++; 1839 | child = child->next; 1840 | } 1841 | 1842 | /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ 1843 | 1844 | return (int)size; 1845 | } 1846 | 1847 | static cJSON* get_array_item(const cJSON *array, size_t index) 1848 | { 1849 | cJSON *current_child = NULL; 1850 | 1851 | if (array == NULL) 1852 | { 1853 | return NULL; 1854 | } 1855 | 1856 | current_child = array->child; 1857 | while ((current_child != NULL) && (index > 0)) 1858 | { 1859 | index--; 1860 | current_child = current_child->next; 1861 | } 1862 | 1863 | return current_child; 1864 | } 1865 | 1866 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) 1867 | { 1868 | if (index < 0) 1869 | { 1870 | return NULL; 1871 | } 1872 | 1873 | return get_array_item(array, (size_t)index); 1874 | } 1875 | 1876 | static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) 1877 | { 1878 | cJSON *current_element = NULL; 1879 | 1880 | if ((object == NULL) || (name == NULL)) 1881 | { 1882 | return NULL; 1883 | } 1884 | 1885 | current_element = object->child; 1886 | if (case_sensitive) 1887 | { 1888 | while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) 1889 | { 1890 | current_element = current_element->next; 1891 | } 1892 | } 1893 | else 1894 | { 1895 | while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) 1896 | { 1897 | current_element = current_element->next; 1898 | } 1899 | } 1900 | 1901 | if ((current_element == NULL) || (current_element->string == NULL)) { 1902 | return NULL; 1903 | } 1904 | 1905 | return current_element; 1906 | } 1907 | 1908 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) 1909 | { 1910 | return get_object_item(object, string, false); 1911 | } 1912 | 1913 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) 1914 | { 1915 | return get_object_item(object, string, true); 1916 | } 1917 | 1918 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) 1919 | { 1920 | return cJSON_GetObjectItem(object, string) ? 1 : 0; 1921 | } 1922 | 1923 | /* Utility for array list handling. */ 1924 | static void suffix_object(cJSON *prev, cJSON *item) 1925 | { 1926 | prev->next = item; 1927 | item->prev = prev; 1928 | } 1929 | 1930 | /* Utility for handling references. */ 1931 | static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) 1932 | { 1933 | cJSON *reference = NULL; 1934 | if (item == NULL) 1935 | { 1936 | return NULL; 1937 | } 1938 | 1939 | reference = cJSON_New_Item(hooks); 1940 | if (reference == NULL) 1941 | { 1942 | return NULL; 1943 | } 1944 | 1945 | memcpy(reference, item, sizeof(cJSON)); 1946 | reference->string = NULL; 1947 | reference->type |= cJSON_IsReference; 1948 | reference->next = reference->prev = NULL; 1949 | return reference; 1950 | } 1951 | 1952 | static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) 1953 | { 1954 | cJSON *child = NULL; 1955 | 1956 | if ((item == NULL) || (array == NULL) || (array == item)) 1957 | { 1958 | return false; 1959 | } 1960 | 1961 | child = array->child; 1962 | /* 1963 | * To find the last item in array quickly, we use prev in array 1964 | */ 1965 | if (child == NULL) 1966 | { 1967 | /* list is empty, start new one */ 1968 | array->child = item; 1969 | item->prev = item; 1970 | item->next = NULL; 1971 | } 1972 | else 1973 | { 1974 | /* append to the end */ 1975 | if (child->prev) 1976 | { 1977 | suffix_object(child->prev, item); 1978 | array->child->prev = item; 1979 | } 1980 | } 1981 | 1982 | return true; 1983 | } 1984 | 1985 | /* Add item to array/object. */ 1986 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) 1987 | { 1988 | return add_item_to_array(array, item); 1989 | } 1990 | 1991 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 1992 | #pragma GCC diagnostic push 1993 | #endif 1994 | #ifdef __GNUC__ 1995 | #pragma GCC diagnostic ignored "-Wcast-qual" 1996 | #endif 1997 | /* helper function to cast away const */ 1998 | static void* cast_away_const(const void* string) 1999 | { 2000 | return (void*)string; 2001 | } 2002 | #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 2003 | #pragma GCC diagnostic pop 2004 | #endif 2005 | 2006 | 2007 | static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) 2008 | { 2009 | char *new_key = NULL; 2010 | int new_type = cJSON_Invalid; 2011 | 2012 | if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) 2013 | { 2014 | return false; 2015 | } 2016 | 2017 | if (constant_key) 2018 | { 2019 | new_key = (char*)cast_away_const(string); 2020 | new_type = item->type | cJSON_StringIsConst; 2021 | } 2022 | else 2023 | { 2024 | new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); 2025 | if (new_key == NULL) 2026 | { 2027 | return false; 2028 | } 2029 | 2030 | new_type = item->type & ~cJSON_StringIsConst; 2031 | } 2032 | 2033 | if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) 2034 | { 2035 | hooks->deallocate(item->string); 2036 | } 2037 | 2038 | item->string = new_key; 2039 | item->type = new_type; 2040 | 2041 | return add_item_to_array(object, item); 2042 | } 2043 | 2044 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 2045 | { 2046 | return add_item_to_object(object, string, item, &global_hooks, false); 2047 | } 2048 | 2049 | /* Add an item to an object with constant string as key */ 2050 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 2051 | { 2052 | return add_item_to_object(object, string, item, &global_hooks, true); 2053 | } 2054 | 2055 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 2056 | { 2057 | if (array == NULL) 2058 | { 2059 | return false; 2060 | } 2061 | 2062 | return add_item_to_array(array, create_reference(item, &global_hooks)); 2063 | } 2064 | 2065 | CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 2066 | { 2067 | if ((object == NULL) || (string == NULL)) 2068 | { 2069 | return false; 2070 | } 2071 | 2072 | return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); 2073 | } 2074 | 2075 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) 2076 | { 2077 | cJSON *null = cJSON_CreateNull(); 2078 | if (add_item_to_object(object, name, null, &global_hooks, false)) 2079 | { 2080 | return null; 2081 | } 2082 | 2083 | cJSON_Delete(null); 2084 | return NULL; 2085 | } 2086 | 2087 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) 2088 | { 2089 | cJSON *true_item = cJSON_CreateTrue(); 2090 | if (add_item_to_object(object, name, true_item, &global_hooks, false)) 2091 | { 2092 | return true_item; 2093 | } 2094 | 2095 | cJSON_Delete(true_item); 2096 | return NULL; 2097 | } 2098 | 2099 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) 2100 | { 2101 | cJSON *false_item = cJSON_CreateFalse(); 2102 | if (add_item_to_object(object, name, false_item, &global_hooks, false)) 2103 | { 2104 | return false_item; 2105 | } 2106 | 2107 | cJSON_Delete(false_item); 2108 | return NULL; 2109 | } 2110 | 2111 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) 2112 | { 2113 | cJSON *bool_item = cJSON_CreateBool(boolean); 2114 | if (add_item_to_object(object, name, bool_item, &global_hooks, false)) 2115 | { 2116 | return bool_item; 2117 | } 2118 | 2119 | cJSON_Delete(bool_item); 2120 | return NULL; 2121 | } 2122 | 2123 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) 2124 | { 2125 | cJSON *number_item = cJSON_CreateNumber(number); 2126 | if (add_item_to_object(object, name, number_item, &global_hooks, false)) 2127 | { 2128 | return number_item; 2129 | } 2130 | 2131 | cJSON_Delete(number_item); 2132 | return NULL; 2133 | } 2134 | 2135 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) 2136 | { 2137 | cJSON *string_item = cJSON_CreateString(string); 2138 | if (add_item_to_object(object, name, string_item, &global_hooks, false)) 2139 | { 2140 | return string_item; 2141 | } 2142 | 2143 | cJSON_Delete(string_item); 2144 | return NULL; 2145 | } 2146 | 2147 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) 2148 | { 2149 | cJSON *raw_item = cJSON_CreateRaw(raw); 2150 | if (add_item_to_object(object, name, raw_item, &global_hooks, false)) 2151 | { 2152 | return raw_item; 2153 | } 2154 | 2155 | cJSON_Delete(raw_item); 2156 | return NULL; 2157 | } 2158 | 2159 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) 2160 | { 2161 | cJSON *object_item = cJSON_CreateObject(); 2162 | if (add_item_to_object(object, name, object_item, &global_hooks, false)) 2163 | { 2164 | return object_item; 2165 | } 2166 | 2167 | cJSON_Delete(object_item); 2168 | return NULL; 2169 | } 2170 | 2171 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) 2172 | { 2173 | cJSON *array = cJSON_CreateArray(); 2174 | if (add_item_to_object(object, name, array, &global_hooks, false)) 2175 | { 2176 | return array; 2177 | } 2178 | 2179 | cJSON_Delete(array); 2180 | return NULL; 2181 | } 2182 | 2183 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) 2184 | { 2185 | if ((parent == NULL) || (item == NULL)) 2186 | { 2187 | return NULL; 2188 | } 2189 | 2190 | if (item != parent->child) 2191 | { 2192 | /* not the first element */ 2193 | item->prev->next = item->next; 2194 | } 2195 | if (item->next != NULL) 2196 | { 2197 | /* not the last element */ 2198 | item->next->prev = item->prev; 2199 | } 2200 | 2201 | if (item == parent->child) 2202 | { 2203 | /* first element */ 2204 | parent->child = item->next; 2205 | } 2206 | else if (item->next == NULL) 2207 | { 2208 | /* last element */ 2209 | parent->child->prev = item->prev; 2210 | } 2211 | 2212 | /* make sure the detached item doesn't point anywhere anymore */ 2213 | item->prev = NULL; 2214 | item->next = NULL; 2215 | 2216 | return item; 2217 | } 2218 | 2219 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) 2220 | { 2221 | if (which < 0) 2222 | { 2223 | return NULL; 2224 | } 2225 | 2226 | return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); 2227 | } 2228 | 2229 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) 2230 | { 2231 | cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 2232 | } 2233 | 2234 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) 2235 | { 2236 | cJSON *to_detach = cJSON_GetObjectItem(object, string); 2237 | 2238 | return cJSON_DetachItemViaPointer(object, to_detach); 2239 | } 2240 | 2241 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) 2242 | { 2243 | cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); 2244 | 2245 | return cJSON_DetachItemViaPointer(object, to_detach); 2246 | } 2247 | 2248 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) 2249 | { 2250 | cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 2251 | } 2252 | 2253 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) 2254 | { 2255 | cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); 2256 | } 2257 | 2258 | /* Replace array/object items with new ones. */ 2259 | CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 2260 | { 2261 | cJSON *after_inserted = NULL; 2262 | 2263 | if (which < 0) 2264 | { 2265 | return false; 2266 | } 2267 | 2268 | after_inserted = get_array_item(array, (size_t)which); 2269 | if (after_inserted == NULL) 2270 | { 2271 | return add_item_to_array(array, newitem); 2272 | } 2273 | 2274 | newitem->next = after_inserted; 2275 | newitem->prev = after_inserted->prev; 2276 | after_inserted->prev = newitem; 2277 | if (after_inserted == array->child) 2278 | { 2279 | array->child = newitem; 2280 | } 2281 | else 2282 | { 2283 | newitem->prev->next = newitem; 2284 | } 2285 | return true; 2286 | } 2287 | 2288 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) 2289 | { 2290 | if ((parent == NULL) || (replacement == NULL) || (item == NULL)) 2291 | { 2292 | return false; 2293 | } 2294 | 2295 | if (replacement == item) 2296 | { 2297 | return true; 2298 | } 2299 | 2300 | replacement->next = item->next; 2301 | replacement->prev = item->prev; 2302 | 2303 | if (replacement->next != NULL) 2304 | { 2305 | replacement->next->prev = replacement; 2306 | } 2307 | if (parent->child == item) 2308 | { 2309 | if (parent->child->prev == parent->child) 2310 | { 2311 | replacement->prev = replacement; 2312 | } 2313 | parent->child = replacement; 2314 | } 2315 | else 2316 | { /* 2317 | * To find the last item in array quickly, we use prev in array. 2318 | * We can't modify the last item's next pointer where this item was the parent's child 2319 | */ 2320 | if (replacement->prev != NULL) 2321 | { 2322 | replacement->prev->next = replacement; 2323 | } 2324 | if (replacement->next == NULL) 2325 | { 2326 | parent->child->prev = replacement; 2327 | } 2328 | } 2329 | 2330 | item->next = NULL; 2331 | item->prev = NULL; 2332 | cJSON_Delete(item); 2333 | 2334 | return true; 2335 | } 2336 | 2337 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 2338 | { 2339 | if (which < 0) 2340 | { 2341 | return false; 2342 | } 2343 | 2344 | return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); 2345 | } 2346 | 2347 | static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) 2348 | { 2349 | if ((replacement == NULL) || (string == NULL)) 2350 | { 2351 | return false; 2352 | } 2353 | 2354 | /* replace the name in the replacement */ 2355 | if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) 2356 | { 2357 | cJSON_free(replacement->string); 2358 | } 2359 | replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2360 | replacement->type &= ~cJSON_StringIsConst; 2361 | 2362 | return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); 2363 | } 2364 | 2365 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 2366 | { 2367 | return replace_item_in_object(object, string, newitem, false); 2368 | } 2369 | 2370 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) 2371 | { 2372 | return replace_item_in_object(object, string, newitem, true); 2373 | } 2374 | 2375 | /* Create basic types: */ 2376 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) 2377 | { 2378 | cJSON *item = cJSON_New_Item(&global_hooks); 2379 | if(item) 2380 | { 2381 | item->type = cJSON_NULL; 2382 | } 2383 | 2384 | return item; 2385 | } 2386 | 2387 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) 2388 | { 2389 | cJSON *item = cJSON_New_Item(&global_hooks); 2390 | if(item) 2391 | { 2392 | item->type = cJSON_True; 2393 | } 2394 | 2395 | return item; 2396 | } 2397 | 2398 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) 2399 | { 2400 | cJSON *item = cJSON_New_Item(&global_hooks); 2401 | if(item) 2402 | { 2403 | item->type = cJSON_False; 2404 | } 2405 | 2406 | return item; 2407 | } 2408 | 2409 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) 2410 | { 2411 | cJSON *item = cJSON_New_Item(&global_hooks); 2412 | if(item) 2413 | { 2414 | item->type = boolean ? cJSON_True : cJSON_False; 2415 | } 2416 | 2417 | return item; 2418 | } 2419 | 2420 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) 2421 | { 2422 | cJSON *item = cJSON_New_Item(&global_hooks); 2423 | if(item) 2424 | { 2425 | item->type = cJSON_Number; 2426 | item->valuedouble = num; 2427 | 2428 | /* use saturation in case of overflow */ 2429 | if (num >= INT_MAX) 2430 | { 2431 | item->valueint = INT_MAX; 2432 | } 2433 | else if (num <= (double)INT_MIN) 2434 | { 2435 | item->valueint = INT_MIN; 2436 | } 2437 | else 2438 | { 2439 | item->valueint = (int)num; 2440 | } 2441 | } 2442 | 2443 | return item; 2444 | } 2445 | 2446 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) 2447 | { 2448 | cJSON *item = cJSON_New_Item(&global_hooks); 2449 | if(item) 2450 | { 2451 | item->type = cJSON_String; 2452 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); 2453 | if(!item->valuestring) 2454 | { 2455 | cJSON_Delete(item); 2456 | return NULL; 2457 | } 2458 | } 2459 | 2460 | return item; 2461 | } 2462 | 2463 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) 2464 | { 2465 | cJSON *item = cJSON_New_Item(&global_hooks); 2466 | if (item != NULL) 2467 | { 2468 | item->type = cJSON_String | cJSON_IsReference; 2469 | item->valuestring = (char*)cast_away_const(string); 2470 | } 2471 | 2472 | return item; 2473 | } 2474 | 2475 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) 2476 | { 2477 | cJSON *item = cJSON_New_Item(&global_hooks); 2478 | if (item != NULL) { 2479 | item->type = cJSON_Object | cJSON_IsReference; 2480 | item->child = (cJSON*)cast_away_const(child); 2481 | } 2482 | 2483 | return item; 2484 | } 2485 | 2486 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { 2487 | cJSON *item = cJSON_New_Item(&global_hooks); 2488 | if (item != NULL) { 2489 | item->type = cJSON_Array | cJSON_IsReference; 2490 | item->child = (cJSON*)cast_away_const(child); 2491 | } 2492 | 2493 | return item; 2494 | } 2495 | 2496 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) 2497 | { 2498 | cJSON *item = cJSON_New_Item(&global_hooks); 2499 | if(item) 2500 | { 2501 | item->type = cJSON_Raw; 2502 | item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); 2503 | if(!item->valuestring) 2504 | { 2505 | cJSON_Delete(item); 2506 | return NULL; 2507 | } 2508 | } 2509 | 2510 | return item; 2511 | } 2512 | 2513 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) 2514 | { 2515 | cJSON *item = cJSON_New_Item(&global_hooks); 2516 | if(item) 2517 | { 2518 | item->type=cJSON_Array; 2519 | } 2520 | 2521 | return item; 2522 | } 2523 | 2524 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) 2525 | { 2526 | cJSON *item = cJSON_New_Item(&global_hooks); 2527 | if (item) 2528 | { 2529 | item->type = cJSON_Object; 2530 | } 2531 | 2532 | return item; 2533 | } 2534 | 2535 | /* Create Arrays: */ 2536 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) 2537 | { 2538 | size_t i = 0; 2539 | cJSON *n = NULL; 2540 | cJSON *p = NULL; 2541 | cJSON *a = NULL; 2542 | 2543 | if ((count < 0) || (numbers == NULL)) 2544 | { 2545 | return NULL; 2546 | } 2547 | 2548 | a = cJSON_CreateArray(); 2549 | 2550 | for(i = 0; a && (i < (size_t)count); i++) 2551 | { 2552 | n = cJSON_CreateNumber(numbers[i]); 2553 | if (!n) 2554 | { 2555 | cJSON_Delete(a); 2556 | return NULL; 2557 | } 2558 | if(!i) 2559 | { 2560 | a->child = n; 2561 | } 2562 | else 2563 | { 2564 | suffix_object(p, n); 2565 | } 2566 | p = n; 2567 | } 2568 | 2569 | if (a && a->child) { 2570 | a->child->prev = n; 2571 | } 2572 | 2573 | return a; 2574 | } 2575 | 2576 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) 2577 | { 2578 | size_t i = 0; 2579 | cJSON *n = NULL; 2580 | cJSON *p = NULL; 2581 | cJSON *a = NULL; 2582 | 2583 | if ((count < 0) || (numbers == NULL)) 2584 | { 2585 | return NULL; 2586 | } 2587 | 2588 | a = cJSON_CreateArray(); 2589 | 2590 | for(i = 0; a && (i < (size_t)count); i++) 2591 | { 2592 | n = cJSON_CreateNumber((double)numbers[i]); 2593 | if(!n) 2594 | { 2595 | cJSON_Delete(a); 2596 | return NULL; 2597 | } 2598 | if(!i) 2599 | { 2600 | a->child = n; 2601 | } 2602 | else 2603 | { 2604 | suffix_object(p, n); 2605 | } 2606 | p = n; 2607 | } 2608 | 2609 | if (a && a->child) { 2610 | a->child->prev = n; 2611 | } 2612 | 2613 | return a; 2614 | } 2615 | 2616 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) 2617 | { 2618 | size_t i = 0; 2619 | cJSON *n = NULL; 2620 | cJSON *p = NULL; 2621 | cJSON *a = NULL; 2622 | 2623 | if ((count < 0) || (numbers == NULL)) 2624 | { 2625 | return NULL; 2626 | } 2627 | 2628 | a = cJSON_CreateArray(); 2629 | 2630 | for(i = 0; a && (i < (size_t)count); i++) 2631 | { 2632 | n = cJSON_CreateNumber(numbers[i]); 2633 | if(!n) 2634 | { 2635 | cJSON_Delete(a); 2636 | return NULL; 2637 | } 2638 | if(!i) 2639 | { 2640 | a->child = n; 2641 | } 2642 | else 2643 | { 2644 | suffix_object(p, n); 2645 | } 2646 | p = n; 2647 | } 2648 | 2649 | if (a && a->child) { 2650 | a->child->prev = n; 2651 | } 2652 | 2653 | return a; 2654 | } 2655 | 2656 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) 2657 | { 2658 | size_t i = 0; 2659 | cJSON *n = NULL; 2660 | cJSON *p = NULL; 2661 | cJSON *a = NULL; 2662 | 2663 | if ((count < 0) || (strings == NULL)) 2664 | { 2665 | return NULL; 2666 | } 2667 | 2668 | a = cJSON_CreateArray(); 2669 | 2670 | for (i = 0; a && (i < (size_t)count); i++) 2671 | { 2672 | n = cJSON_CreateString(strings[i]); 2673 | if(!n) 2674 | { 2675 | cJSON_Delete(a); 2676 | return NULL; 2677 | } 2678 | if(!i) 2679 | { 2680 | a->child = n; 2681 | } 2682 | else 2683 | { 2684 | suffix_object(p,n); 2685 | } 2686 | p = n; 2687 | } 2688 | 2689 | if (a && a->child) { 2690 | a->child->prev = n; 2691 | } 2692 | 2693 | return a; 2694 | } 2695 | 2696 | /* Duplication */ 2697 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) 2698 | { 2699 | cJSON *newitem = NULL; 2700 | cJSON *child = NULL; 2701 | cJSON *next = NULL; 2702 | cJSON *newchild = NULL; 2703 | 2704 | /* Bail on bad ptr */ 2705 | if (!item) 2706 | { 2707 | goto fail; 2708 | } 2709 | /* Create new item */ 2710 | newitem = cJSON_New_Item(&global_hooks); 2711 | if (!newitem) 2712 | { 2713 | goto fail; 2714 | } 2715 | /* Copy over all vars */ 2716 | newitem->type = item->type & (~cJSON_IsReference); 2717 | newitem->valueint = item->valueint; 2718 | newitem->valuedouble = item->valuedouble; 2719 | if (item->valuestring) 2720 | { 2721 | newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); 2722 | if (!newitem->valuestring) 2723 | { 2724 | goto fail; 2725 | } 2726 | } 2727 | if (item->string) 2728 | { 2729 | newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); 2730 | if (!newitem->string) 2731 | { 2732 | goto fail; 2733 | } 2734 | } 2735 | /* If non-recursive, then we're done! */ 2736 | if (!recurse) 2737 | { 2738 | return newitem; 2739 | } 2740 | /* Walk the ->next chain for the child. */ 2741 | child = item->child; 2742 | while (child != NULL) 2743 | { 2744 | newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ 2745 | if (!newchild) 2746 | { 2747 | goto fail; 2748 | } 2749 | if (next != NULL) 2750 | { 2751 | /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 2752 | next->next = newchild; 2753 | newchild->prev = next; 2754 | next = newchild; 2755 | } 2756 | else 2757 | { 2758 | /* Set newitem->child and move to it */ 2759 | newitem->child = newchild; 2760 | next = newchild; 2761 | } 2762 | child = child->next; 2763 | } 2764 | if (newitem && newitem->child) 2765 | { 2766 | newitem->child->prev = newchild; 2767 | } 2768 | 2769 | return newitem; 2770 | 2771 | fail: 2772 | if (newitem != NULL) 2773 | { 2774 | cJSON_Delete(newitem); 2775 | } 2776 | 2777 | return NULL; 2778 | } 2779 | 2780 | static void skip_oneline_comment(char **input) 2781 | { 2782 | *input += static_strlen("//"); 2783 | 2784 | for (; (*input)[0] != '\0'; ++(*input)) 2785 | { 2786 | if ((*input)[0] == '\n') { 2787 | *input += static_strlen("\n"); 2788 | return; 2789 | } 2790 | } 2791 | } 2792 | 2793 | static void skip_multiline_comment(char **input) 2794 | { 2795 | *input += static_strlen("/*"); 2796 | 2797 | for (; (*input)[0] != '\0'; ++(*input)) 2798 | { 2799 | if (((*input)[0] == '*') && ((*input)[1] == '/')) 2800 | { 2801 | *input += static_strlen("*/"); 2802 | return; 2803 | } 2804 | } 2805 | } 2806 | 2807 | static void minify_string(char **input, char **output) { 2808 | (*output)[0] = (*input)[0]; 2809 | *input += static_strlen("\""); 2810 | *output += static_strlen("\""); 2811 | 2812 | 2813 | for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { 2814 | (*output)[0] = (*input)[0]; 2815 | 2816 | if ((*input)[0] == '\"') { 2817 | (*output)[0] = '\"'; 2818 | *input += static_strlen("\""); 2819 | *output += static_strlen("\""); 2820 | return; 2821 | } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { 2822 | (*output)[1] = (*input)[1]; 2823 | *input += static_strlen("\""); 2824 | *output += static_strlen("\""); 2825 | } 2826 | } 2827 | } 2828 | 2829 | CJSON_PUBLIC(void) cJSON_Minify(char *json) 2830 | { 2831 | char *into = json; 2832 | 2833 | if (json == NULL) 2834 | { 2835 | return; 2836 | } 2837 | 2838 | while (json[0] != '\0') 2839 | { 2840 | switch (json[0]) 2841 | { 2842 | case ' ': 2843 | case '\t': 2844 | case '\r': 2845 | case '\n': 2846 | json++; 2847 | break; 2848 | 2849 | case '/': 2850 | if (json[1] == '/') 2851 | { 2852 | skip_oneline_comment(&json); 2853 | } 2854 | else if (json[1] == '*') 2855 | { 2856 | skip_multiline_comment(&json); 2857 | } else { 2858 | json++; 2859 | } 2860 | break; 2861 | 2862 | case '\"': 2863 | minify_string(&json, (char**)&into); 2864 | break; 2865 | 2866 | default: 2867 | into[0] = json[0]; 2868 | json++; 2869 | into++; 2870 | } 2871 | } 2872 | 2873 | /* and null-terminate. */ 2874 | *into = '\0'; 2875 | } 2876 | 2877 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) 2878 | { 2879 | if (item == NULL) 2880 | { 2881 | return false; 2882 | } 2883 | 2884 | return (item->type & 0xFF) == cJSON_Invalid; 2885 | } 2886 | 2887 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) 2888 | { 2889 | if (item == NULL) 2890 | { 2891 | return false; 2892 | } 2893 | 2894 | return (item->type & 0xFF) == cJSON_False; 2895 | } 2896 | 2897 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) 2898 | { 2899 | if (item == NULL) 2900 | { 2901 | return false; 2902 | } 2903 | 2904 | return (item->type & 0xff) == cJSON_True; 2905 | } 2906 | 2907 | 2908 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) 2909 | { 2910 | if (item == NULL) 2911 | { 2912 | return false; 2913 | } 2914 | 2915 | return (item->type & (cJSON_True | cJSON_False)) != 0; 2916 | } 2917 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) 2918 | { 2919 | if (item == NULL) 2920 | { 2921 | return false; 2922 | } 2923 | 2924 | return (item->type & 0xFF) == cJSON_NULL; 2925 | } 2926 | 2927 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) 2928 | { 2929 | if (item == NULL) 2930 | { 2931 | return false; 2932 | } 2933 | 2934 | return (item->type & 0xFF) == cJSON_Number; 2935 | } 2936 | 2937 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) 2938 | { 2939 | if (item == NULL) 2940 | { 2941 | return false; 2942 | } 2943 | 2944 | return (item->type & 0xFF) == cJSON_String; 2945 | } 2946 | 2947 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) 2948 | { 2949 | if (item == NULL) 2950 | { 2951 | return false; 2952 | } 2953 | 2954 | return (item->type & 0xFF) == cJSON_Array; 2955 | } 2956 | 2957 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) 2958 | { 2959 | if (item == NULL) 2960 | { 2961 | return false; 2962 | } 2963 | 2964 | return (item->type & 0xFF) == cJSON_Object; 2965 | } 2966 | 2967 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) 2968 | { 2969 | if (item == NULL) 2970 | { 2971 | return false; 2972 | } 2973 | 2974 | return (item->type & 0xFF) == cJSON_Raw; 2975 | } 2976 | 2977 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) 2978 | { 2979 | if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) 2980 | { 2981 | return false; 2982 | } 2983 | 2984 | /* check if type is valid */ 2985 | switch (a->type & 0xFF) 2986 | { 2987 | case cJSON_False: 2988 | case cJSON_True: 2989 | case cJSON_NULL: 2990 | case cJSON_Number: 2991 | case cJSON_String: 2992 | case cJSON_Raw: 2993 | case cJSON_Array: 2994 | case cJSON_Object: 2995 | break; 2996 | 2997 | default: 2998 | return false; 2999 | } 3000 | 3001 | /* identical objects are equal */ 3002 | if (a == b) 3003 | { 3004 | return true; 3005 | } 3006 | 3007 | switch (a->type & 0xFF) 3008 | { 3009 | /* in these cases and equal type is enough */ 3010 | case cJSON_False: 3011 | case cJSON_True: 3012 | case cJSON_NULL: 3013 | return true; 3014 | 3015 | case cJSON_Number: 3016 | if (compare_double(a->valuedouble, b->valuedouble)) 3017 | { 3018 | return true; 3019 | } 3020 | return false; 3021 | 3022 | case cJSON_String: 3023 | case cJSON_Raw: 3024 | if ((a->valuestring == NULL) || (b->valuestring == NULL)) 3025 | { 3026 | return false; 3027 | } 3028 | if (strcmp(a->valuestring, b->valuestring) == 0) 3029 | { 3030 | return true; 3031 | } 3032 | 3033 | return false; 3034 | 3035 | case cJSON_Array: 3036 | { 3037 | cJSON *a_element = a->child; 3038 | cJSON *b_element = b->child; 3039 | 3040 | for (; (a_element != NULL) && (b_element != NULL);) 3041 | { 3042 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3043 | { 3044 | return false; 3045 | } 3046 | 3047 | a_element = a_element->next; 3048 | b_element = b_element->next; 3049 | } 3050 | 3051 | /* one of the arrays is longer than the other */ 3052 | if (a_element != b_element) { 3053 | return false; 3054 | } 3055 | 3056 | return true; 3057 | } 3058 | 3059 | case cJSON_Object: 3060 | { 3061 | cJSON *a_element = NULL; 3062 | cJSON *b_element = NULL; 3063 | cJSON_ArrayForEach(a_element, a) 3064 | { 3065 | /* TODO This has O(n^2) runtime, which is horrible! */ 3066 | b_element = get_object_item(b, a_element->string, case_sensitive); 3067 | if (b_element == NULL) 3068 | { 3069 | return false; 3070 | } 3071 | 3072 | if (!cJSON_Compare(a_element, b_element, case_sensitive)) 3073 | { 3074 | return false; 3075 | } 3076 | } 3077 | 3078 | /* doing this twice, once on a and b to prevent true comparison if a subset of b 3079 | * TODO: Do this the proper way, this is just a fix for now */ 3080 | cJSON_ArrayForEach(b_element, b) 3081 | { 3082 | a_element = get_object_item(a, b_element->string, case_sensitive); 3083 | if (a_element == NULL) 3084 | { 3085 | return false; 3086 | } 3087 | 3088 | if (!cJSON_Compare(b_element, a_element, case_sensitive)) 3089 | { 3090 | return false; 3091 | } 3092 | } 3093 | 3094 | return true; 3095 | } 3096 | 3097 | default: 3098 | return false; 3099 | } 3100 | } 3101 | 3102 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 3103 | { 3104 | return global_hooks.allocate(size); 3105 | } 3106 | 3107 | CJSON_PUBLIC(void) cJSON_free(void *object) 3108 | { 3109 | global_hooks.deallocate(object); 3110 | } 3111 | --------------------------------------------------------------------------------