Clean up and update ra_tls demo

This commit is contained in:
Zheng, Qi 2022-01-21 17:12:40 +08:00 committed by Zongmin.Gu
parent 2810b8e4ce
commit 2d7fbefcc2
32 changed files with 634 additions and 2045 deletions

@ -865,11 +865,14 @@ jobs:
container-name: ${{ github.job }} container-name: ${{ github.job }}
build-envs: 'OCCLUM_RELEASE_BUILD=1' build-envs: 'OCCLUM_RELEASE_BUILD=1'
- name: Download and build the pakckages - name: Download source code
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./prepare_and_build_package.sh" run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./download_and_prepare.sh"
- name: Build and install gRPC+RATLS
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./build_and_install.sh musl"
- name: Build occlum instances - name: Build occlum instances
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./build_occlum_instance.sh" run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./build_occlum_instance.sh musl"
- name: Run gRPC server - name: Run gRPC server
run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./run.sh server &" run: docker exec ${{ env.CONTAINER_NAME }} bash -c "cd /root/occlum/demos/ra_tls; ./run.sh server &"

@ -1,23 +1,74 @@
# gRPC Package With RA-TLS # gRPC Package With RA-TLS
## Simple GRPC protocol for the demo
#### Executing the demo in Occlum * Server side, holds a [`json file`](./secret_config.json) including secret name and the secret's base64 encoded string.
The following command will download the gRPC source code and apply the ra-tls patches, then build gRPC source code and demo. * Client side, request the secret by the secret name.
## Example libraries/executables in the demo
* libhw_grpc_proto.so
* libgrpc_ratls_client.so
* libgrpc_ratls_server.so
* client
* server
### APIs defined for sample server and client
* Server
``` ```
./prepare_and_build_package.sh int gr_start_server(
const char *server_addr, // grpc server address+port, such as "localhost:50051"
const char *config_json, // ratls handshake config json file
const char *secret_json // secret config json file
);
```
* Client
```
int gr_client_get_secret(
const char *server_addr, // grpc server address+port, such as "localhost:50051"
const char *config_json, // ratls handshake config json file
const char *name, // secret name to be requested
const char *secret_file // secret file to be saved
);
```
All source could be found on [`example`](./grpc/v1.38.1/examples/cpp/ratls/)
## Executing the demo in Occlum
The following command will download prerequisite source and the gRPC source code.
```
./download_and_prepare.sh
```
The following command will patch the gRPC source code and do the build and install.
```
./build_and_install.sh
```
If musl-libc version is expected.
```
./build_and_install.sh musl
``` ```
The following command will generate the client and server occlum images. It automatically parses the mr_enclave and mr_signer of the client, and write the value into dynamic_config.json. If you want to verify the other measurements of client, please modify the dynamic_config.json before run the script. The following command will generate the client and server occlum images. It automatically parses the mr_enclave and mr_signer of the client, and write the value into dynamic_config.json. If you want to verify the other measurements of client, please modify the dynamic_config.json before run the script.
``` ```
./build_occlum_instance.sh ./build_occlum_instance.sh
``` ```
If previous build choice is `musl`.
```
./build_occlum_instance.sh musl
```
Run the gRPC server & client in occlum. Run the gRPC server & client in occlum.
``` ```
./run.sh server & ./run.sh server &
./run.sh client ./run.sh client <request_secret_name> ( cert, key )
``` ```
***Note:*** 1. The demo runs in the same machine by default. If you want to run server and client in different machines. Please modify the examples/cpp/ratls. ***Note:*** 1. The demo runs in the same machine by default. If you want to run server and client in different machines. Please modify the examples/cpp/ratls.

