Clean up and update ra_tls demo
This commit is contained in:
parent
2810b8e4ce
commit
2d7fbefcc2
9
.github/workflows/hw_mode_test.yml
vendored
9
.github/workflows/hw_mode_test.yml
vendored
@ -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.
|
||||||
|
77
demos/ra_tls/build_and_install.sh
Executable file
77
demos/ra_tls/build_and_install.sh
Executable file
@ -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
|
||||||
|
46
demos/ra_tls/download_and_prepare.sh
Executable file
46
demos/ra_tls/download_and_prepare.sh
Executable file
@ -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
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
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);
|
||||||
|
15
demos/ra_tls/grpc_ratls_client.yaml
Normal file
15
demos/ra_tls/grpc_ratls_client.yaml
Normal file
@ -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
|
16
demos/ra_tls/grpc_ratls_server.yaml
Normal file
16
demos/ra_tls/grpc_ratls_server.yaml
Normal file
@ -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
|
||||||
|
4
demos/ra_tls/secret_config.json
Normal file
4
demos/ra_tls/secret_config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"cert" : "dGVzdCBzYW1wbGUgY2VydGlmaWNhdGVzCg==",
|
||||||
|
"key" : "dGVzdCBzYW1wbGUga2V5Cg=="
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user