@ -0,0 +1,77 @@
#!/bin/bash
set -e
source ./env.sh
BUILD_TYPE=Release
if [[ $1 == "musl" ]]; then
echo "*** Build and run musl-libc demo ***"
CC=occlum-gcc
CXX=occlum-g++
DCAP_LIB_PATH="/opt/occlum/toolchains/dcap_lib/musl"
INSTALL_PREFIX="/usr/local/occlum/x86_64-linux-musl"
else
echo "*** Build and run glibc demo ***"
CC=gcc
CXX=g++
DCAP_LIB_PATH="/opt/occlum/toolchains/dcap_lib/glibc"
INSTALL_PREFIX="/usr/local"
fi
# Build and install cJSON
function build_cjson() {
pushd cJSON-${CJSON_VER}
rm -rf build && mkdir build && cd build
cmake -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
-DCMAKE_C_COMPILER=${CC} ..
make install
popd
}
function build_grpc_ratls() {
# Copy occlum dcap lib first
cp ${DCAP_LIB_PATH}/libocclum_dcap.so* ${INSTALL_PREFIX}/lib
cp /opt/occlum/toolchains/dcap_lib/inc/occlum_dcap.h ${INSTALL_PREFIX}/include/
# Copy ratls added/updated files to grpc source
cp -rf grpc/${GRPC_VERSION}/* ${GRPC_PATH}/
ABSEIL_PATH=${GRPC_PATH}/third_party/abseil-cpp
# build and install abseil library
# https://abseil.io/docs/cpp/quickstart-cmake.html
pushd ${ABSEIL_PATH}
rm -rf build && mkdir build && cd build
cmake -DCMAKE_CXX_STANDARD=11 -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} \
-DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_COMPILER=${CC} ..
make -j `nproc`
make install
popd
# Build grpc + ratls
pushd ${GRPC_PATH}
rm -rf build && mkdir build && cd build
cmake -DgRPC_INSTALL=ON -DgRPC_ABSL_PROVIDER=package -DgRPC_BUILD_TESTS=OFF \
-DgRPC_BUILD_CSHARP_EXT=OFF -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
-DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
-DDEFINE_SGX_RA_TLS_OCCLUM_BACKEND=ON \
-DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_COMPILER=${CC} \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
make -j `nproc`
make install
popd
# Build grpc ratls client and server demo
pushd ${GRPC_PATH}/examples/cpp/ratls
rm -rf build && mkdir -p build
cd build
cmake -D CMAKE_PREFIX_PATH=${INSTALL_PREFIX} -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_COMPILER=${CC} ..
make -j `nproc`
popd
}
build_cjson
build_grpc_ratls

@ -1,9 +1,8 @@
#!/bin/bash #!/bin/bash
occlum_glibc=/opt/occlum/glibc/lib/ set -e
set -ex
get_mr() { function get_mr() {
sgx_sign dump -enclave ../occlum_instance_$1/build/lib/libocclum-libos.signed.so -dumpfile ../metadata_info_$1.txt sgx_sign dump -enclave ../occlum_$1/build/lib/libocclum-libos.signed.so -dumpfile ../metadata_info_$1.txt
if [ "$2" == "mr_enclave" ]; then if [ "$2" == "mr_enclave" ]; then
sed -n -e '/enclave_hash.m/,/metadata->enclave_css.body.isv_prod_id/p' ../metadata_info_$1.txt |head -3|tail -2|xargs|sed 's/0x//g'|sed 's/ //g' sed -n -e '/enclave_hash.m/,/metadata->enclave_css.body.isv_prod_id/p' ../metadata_info_$1.txt |head -3|tail -2|xargs|sed 's/0x//g'|sed 's/ //g'
elif [ "$2" == "mr_signer" ]; then elif [ "$2" == "mr_signer" ]; then
@ -11,43 +10,56 @@ get_mr() {
fi fi
} }
build_instance() { function build_instance() {
# 1. Init Occlum Workspace # 1. Init Occlum Workspace
rm -rf occlum_instance_$postfix rm -rf occlum_$postfix
mkdir occlum_instance_$postfix mkdir occlum_$postfix
pushd occlum_instance_$postfix pushd occlum_$postfix
occlum init occlum init
new_json="$(jq '.resource_limits.user_space_size = "320MB" | new_json="$(jq '.resource_limits.user_space_size = "500MB"' Occlum.json)" && \
.process.default_mmap_size = "256MB"' Occlum.json)" && \
echo "${new_json}" > Occlum.json echo "${new_json}" > Occlum.json
# 2. Copy files into Occlum Workspace and Build
#cp ../dynamic_config.json image/etc/dynamic_config.json
cp ../dynamic_config.json image/dynamic_config.json
#cp ../dynamic_config_$postfix.json image/dynamic_config.json
if [ "$postfix" == "server" ]; then if [ "$postfix" == "server" ]; then
# Server will verify client's mr_enclave and mr_signer
jq ' .verify_mr_enclave = "on" | jq ' .verify_mr_enclave = "on" |
.verify_mr_signer = "on" | .verify_mr_signer = "on" |
.verify_isv_prod_id = "off" |
.verify_isv_svn = "off" |
.sgx_mrs[0].mr_enclave = ''"'`get_mr client mr_enclave`'" | .sgx_mrs[0].mr_enclave = ''"'`get_mr client mr_enclave`'" |
.sgx_mrs[0].mr_signer = ''"'`get_mr client mr_signer`'" ' ../dynamic_config.json > image/dynamic_config.json .sgx_mrs[0].mr_signer = ''"'`get_mr client mr_signer`'" ' ../ra_config_template.json > dynamic_config.json
fi
mkdir -p image/usr/share/grpc if [ "$libnss_require" == "y" ]; then
cp -rf /share/grpc/* image/usr/share/grpc/
cp $occlum_glibc/libdl.so.2 image/$occlum_glibc
cp $occlum_glibc/librt.so.1 image/$occlum_glibc
cp $occlum_glibc/libm.so.6 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libtinfo.so.5 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libnss*.so.2 image/$occlum_glibc cp /lib/x86_64-linux-gnu/libnss*.so.2 image/$occlum_glibc
cp /lib/x86_64-linux-gnu/libresolv.so.2 image/$occlum_glibc cp /lib/x86_64-linux-gnu/libresolv.so.2 image/$occlum_glibc
cp -rf /etc/hostname image/etc/ fi
cp -rf /etc/ssl image/etc/
cp -rf /etc/passwd image/etc/ bomfile="../grpc_ratls_server.yaml"
cp -rf /etc/group image/etc/ else
cp -rf /etc/nsswitch.conf image/etc/ # Client verify nothing from server
cp -rf /grpc/examples/cpp/ratls/build/* image/bin/ jq ' .verify_mr_enclave = "off" |
.verify_mr_signer = "off" |
.verify_isv_prod_id = "off" |
.verify_isv_svn = "off" ' ../ra_config_template.json > dynamic_config.json
bomfile="../grpc_ratls_client.yaml"
fi
rm -rf image
copy_bom -f $bomfile --root image --include-dir /opt/occlum/etc/template
occlum build occlum build
popd popd
} }
if [[ $1 == "musl" ]]; then
echo "*** Build and musl-libc Occlum instance ***"
else
echo "*** Build and run glibc Occlum instance ***"
# glibc version requires libnss
libnss_require="y"
occlum_glibc=/opt/occlum/glibc/lib/
fi
postfix=client postfix=client
build_instance build_instance
postfix=server postfix=server

@ -0,0 +1,46 @@
#!/bin/bash
set -e
source ./env.sh
# Download and update cmake
function dl_and_build_cmake() {
# Ubuntu 20.04 has newer enough cmake version
if [ -f "/etc/os-release" ]; then
local os_name=$(cat /etc/os-release)
if [[ $os_name =~ "Ubuntu" && $os_name =~ "20.04" ]]; then
return
fi
fi
rm -rf cmake-3.20.2*
wget https://github.com/Kitware/CMake/releases/download/v3.20.2/cmake-3.20.2.tar.gz
tar -zxvf cmake-3.20.2.tar.gz
pushd cmake-3.20.2
./bootstrap
make install
popd
}
# GRPC env
function dl_grpc() {
# GRPC source code
rm -rf ${GRPC_PATH}
git clone https://github.com/grpc/grpc -b ${GRPC_VERSION} ${GRPC_PATH}
pushd ${GRPC_PATH} \
&& git checkout ${GRPC_VERSION} \
&& git submodule update --init
popd
}
# Download cJSON
function dl_cjson() {
rm -rf cJSON*
wget https://github.com/DaveGamble/cJSON/archive/refs/tags/v${CJSON_VER}.tar.gz
tar zxvf v${CJSON_VER}.tar.gz
}
dl_and_build_cmake
dl_grpc
dl_cjson

7
demos/ra_tls/env.sh Normal file

@ -0,0 +1,7 @@
#!/bin/bash
set -e
GRPC_VERSION=v1.38.1
GRPC_PATH=grpc-src
CJSON_VER=1.7.15

@ -1,46 +0,0 @@
#
# Copyright (c) 2022 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -ex
export ABSEIL_PATH=${GRPC_PATH}/third_party/abseil-cpp
if [ ! -d "${BUILD_TYPE}" ]; then
BUILD_TYPE=Release
fi
# build and install abseil library
# https://abseil.io/docs/cpp/quickstart-cmake.html
if [ ! -d "${ABSEIL_PATH}/build" ]; then
mkdir -p ${ABSEIL_PATH}/build
cd ${ABSEIL_PATH}/build
cmake -DCMAKE_CXX_STANDARD=11 -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
make -j `nproc`
make install
cd -
fi
# build and install grpc library
mkdir -p ${GRPC_PATH}/build
cd ${GRPC_PATH}/build
cmake -DgRPC_INSTALL=ON -DgRPC_ABSL_PROVIDER=package -DgRPC_BUILD_TESTS=OFF \
-DgRPC_BUILD_CSHARP_EXT=OFF -DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
-DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF -DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
-DDEFINE_SGX_RA_TLS_OCCLUM_BACKEND=ON \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
make -j `nproc`
make install
cd -

@ -54,6 +54,7 @@ option(DEFINE_SGX_RA_TLS_OCCLUM_BACKEND "SGX Occlum Backend" OFF)
if(DEFINE_SGX_RA_TLS_OCCLUM_BACKEND) if(DEFINE_SGX_RA_TLS_OCCLUM_BACKEND)
message("SGX_RA_TLS_OCCLUM_BACKEND is defined") message("SGX_RA_TLS_OCCLUM_BACKEND is defined")
include_directories(/opt/intel/sgxsdk/include)
add_definitions(-DSGX_RA_TLS_OCCLUM_BACKEND) add_definitions(-DSGX_RA_TLS_OCCLUM_BACKEND)
endif() endif()
@ -2089,7 +2090,8 @@ if(DEFINE_SGX_RA_TLS_OCCLUM_BACKEND)
gpr gpr
${_gRPC_SSL_LIBRARIES} ${_gRPC_SSL_LIBRARIES}
address_sorting address_sorting
libdcap_quote.a occlum_dcap
cjson
) )
endif() endif()
@ -2793,7 +2795,6 @@ add_library(grpc++
src/cpp/sgx/sgx_ra_tls_backends.cc src/cpp/sgx/sgx_ra_tls_backends.cc
src/cpp/sgx/sgx_ra_tls_occlum.cc src/cpp/sgx/sgx_ra_tls_occlum.cc
src/cpp/sgx/sgx_ra_tls_utils.cc src/cpp/sgx/sgx_ra_tls_utils.cc
src/cpp/sgx/cjson/cJSON.c
) )
set_target_properties(grpc++ PROPERTIES set_target_properties(grpc++ PROPERTIES

@ -41,7 +41,7 @@ add_custom_command(
include_directories("${CMAKE_CURRENT_BINARY_DIR}") include_directories("${CMAKE_CURRENT_BINARY_DIR}")
# hw_grpc_proto # hw_grpc_proto
add_library(hw_grpc_proto add_library(hw_grpc_proto SHARED
${hw_grpc_srcs} ${hw_grpc_srcs}
${hw_grpc_hdrs} ${hw_grpc_hdrs}
${hw_proto_srcs} ${hw_proto_srcs}
@ -51,12 +51,14 @@ target_link_libraries(hw_grpc_proto
${_GRPC_GRPCPP} ${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF}) ${_PROTOBUF_LIBPROTOBUF})
# Targets greeter_[async_](client|server) foreach(_target grpc_ratls_client grpc_ratls_server)
add_library(${_target} SHARED "${_target}.cc")
target_link_libraries(${_target}
hw_grpc_proto)
endforeach()
foreach(_target client server) foreach(_target client server)
add_executable(${_target} "${_target}.cc") add_executable(${_target} "${_target}.cc")
target_link_libraries(${_target} target_link_libraries(${_target}
hw_grpc_proto grpc_ratls_${_target})
${_REFLECTION}
${_GRPC_GRPCPP}
${_PROTOBUF_LIBPROTOBUF})
endforeach() endforeach()

2
demos/ra_tls/grpc/v1.38.1/examples/cpp/ratls/build.sh Executable file → Normal file

@ -18,8 +18,6 @@ set -ex
export BUILD_TYPE=Release export BUILD_TYPE=Release
export EXP_PATH=`dirname $0` export EXP_PATH=`dirname $0`
${GRPC_PATH}/build_cpp.sh
# build c++ example # build c++ example
cd ${EXP_PATH} cd ${EXP_PATH}
mkdir -p build mkdir -p build

@ -15,72 +15,27 @@
* limitations under the License. * limitations under the License.
* *
*/ */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <grpcpp/grpcpp.h> #include "../grpc_ratls_client.h"
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../getopt.hpp"
using ratls::Greeter;
using ratls::HelloReply;
using ratls::HelloRequest;
struct argparser {
const char* config;
std::string server_address;
argparser() {
server_address = getarg("localhost:50051", "-host", "--host");
config = getarg("dynamic_config.json", "-cfg", "--config");
};
};
class GreeterClient {
public:
GreeterClient(std::shared_ptr<grpc::Channel> channel) : stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user) {
HelloRequest request;
request.set_name(user);
HelloReply reply;
grpc::ClientContext context;
grpc::Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.message();
} else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<Greeter::Stub> stub_;
};
void run_client() {
argparser args;
auto cred = grpc::sgx::TlsCredentials(args.config);
auto channel = grpc::CreateChannel(args.server_address, cred);
GreeterClient greeter(channel);
std::string user_a = greeter.SayHello("a");
std::string user_b = greeter.SayHello("b");
std::cout << "Greeter received: " << user_a << ", "<< user_b << std::endl;
};
int main(int argc, char** argv) { int main(int argc, char** argv) {
run_client(); // Parse arguments
if (argc < 3) {
printf("[ERROR] At least one argument must be provided\n\n");
printf("Usage: client [<request_name>] [<secret_file_to_be_saved>]\n");
return -1;
}
gr_client_get_secret(
"localhost:50051",
"dynamic_config.json",
argv[1],
argv[2]
);
return 0; return 0;
} }

@ -1,316 +0,0 @@
// Simple getopt replacement class (C++11).
// - rlyeh, zlib/libpng licensed.
// https://github.com/r-lyeh-archived/getopt/blob/master/getopt.hpp
// Two APIs provided:
//
// 1) Simple functional api `getarg(...)`.
// - No initialization required: (argc, argv) pair automatically retrieved.
// - First argument is default option value, then all option indentifiers follow.
//
// int main() {
// bool help = getarg( false, "-h", "--help", "-?" );
// int version = getarg( 0, "-v", "--version", "--show-version" );
// int depth = getarg( 1, "-d", "--depth", "--max-depth");
// std::string file = getarg( "", "-f", "--file" );
// [...]
// }
//
// 2) Simple OOP map-based api `getopt class`. Initialization (argc, argv) pair required.
//
// This getopt class is a std::map replacement where key/value are std::string types.
// Given invokation './app.out --user=me --pass=123 -h' this class delivers not only:
// map[0] = "./app.out", map[1] = "--user=me", map[2]="--pass=123", map[3]='-h'
// but also, map["--user"]="me", map["--pass"]="123" and also, map["-h"]=true
//
// Additional API:
// - .cmdline() for a print app invokation string
// - .str() for pretty map printing
// - .size() number of arguments (equivalent to argc), rather than std::map.size()
//
// int main( int argc, const char **argv ) {
// getopt args( argc, argv );
// if( args.has("-h") || args.has("--help") || args.has("-?") || args.size() == 1 ) {
// std::cout << args["0"] << " [-?|-h|--help] [-v|--version] [--depth=number]" << std::endl;
// return 0;
// }
// if( args.has("-v") || args.has("--version") ) {
// std::cout << args["0"] << " sample v1.0.0. Compiled on " << __DATE__ << std::endl;
// }
// if( args.has("--depth") ) {
// int depth = atoi( args["--depth"].c_str() );
// std::cout << "depth set to " << depth << std::endl;
// }
// [...]
// }
#pragma once
#include <map>
#include <string>
#include <sstream>
#include <vector>
#ifdef _WIN32
#include <io.h>
#include <winsock2.h>
#include <shellapi.h>
#pragma comment(lib, "Shell32.lib")
#else
#include <fstream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#endif
#define GETOPT_VERSION "1.0.0" // (2016/04/18) Initial version
namespace getopt_utils
{
// string conversion
template< typename T >
inline T as( const std::string &self ) {
T t;
return (std::istringstream(self) >> t) ? t :
(T)(self.size() && (self != "0") && (self != "false"));
}
template<>
inline char as( const std::string &self ) {
return self.size() == 1 ? (char)(self[0]) : (char)(as<int>(self));
}
template<>
inline signed char as( const std::string &self ) {
return self.size() == 1 ? (signed char)(self[0]) : (signed char)(as<int>(self));
}
template<>
inline unsigned char as( const std::string &self ) {
return self.size() == 1 ? (unsigned char)(self[0]) : (unsigned char)(as<int>(self));
}
template<>
inline const char *as( const std::string &self ) {
return self.c_str();
}
template<>
inline std::string as( const std::string &self ) {
return self;
}
// token split
inline size_t split( std::vector<std::string> &tokens, const std::string &self, const std::string &delimiters ) {
std::string str;
tokens.clear();
for( auto &ch : self ) {
if( delimiters.find_first_of( ch ) != std::string::npos ) {
if( str.size() ) tokens.push_back( str ), str = "";
tokens.push_back( std::string() + ch );
} else str += ch;
}
return str.empty() ? tokens.size() : ( tokens.push_back( str ), tokens.size() );
};
// portable cmdline
inline std::vector<std::string> cmdline() {
std::vector<std::string> args;
std::string arg;
# ifdef _WIN32
int argv;
auto *list = CommandLineToArgvW( GetCommandLineW(), &argv );
if( list ) {
for( int i = 0; i < argv; ++i ) {
std::wstring ws( list[i] );
args.push_back( std::string( ws.begin(), ws.end() ) );
}
LocalFree(list);
}
# else
pid_t pid = getpid();
char fname[32] = {};
sprintf(fname, "/proc/%d/cmdline", pid);
std::ifstream ifs(fname);
if( ifs.good() ) {
std::stringstream ss;
ifs >> ss.rdbuf();
arg = ss.str();
}
for( auto end = arg.size(), i = end - end; i < end; ++i ) {
auto st = i;
while (i < arg.size() && arg[i] != '\0') ++i;
args.push_back( arg.substr(st, i - st) );
}
# endif
return args;
}
}
// main map class; explicit initialization
struct getopt : public std::map< std::string, std::string >
{
using super = std::map< std::string, std::string >;
getopt( int argc, const char **argv ) : super() {
// reconstruct vector
std::vector<std::string> args( argc, std::string() );
for( int i = 0; i < argc; ++i ) {
args[ i ] = argv[ i ];
}
// create key=value and key= args as well
for( auto &it : args ) {
std::vector<std::string> tokens;
auto size = getopt_utils::split( tokens, it, "=" );
if( size == 3 && tokens[1] == "=" )
(*this)[ tokens[0] ] = tokens[2];
else
if( size == 2 && tokens[1] == "=" )
(*this)[ tokens[0] ] = true;
else
if( size == 1 && tokens[0] != argv[0] )
(*this)[ tokens[0] ] = true;
}
// recreate args
while( argc-- ) {
(*this)[ std::to_string(argc) ] = std::string( argv[argc] );
}
}
getopt( const std::vector<std::string> &args ) : super() {
std::vector<const char *> argv;
for( auto &it : args ) {
argv.push_back( it.c_str() );
}
*this = getopt( argv.size(), argv.data() );
}
size_t size() const {
unsigned i = 0;
while( has(std::to_string(i)) ) ++i;
return i;
}
bool has( const std::string &op ) const {
return this->find(op) != this->end();
}
std::string str() const {
std::stringstream ss;
std::string sep;
for( auto &it : *this ) {
ss << sep << it.first << "=" << it.second;
sep = ',';
}
return ss.str();
}
std::string cmdline() const {
std::stringstream cmd;
std::string sep;
// concatenate args
for( auto end = size(), arg = end - end; arg < end; ++arg ) {
cmd << sep << this->find(std::to_string(arg))->second;
sep = ' ';
}
return cmd.str();
}
};
// variadic syntax sugars {
template< typename T >
inline T getarg( const T &defaults, const char *argv ) {
static struct getopt map( getopt_utils::cmdline() );
return map.has( argv ) ? getopt_utils::as<T>(map[ argv ]) : defaults;
}
template< typename T, typename... Args >
inline T getarg( const T &defaults, const char *arg0, Args... argv ) {
T t = getarg<T>( defaults, arg0 );
return t == defaults ? getarg<T>( defaults, argv... ) : t;
}
inline const char * getarg( const char *defaults, const char *argv ) {
static struct getopt map( getopt_utils::cmdline() );
return map.has( argv ) ? getopt_utils::as<const char *>(map[ argv ]) : defaults;
}
template< typename... Args >
inline const char * getarg( const char *defaults, const char *arg0, Args... argv ) {
const char *t = getarg( defaults, arg0 );
return t == defaults ? getarg( defaults, argv... ) : t;
}
// }
#ifdef GETOPT_BUILD_DEMO
#include <iostream>
#include <stdlib.h>
int main( int argc, const char **argv ) {
auto show_help = [&]() {
std::cout << argv[0] << " [-h|--help|-?] [-f=path|--file=path] [-v|--version] [-d=number|--depth=number|--max-depth=number]" << std::endl;
exit(0);
};
// Simple functional api. No initialization required.
bool help = getarg( false, "-h", "--help", "-?" );
int version = getarg( 0, "-v", "--version", "--show-version" );
int depth = getarg( 0, "-d", "--depth", "--max-depth");
std::string file = getarg( "", "-f", "--file" );
if( help || argc <= 1 ) {
show_help();
}
if( version ) {
std::cout << argv[0] << " demo v1.0.0. Compiled on " << __DATE__ << std::endl;
}
if( depth ) {
std::cout << "provided depth: " << depth << std::endl;
}
if( !file.empty() ) {
std::cout << "provided file: " << file << std::endl;
}
// OOP map-based api. Explicit (argc, argv) initialization required.
struct getopt args( argc, argv );
if( args.has("-h") || args.has("--help") || args.has("-?") || args.size() == 1 ) {
show_help();
}
if( args.has("-v") || args.has("--version") ) {
std::cout << args["0"] << " demo v1.0.0. Compiled on " << __DATE__ << std::endl;
}
if( args.has("-d") || args.has("--depth") || args.has("--max-depth") ) {
std::string arg = args["-d"];
if( arg.empty() ) arg = args["--depth"];
if( arg.empty() ) arg = args["--max-depth"];
int depth = atoi( arg.c_str() );
std::cout << "provided depth: " << depth << std::endl;
}
if( args.has("-f") || args.has("--file") ) {
std::string arg = args["-f"];
if( arg.empty() ) arg = args["--file"];
std::string fname = arg;
std::cout << "provided file: " << fname << std::endl;
}
std::cout << "---" << std::endl;
std::cout << args.cmdline() << std::endl;
//std::cout << args.size() << " provided args: " << args.str() << std::endl;
}
#endif

@ -0,0 +1,148 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../grpc_ratls_client.h"
using ratls::GrSecret;
using ratls::SecretRequest;
using ratls::SecretReply;
// Client
class GrSecretClient {
public:
GrSecretClient(std::shared_ptr<grpc::Channel> channel) : stub_(GrSecret::NewStub(channel)) {}
std::string GetSecret(const std::string& name) {
SecretRequest request;
request.set_name(name);
SecretReply reply;
grpc::ClientContext context;
grpc::Status status = stub_->GetSecret(&context, request, &reply);
if (status.ok()) {
return reply.secret();
} else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<GrSecret::Stub> stub_;
};
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static size_t base64_decode_len(const char *b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len - 1] == '=' && b64input[len - 2] == '=') { //last two chars are =
padding = 2;
} else if (b64input[len - 1] == '=') { //last char is =
padding = 1;
}
return (len * 3) / 4 - padding;
}
/**
* base64_decode - Base64 decode
*/
void base64_decode(const char *b64input, unsigned char *dest, size_t dest_len) {
unsigned char dtable[256], *pos, block[4], tmp;
size_t i, count, olen;
size_t len = strlen(b64input);
memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++) {
dtable[base64_table[i]] = (unsigned char) i;
}
dtable['='] = 0;
olen = base64_decode_len(b64input);
if (olen > dest_len) {
printf("Base64 encoded length %ld is biggeer than %ld\n", olen, dest_len);
return;
}
pos = dest;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[(unsigned char)b64input[i]];
if (tmp == 0x80) {
continue;
}
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
}
}
}
int gr_client_get_secret(
const char *server_addr,
const char *config_json,
const char *name,
const char *secret_file
)
{
auto cred = grpc::sgx::TlsCredentials(config_json);
auto channel = grpc::CreateChannel(server_addr, cred);
GrSecretClient gr_secret(channel);
std::string secret = gr_secret.GetSecret(name);
//std::cout << "secret received: " << secret << std::endl;
//Decode From Base64
size_t len = base64_decode_len(secret.c_str());
if (len) {
char *secret_orig = (char *)malloc(len);
base64_decode(secret.c_str(), (unsigned char *)secret_orig, len);
std::string secret_string(secret_orig, secret_orig + len - 1);
//write to file
std::ofstream myfile;
myfile.open(secret_file);
myfile << secret_string;
myfile.close();
}
return 0;
}

@ -0,0 +1,20 @@
#ifndef _GRPC_RATLS_CLIENT_H_
#define _GRPC_RATLS_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
// client get secret
extern int gr_client_get_secret(
const char *server_addr, // grpc server address+port, such as "localhost:50051"
const char *config_json, // ratls handshake config json file
const char *name, // secret name to be requested
const char *secret_file // secret file to be saved
);
#ifdef __cplusplus
}
#endif
#endif // _GRPC_RATLS_CLIENT_H_

@ -0,0 +1,96 @@
/*
*
* Copyright (c) 2022 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../grpc_ratls_server.h"
using ratls::GrSecret;
using ratls::SecretRequest;
using ratls::SecretReply;
// Logic and data behind the server's behavior.
class GrSecretServiceImpl final: public GrSecret::Service {
public:
grpc::Status GetSecret(
grpc::ServerContext* context, const SecretRequest* request, SecretReply* reply) override {
//std::cout << "Request: " << request->name() << std::endl;
auto secret = this->get_secret_string(request->name().c_str());
if (!secret.empty()) {
reply->set_secret(secret);
return grpc::Status::OK;
} else {
return grpc::Status::CANCELLED;
}
}
GrSecretServiceImpl(const char* file) : secret_file(nullptr) {
this->secret_file = file;
}
private:
std::string get_secret_string(const char *name) {
std::string secret = "";
class grpc::sgx::json_engine secret_config(this->secret_file);
auto item = secret_config.get_item(secret_config.get_handle(), name);
if (item) {
secret = secret_config.print_item(item);
}
return secret;
}
const char *secret_file;
};
int gr_start_server(
const char *server_addr,
const char *config_json,
const char *secret_json
)
{
GrSecretServiceImpl service(secret_json);
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
grpc::ServerBuilder builder;
auto creds = grpc::sgx::TlsServerCredentials(config_json);
GPR_ASSERT(creds.get() != nullptr);
builder.AddListeningPort(server_addr, creds);
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_addr << std::endl;
server->Wait();
return 0;
}

@ -0,0 +1,19 @@
#ifndef _GRPC_RATLS_SERVER_H_
#define _GRPC_RATLS_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
// start server
extern int gr_start_server(
const char *server_addr, // grpc server address+port, such as "localhost:50051"
const char *config_json, // ratls handshake config json file
const char *secret_json // secret config json file
);
#ifdef __cplusplus
}
#endif
#endif // _GRPC_RATLS_SERVER_H_

@ -15,66 +15,15 @@
* limitations under the License. * limitations under the License.
* *
*/ */
#include "../grpc_ratls_server.h"
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/sgx/sgx_ra_tls.h>
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#ifdef BAZEL_BUILD
#include "examples/protos/ratls.grpc.pb.h"
#else
#include "ratls.grpc.pb.h"
#endif
#include "../getopt.hpp"
using ratls::Greeter;
using ratls::HelloReply;
using ratls::HelloRequest;
struct argparser {
const char* config;
std::string server_address;
argparser() {
server_address = getarg("localhost:50051", "-host", "--host");
config = getarg("dynamic_config.json", "-cfg", "--config");
};
};
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
grpc::Status SayHello(
grpc::ServerContext* context, const HelloRequest* request, HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return grpc::Status::OK;
}
};
void RunServer() {
argparser args;
GreeterServiceImpl service;
grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
grpc::ServerBuilder builder;
auto creds = grpc::sgx::TlsServerCredentials(args.config);
GPR_ASSERT(creds.get() != nullptr);
builder.AddListeningPort(args.server_address, creds);
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << args.server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
RunServer(); gr_start_server(
"localhost:50051",
"dynamic_config.json",
"secret_config.json"
);
return 0; return 0;
} }

@ -21,18 +21,18 @@ option objc_class_prefix = "HLW";
package ratls; package ratls;
// The greeting service definition. // The GRPC_RATLS secret service definition.
service Greeter { service GrSecret {
// Sends a greeting // Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {} rpc GetSecret (SecretRequest) returns (SecretReply) {}
} }
// The request message containing the user's name. // The request message containing the request's name.
message HelloRequest { message SecretRequest {
string name = 1; string name = 1;
} }
// The response message containing the greetings // The response message containing the secret (base64 encoded string)
message HelloReply { message SecretReply {
string message = 1; string secret = 1;
} }

@ -24,6 +24,8 @@
#include <grpcpp/security/credentials.h> #include <grpcpp/security/credentials.h>
#include <grpcpp/security/server_credentials.h> #include <grpcpp/security/server_credentials.h>
#include <cjson/cJSON.h>
namespace grpc { namespace grpc {
namespace sgx { namespace sgx {
@ -51,6 +53,30 @@ std::shared_ptr<grpc::ServerCredentials> TlsServerCredentials(const char* sgx_cf
std::shared_ptr<grpc::Channel> CreateSecureChannel( std::shared_ptr<grpc::Channel> CreateSecureChannel(
string target_str, std::shared_ptr<grpc::ChannelCredentials> channel_creds); string target_str, std::shared_ptr<grpc::ChannelCredentials> channel_creds);
class json_engine {
public:
json_engine();
json_engine(const char*);
~json_engine();
bool open(const char*);
void close();
cJSON* get_handle();
cJSON* get_item(cJSON* obj, const char* item);
char* print_item(cJSON* obj);
bool compare_item(cJSON* obj, const char* item);
private:
cJSON* handle;
};
} // namespace sgx } // namespace sgx
} // namespace grpc } // namespace grpc

@ -1,567 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
/* Could use sscanf for this? */
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
double d=item->valuedouble;
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
if (!str) return cJSON_strdup("");
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt)
{
char *out=0;
if (!item) return 0;
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item);break;
case cJSON_String: out=print_string(item);break;
case cJSON_Array: out=print_array(item,depth,fmt);break;
case cJSON_Object: out=print_object(item,depth,fmt);break;
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
out=(char*)cJSON_malloc(fmt?depth+3:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}

@ -1,140 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

@ -1,567 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
/* Could use sscanf for this? */
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item)
{
char *str;
double d=item->valuedouble;
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str)
{
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
if (!str) return cJSON_strdup("");
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt)
{
char *out=0;
if (!item) return 0;
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item);break;
case cJSON_String: out=print_string(item);break;
case cJSON_Array: out=print_array(item,depth,fmt);break;
case cJSON_Object: out=print_object(item,depth,fmt);break;
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to malloc the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
out=(char*)cJSON_malloc(fmt?depth+3:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}

@ -1,140 +0,0 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

@ -1,7 +1,7 @@
#include "sgx_ra_tls_utils.h" #include "sgx_ra_tls_utils.h"
#include "sgx_ra_tls_backend.h" #include "sgx_ra_tls_backend.h"
#include "sgx_quote_3.h" #include "sgx_quote_3.h"
#include "dcap_quote.h" #include "occlum_dcap.h"
namespace grpc { namespace grpc {
namespace sgx { namespace sgx {

@ -32,7 +32,7 @@ namespace sgx {
#include <openssl/asn1.h> #include <openssl/asn1.h>
#include "sgx_quote_3.h" #include "sgx_quote_3.h"
#include "dcap_quote.h" #include "occlum_dcap.h"
const char * RA_TLS_LONG_NAME = "RA-TLS Extension"; const char * RA_TLS_LONG_NAME = "RA-TLS Extension";
const char * RA_TLS_SHORT_NAME = "RA-TLS"; const char * RA_TLS_SHORT_NAME = "RA-TLS";

@ -27,10 +27,11 @@
#define grpc_printf printf #define grpc_printf printf
#define grpc_fprintf fprintf #define grpc_fprintf fprintf
#include <grpcpp/security/sgx/sgx_ra_tls.h>
namespace grpc { namespace grpc {
namespace sgx { namespace sgx {
#include "cjson/cJSON.h"
class library_engine { class library_engine {
public: public:
@ -53,30 +54,6 @@ class library_engine {
char* error; char* error;
}; };
class json_engine {
public:
json_engine();
json_engine(const char*);
~json_engine();
bool open(const char*);
void close();
cJSON* get_handle();
cJSON* get_item(cJSON* obj, const char* item);
char* print_item(cJSON* obj);
bool compare_item(cJSON* obj, const char* item);
private:
cJSON* handle;
};
void check_free(void* ptr); void check_free(void* ptr);
bool hex_to_byte(const char* src, char* dst, size_t dst_size); bool hex_to_byte(const char* src, char* dst, size_t dst_size);

@ -0,0 +1,15 @@
includes:
- base.yaml
targets:
- target: /bin/
copy:
- files:
- ../grpc-src/examples/cpp/ratls/build/client
- target: /
copy:
- files:
- dynamic_config.json
- target: /usr/share/grpc/
copy:
- files:
- ../grpc-src/etc/roots.pem

@ -0,0 +1,16 @@
includes:
- base.yaml
targets:
- target: /bin/
copy:
- files:
- ../grpc-src/examples/cpp/ratls/build/server
- target: /
copy:
- files:
- dynamic_config.json
- ../secret_config.json
- target: /usr/share/grpc/
copy:
- files:
- ../grpc-src/etc/roots.pem

@ -1,59 +0,0 @@
#!/bin/bash
INSTALL_PREFIX=/usr/local
apt-get update \
&& apt-get install -y --no-install-recommends apt-utils \
&& apt-get install -y \
ca-certificates \
build-essential \
autoconf \
libtool \
python3-pip \
python3-dev \
git \
wget \
unzip
mkdir -p ${INSTALL_PREFIX} \
&& wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh \
&& sh cmake-linux.sh -- --skip-license --prefix=${INSTALL_PREFIX} \
&& rm cmake-linux.sh
# Install cJSON
CJSON_PATH=/cJSON
git clone https://github.com/DaveGamble/cJSON.git ${CJSON_PATH}
pushd ${CJSON_PATH} \
&& make static \
&& cp -r *.a ${INSTALL_PREFIX}/lib \
&& mkdir -p ${INSTALL_PREFIX}/include/cjson \
&& cp -r *.h ${INSTALL_PREFIX}/include/cjson
popd
# GRPC env
GRPC_VERSION=v1.38.x
export GRPC_PATH=/grpc
# GRPC source code
git clone https://github.com/grpc/grpc -b ${GRPC_VERSION} ${GRPC_PATH}
pushd ${GRPC_PATH} \
&& pip3 install --upgrade pip setuptools==44.1.1 \
&& pip3 install -r requirements.txt \
&& git checkout v1.38.1 \
&& git submodule update --init
popd
cp -rf grpc/common/* ${GRPC_PATH}/
cp -rf grpc/v1.38.1/* ${GRPC_PATH}/
git clone https://github.com/occlum/occlum
pushd occlum
make submodule
cd demos/remote_attestation/dcap/dcap_lib
cargo build --all-targets
cp target/debug/libdcap_quote.a /usr/local/lib/
cp ../c_app/dcap_quote.h /usr/local/include/
popd
pushd ${GRPC_PATH}/examples/cpp/ratls
./build.sh
popd

@ -1,8 +1,8 @@
{ {
"verify_mr_enclave" : "off", "verify_mr_enclave" : "on",
"verify_mr_signer" : "off", "verify_mr_signer" : "on",
"verify_isv_prod_id" : "off", "verify_isv_prod_id" : "on",
"verify_isv_svn" : "off", "verify_isv_svn" : "on",
"sgx_mrs": [ "sgx_mrs": [
{ {
"mr_enclave" : "", "mr_enclave" : "",

@ -1,7 +1,9 @@
#!/bin/bash #!/bin/bash
set -ex set -e
postfix=$1 postfix=$1
request=$2
file=${3:-/host/secret}
if [ "$postfix" != "server" ] && [ "$postfix" != "client" ]; then if [ "$postfix" != "server" ] && [ "$postfix" != "client" ]; then
echo "input error args, it should be:" echo "input error args, it should be:"
@ -10,6 +12,6 @@ if [ "$postfix" != "server" ] && [ "$postfix" != "client" ]; then
exit 1 exit 1
fi fi
pushd occlum_instance_$postfix pushd occlum_$postfix
occlum run /bin/$postfix occlum run /bin/$postfix ${request} ${file}
popd popd

@ -0,0 +1,4 @@
{
"cert" : "dGVzdCBzYW1wbGUgY2VydGlmaWNhdGVzCg==",
"key" : "dGVzdCBzYW1wbGUga2V5Cg=="
}