1
0
Fork 0

Adding upstream version 2.0.24.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-18 11:18:17 +01:00
parent e508fcfeb9
commit afb0a8fea7
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
118 changed files with 45084 additions and 0 deletions

213
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,213 @@
name: libnetconf2 CI
on:
push:
branches:
- master
- devel
pull_request:
branches:
- master
- devel
env:
DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev
jobs:
git-branch:
name: Get git branch
runs-on: ubuntu-18.04
outputs:
branch-name: ${{ steps.get-git-branch.outputs.branch-name }}
steps:
- id: get-git-branch
run: |
if ${{ github.event_name == 'push' }}
then export GIT_BRANCH=`echo ${{ github.ref }} | cut -d'/' -f 3`
else
export GIT_BRANCH=${{ github.base_ref }}
fi
echo "::set-output name=branch-name::$GIT_BRANCH"
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
needs: git-branch
strategy:
fail-fast: false
matrix:
config:
- {
name: "Release, Ubuntu 18.04, gcc",
os: "ubuntu-18.04",
build-type: "Release",
dep-build-type: "Release",
cc: "gcc",
options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON",
packages: "",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "Release, Ubuntu 18.04, clang",
os: "ubuntu-18.04",
build-type: "Release",
dep-build-type: "Release",
cc: "clang",
options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON",
packages: "",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "Debug, Ubuntu 18.04, gcc",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "gcc",
options: "-DENABLE_DNSSEC=ON",
packages: "valgrind",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "Debug, Ubuntu 18.04, clang",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "clang",
options: "-DENABLE_DNSSEC=ON",
packages: "valgrind",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "SSH Only",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "gcc",
options: "-DENABLE_TLS=OFF -DENABLE_SSH=ON",
packages: "valgrind",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "TLS Only",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "gcc",
options: "-DENABLE_TLS=ON -DENABLE_SSH=OFF",
packages: "valgrind",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "No SSH nor TLS",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "gcc",
options: "-DENABLE_TLS=OFF -DENABLE_SSH=OFF",
packages: "valgrind",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "ASAN and UBSAN",
os: "ubuntu-18.04",
build-type: "Debug",
dep-build-type: "Release",
cc: "clang",
options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF",
packages: "",
snaps: "",
make-prepend: "",
make-target: ""
}
- {
name: "ABI Check",
os: "ubuntu-latest",
build-type: "ABICheck",
dep-build-type: "Debug",
cc: "gcc",
options: "",
packages: "abi-dumper abi-compliance-checker snap",
snaps: "core universal-ctags",
make-prepend: "",
make-target: "abi-check"
}
steps:
- uses: actions/checkout@v2
- name: Deps-packages
shell: bash
run: |
sudo add-apt-repository ppa:kedazo/libssh-0.7.x -y
sudo apt-get update
sudo apt-get install $DEFAULT_PACKAGES ${{ matrix.config.packages }}
if ${{ matrix.config.snaps != '' }}
then sudo snap refresh; sudo snap install ${{ matrix.config.snaps }}
fi
- name: Deps-uncrustify
shell: bash
working-directory: ${{ github.workspace }}
run: |
git clone --branch uncrustify-0.71.0 https://github.com/uncrustify/uncrustify
cd uncrustify
mkdir build
cd build
CC=${{ matrix.config.cc }} cmake ..
make
sudo make install
if: ${{ matrix.config.name == 'Debug, Ubuntu 18.04, gcc' }}
- name: Deps-libyang
shell: bash
run: |
git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git
cd libyang
mkdir build
cd build
CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF ..
make -j2
sudo make install
- name: Deps-libval
shell: bash
run: |
git clone https://github.com/DNSSEC-Tools/DNSSEC-Tools.git dnssec-tools
cd dnssec-tools/dnssec-tools/validator
./configure
make -j2
sudo make install
- name: Configure
shell: bash
working-directory: ${{ github.workspace }}
run: |
mkdir build
cd build
CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} ..
- name: Build
shell: bash
working-directory: ${{ github.workspace }}/build
run: |
export LC_ALL=C.UTF-8
export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH
${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }}
- name: Test
shell: bash
working-directory: ${{ github.workspace }}/build
run: ctest --output-on-failure

145
.github/workflows/devel-push.yml vendored Normal file
View file

@ -0,0 +1,145 @@
name: libnetconf2 devel push
on:
push:
branches:
- devel
env:
DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev
COVERITY_PROJECT: CESNET%2Flibnetconf2
jobs:
git-branch:
name: Get git branch
runs-on: ubuntu-18.04
outputs:
branch-name: ${{ steps.get-git-branch.outputs.branch-name }}
steps:
- id: get-git-branch
run: |
if ${{ github.event_name == 'push' }}
then export GIT_BRANCH=`echo ${{ github.ref }} | cut -d'/' -f 3`
else
export GIT_BRANCH=${{ github.base_ref }}
fi
echo "::set-output name=branch-name::$GIT_BRANCH"
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
needs: git-branch
strategy:
fail-fast: false
matrix:
config:
- {
name: "Coverity",
os: "ubuntu-latest",
build-type: "Debug",
dep-build-type: "Debug",
cc: "clang",
options: "-DENABLE_DNSSEC=ON",
packages: "",
snaps: "",
make-prepend: "cov-build --dir cov-int",
make-target: ""
}
- {
name: "Codecov",
os: "ubuntu-latest",
build-type: "Debug",
dep-build-type: "Debug",
cc: "gcc",
options: "-DENABLE_COVERAGE=ON -DENABLE_DNSSEC=ON",
packages: "lcov",
snaps: "",
make-prepend: "",
make-target: ""
}
steps:
- uses: actions/checkout@v2
- name: Deps-packages
shell: bash
run: |
sudo add-apt-repository ppa:kedazo/libssh-0.7.x -y
sudo apt-get update
sudo apt-get install $DEFAULT_PACKAGES ${{ matrix.config.packages }}
if ${{ matrix.config.snaps != '' }}
then sudo snap install ${{ matrix.config.snaps }}
fi
- name: Deps-coverity
shell: bash
working-directory: ${{ github.workspace }}
run: |
wget -q https://scan.coverity.com/download/linux64 --post-data "token=$TOKEN&project=$COVERITY_PROJECT" -O coverity-tools.tar.gz
mkdir coverity-tools
tar xzf coverity-tools.tar.gz --strip 1 -C coverity-tools
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
if: ${{ matrix.config.name == 'Coverity' }}
- name: Deps-libyang
shell: bash
run: |
git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git
cd libyang
mkdir build
cd build
CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF ..
make -j2
sudo make install
- name: Deps-libval
shell: bash
run: |
git clone https://github.com/DNSSEC-Tools/DNSSEC-Tools.git dnssec-tools
cd dnssec-tools/dnssec-tools/validator
./configure
make -j2
sudo make install
- name: Configure
shell: bash
working-directory: ${{ github.workspace }}
run: |
mkdir build
cd build
CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} ..
- name: Build
shell: bash
working-directory: ${{ github.workspace }}/build
run: |
export LC_ALL=C.UTF-8
export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH
${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }}
- name: Test
shell: bash
working-directory: ${{ github.workspace }}/build
run: ctest --output-on-failure
- name: Upload to Coverity.com
shell: bash
working-directory: ${{ github.workspace }}/build
run: |
tar czvf libnetconf2.tgz cov-int
curl \
--form token=$TOKEN \
--form email=mvasko@cesnet.cz \
--form file=@libnetconf2.tgz \
--form version="`grep Version ./libnetconf2.pc | cut -d' ' -f 2`" \
--form description="libnetconf2 NETCONF library" \
https://scan.coverity.com/builds?project=$COVERITY_PROJECT
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
if: ${{ matrix.config.name == 'Coverity' }}
- name: Upload to Codecov.io
shell: bash
working-directory: ${{ github.workspace }}/build
run: bash <(curl -s https://codecov.io/bash)
if: ${{ matrix.config.name == 'Codecov' }}

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/pkg

19
.lgtm.yml Normal file
View file

@ -0,0 +1,19 @@
extraction:
cpp:
prepare:
packages: libpcre2-dev
after_prepare:
- cd $LGTM_WORKSPACE
- git clone -b devel https://github.com/CESNET/libyang.git
- cd libyang; mkdir build; cd build
- cmake -DCMAKE_INSTALL_PREFIX=$LGTM_WORKSPACE -DENABLE_BUILD_TESTS=OFF ..
- make -j2
- make install
configure:
command:
- mkdir build; cd build
- cmake -DCMAKE_INCLUDE_PATH=$LGTM_WORKSPACE/include -DCMAKE_LIBRARY_PATH=$LGTM_WORKSPACE/lib ..
index:
build_command:
- cd build
- make -j2

341
CMakeLists.txt Normal file
View file

@ -0,0 +1,341 @@
cmake_minimum_required(VERSION 2.8.12)
project(libnetconf2 C)
# include custom Modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules/")
include(GNUInstallDirs)
include(CheckFunctionExists)
include(CheckCSourceCompiles)
include(CheckIncludeFile)
include(UseCompat)
include(ABICheck)
include(SourceFormat)
include(GenDoc)
include(GenCoverage)
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
set(LIBNETCONF2_DESCRIPTION "NETCONF server and client library in C.")
# check the supported platform
if(NOT UNIX)
message(FATAL_ERROR "Only *nix like systems are supported.")
endif()
# osx specific
set(CMAKE_MACOSX_RPATH TRUE)
# set default build type if not specified by user
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
# see https://github.com/CESNET/libyang/pull/1692 for why CMAKE_C_FLAGS_<type> are not used directly
# normalize build type string
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_UPPER)
if ("${BUILD_TYPE_UPPER}" STREQUAL "RELEASE")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
set(CMAKE_C_FLAGS "-DNDEBUG -O2 ${CMAKE_C_FLAGS}")
elseif("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build Type" FORCE)
set(CMAKE_C_FLAGS "-g -O0 ${CMAKE_C_FLAGS}")
elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO")
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build Type" FORCE)
elseif("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBUG")
set(CMAKE_BUILD_TYPE "RelWithDebug" CACHE STRING "Build Type" FORCE)
elseif("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
set(CMAKE_BUILD_TYPE "ABICheck" CACHE STRING "Build Type" FORCE)
set(CMAKE_C_FLAGS "-g -Og ${CMAKE_C_FLAGS}")
elseif("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY")
set(CMAKE_BUILD_TYPE "DocOnly" CACHE STRING "Build Type" FORCE)
endif()
# Version of the project
# Generic version of not only the library. Major version is reserved for really big changes of the project,
# minor version changes with added functionality (new tool, functionality of the tool or library, ...) and
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(LIBNETCONF2_MAJOR_VERSION 2)
set(LIBNETCONF2_MINOR_VERSION 0)
set(LIBNETCONF2_MICRO_VERSION 24)
set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION})
# Version of the library
# Major version is changed with every backward non-compatible API/ABI change in libyang, minor version changes
# with backward compatible change and micro version is connected with any internal change of the library.
set(LIBNETCONF2_MAJOR_SOVERSION 2)
set(LIBNETCONF2_MINOR_SOVERSION 1)
set(LIBNETCONF2_MICRO_SOVERSION 20)
set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION})
set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION})
# libyang SO version required
set(LIBYANG_DEP_SOVERSION_MAJOR 2)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fvisibility=hidden -std=gnu99")
#
# options
#
if(("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") OR ("${BUILD_TYPE_UPPER}" STREQUAL "RELWITHDEBINFO"))
option(ENABLE_TESTS "Build tests" ON)
option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" ON)
else()
option(ENABLE_TESTS "Build tests" OFF)
option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF)
endif()
option(ENABLE_COVERAGE "Build code coverage report from tests" OFF)
option(ENABLE_SSH "Enable NETCONF over SSH support (via libssh)" ON)
option(ENABLE_TLS "Enable NETCONF over TLS support (via OpenSSL)" ON)
option(ENABLE_DNSSEC "Enable support for SSHFP retrieval using DNSSEC for SSH (requires OpenSSL and libval)" OFF)
set(READ_INACTIVE_TIMEOUT 20 CACHE STRING "Maximum number of seconds waiting for new data once some data have arrived")
set(READ_ACTIVE_TIMEOUT 300 CACHE STRING "Maximum number of seconds for receiving a full message")
set(MAX_PSPOLL_THREAD_COUNT 6 CACHE STRING "Maximum number of threads that could simultaneously access a ps_poll structure")
set(TIMEOUT_STEP 100 CACHE STRING "Number of microseconds tasks are repeated until timeout elapses")
set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/share/yang/modules" CACHE STRING "Directory with common YANG modules")
#
# sources
#
set(libsrc
src/io.c
src/log.c
src/messages_client.c
src/messages_server.c
src/session.c
src/session_client.c
src/session_server.c)
if(ENABLE_SSH)
list(APPEND libsrc
src/session_client_ssh.c
src/session_server_ssh.c)
set(SSH_MACRO "#ifndef NC_ENABLED_SSH\n#define NC_ENABLED_SSH\n#endif")
endif()
if(ENABLE_TLS)
list(APPEND libsrc
src/session_client_tls.c
src/session_server_tls.c)
set(TLS_MACRO "#ifndef NC_ENABLED_TLS\n#define NC_ENABLED_TLS\n#endif")
endif()
set(headers
src/log.h
src/netconf.h
src/session.h
src/messages_client.h
src/messages_server.h
src/session_client.h
src/session_client_ch.h
src/session_server.h
src/session_server_ch.h)
# files to generate doxygen from
set(doxy_files
src/libnetconf.h
src/log.h
src/netconf.h
src/session.h
src/messages_client.h
src/messages_server.h
src/session_client.h
src/session_client_ch.h
src/session_server.h
src/session_server_ch.h)
# source files to be covered by the 'format' target
set(format_sources
compat/*.c
compat/*.h*
src/*.c
src/*.h
tests/*.c
tests/client/*.c)
#
# checks
#
if(ENABLE_DNSSEC AND NOT ENABLE_SSH)
message(WARNING "DNSSEC SSHFP retrieval cannot be used without SSH support.")
set(ENABLE_DNSSEC OFF)
endif()
if(ENABLE_VALGRIND_TESTS)
find_program(VALGRIND_FOUND valgrind)
if(NOT VALGRIND_FOUND)
message(WARNING "valgrind executable not found! Disabling memory leaks tests.")
set(ENABLE_VALGRIND_TESTS OFF)
else()
set(ENABLE_TESTS ON)
endif()
endif()
if(ENABLE_TESTS)
find_package(CMocka 1.0.1)
if(NOT CMOCKA_FOUND)
message(STATUS "Disabling tests because of missing CMocka")
set(ENABLE_TESTS OFF)
endif()
endif()
if(ENABLE_COVERAGE)
gen_coverage_enable(${ENABLE_TESTS})
endif()
if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG")
source_format_enable()
endif()
if("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY")
gen_doc("${doxy_files}" ${LIBNETCONF2_VERSION} ${LIBNETCONF2_DESCRIPTION} "")
return()
endif()
#
# targets
#
# use compat
use_compat()
# netconf2 target
add_library(netconf2 SHARED ${libsrc} ${compatsrc})
set_target_properties(netconf2 PROPERTIES VERSION ${LIBNETCONF2_SOVERSION_FULL} SOVERSION ${LIBNETCONF2_SOVERSION})
# include repository files with highest priority
include_directories(${PROJECT_BINARY_DIR}/src)
# dependencies - pthread
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
target_link_libraries(netconf2 ${CMAKE_THREAD_LIBS_INIT})
# check availability for some pthread functions
set(CMAKE_REQUIRED_LIBRARIES pthread)
check_function_exists(pthread_rwlockattr_setkind_np HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
# dependencies - openssl
if(ENABLE_TLS OR ENABLE_DNSSEC OR ENABLE_SSH)
find_package(OpenSSL REQUIRED)
if(ENABLE_TLS)
message(STATUS "OpenSSL found, required for TLS")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_TLS")
endif()
if(OPENSSL_VERSION VERSION_LESS 1.1.1)
message(WARNING "OpenSSL version ${OPENSSL_VERSION} is no longer maintained, consider an update.")
endif()
target_link_libraries(netconf2 ${OPENSSL_LIBRARIES})
include_directories(${OPENSSL_INCLUDE_DIR})
endif()
# dependencies - libssh
if(ENABLE_SSH)
find_package(LibSSH 0.7.1 REQUIRED)
if(LIBSSH_VERSION VERSION_EQUAL 0.9.3 OR LIBSSH_VERSION VERSION_EQUAL 0.9.4)
message(FATAL_ERROR "LibSSH ${LIBSSH_VERSION} includes regression bugs and libnetconf2 will NOT work properly, try to use another version")
endif()
target_link_libraries(netconf2 ${LIBSSH_LIBRARIES})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSSH_LIBRARIES})
include_directories(${LIBSSH_INCLUDE_DIRS})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_SSH")
# crypt
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "QNX")
target_link_libraries(netconf2 -lcrypt)
list(APPEND CMAKE_REQUIRED_LIBRARIES crypt)
else()
target_link_libraries(netconf2 -llogin)
list(APPEND CMAKE_REQUIRED_LIBRARIES login)
endif()
endif()
# dependencies - libval
if(ENABLE_DNSSEC)
find_package(LibVAL REQUIRED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_DNSSEC")
target_link_libraries(netconf2 ${LIBVAL_LIBRARIES})
include_directories(${LIBVAL_INCLUDE_DIRS})
endif()
# dependencies - libyang
find_package(LibYANG ${LIBYANG_DEP_SOVERSION_MAJOR} REQUIRED)
target_link_libraries(netconf2 ${LIBYANG_LIBRARIES})
include_directories(${LIBYANG_INCLUDE_DIRS})
# header file compatibility - shadow.h and crypt.h
check_include_file("shadow.h" HAVE_SHADOW)
check_include_file("crypt.h" HAVE_CRYPT)
# function compatibility - getpeereid on QNX
if(${CMAKE_SYSTEM_NAME} MATCHES "QNX")
target_link_libraries(netconf2 -lsocket)
list(APPEND CMAKE_REQUIRED_LIBRARIES socket)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES pthread)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_QNX_SOURCE)
check_symbol_exists(getpeereid "sys/types.h;unistd.h" HAVE_GETPEEREID)
list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_QNX_SOURCE)
endif()
# generate files
configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/src/config.h" ESCAPE_QUOTES @ONLY)
configure_file(nc_client.h.in nc_client.h)
configure_file(nc_server.h.in nc_server.h)
# install library
install(TARGETS netconf2 DESTINATION ${CMAKE_INSTALL_LIBDIR})
# install headers
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nc_client.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nc_server.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${headers} ${PROJECT_BINARY_DIR}/src/config.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libnetconf2)
# install pkg-config file
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
configure_file("libnetconf2.pc.in" "libnetconf2.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnetconf2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
# check that pkg-config includes the used path
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable pc_path pkg-config RESULT_VARIABLE RETURN OUTPUT_VARIABLE PC_PATH ERROR_QUIET)
if(RETURN EQUAL 0)
string(REGEX MATCH "${CMAKE_INSTALL_LIBDIR}/pkgconfig" SUBSTR "${PC_PATH}")
string(LENGTH "${SUBSTR}" SUBSTR_LEN)
if(SUBSTR_LEN EQUAL 0)
message(WARNING "pkg-config will not detect the new package after installation, adjust PKG_CONFIG_PATH using \"export PKG_CONFIG_PATH=\${PKG_CONFIG_PATH}:${CMAKE_INSTALL_LIBDIR}/pkgconfig\".")
endif()
endif()
endif()
# tests
if(ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# create coverage target for generating coverage reports
gen_coverage("test_.*" "test_.*_valgrind")
# generate doxygen documentation for libnetconf2 API
gen_doc("${doxy_files}" ${LIBNETCONF2_VERSION} ${LIBNETCONF2_DESCRIPTION} "")
# generate API/ABI report
if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
lib_abi_check(netconf2 "${headers}" ${LIBNETCONF2_SOVERSION_FULL} e7402149e5b36de7acab2e38970a3a9d6a8165d5)
endif()
# source files to be covered by the 'format' target and a test with 'format-check' target
source_format(${format_sources})
# clean cmake cache
add_custom_target(cleancache
COMMAND make clean
COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -exec rm -rf {} +
COMMAND rm -rf Makefile Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# uninstall
add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake")

View file

@ -0,0 +1,66 @@
# generate API/ABI report
macro(LIB_ABI_CHECK LIB_TARGET LIB_HEADERS LIB_SOVERSION_FULL ABI_BASE_HASH)
# get short hash
string(SUBSTRING "${ABI_BASE_HASH}" 0 8 ABI_BASE_HASH_SHORT)
# find abi-dumper
find_program(ABI_DUMPER abi-dumper)
find_package_handle_standard_args(abi-dumper DEFAULT_MSG ABI_DUMPER)
if(NOT ABI_DUMPER)
message(FATAL_ERROR "Program abi-dumper not found!")
endif()
# find abi-checker
find_program(ABI_CHECKER abi-compliance-checker)
find_package_handle_standard_args(abi-compliance-checker DEFAULT_MSG ABI_CHECKER)
if(NOT ABI_CHECKER)
message(FATAL_ERROR "Program abi-compliance-checker not found!")
endif()
# abi-dump target - generating an ABI dump
set(PUBLIC_HEADERS ${LIB_HEADERS})
string(PREPEND PUBLIC_HEADERS "${CMAKE_SOURCE_DIR}/")
string(REPLACE ";" "\n${CMAKE_SOURCE_DIR}/" PUBLIC_HEADERS "${PUBLIC_HEADERS}")
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/public_headers CONTENT "${PUBLIC_HEADERS}")
add_custom_target(abi-dump
COMMAND ${ABI_DUMPER} ./lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}
-o lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump
-lver ${LIB_SOVERSION_FULL} -public-headers ${CMAKE_BINARY_DIR}/public_headers
DEPENDS ${LIB_TARGET}
BYPRODUCTS ${CMAKE_BINARY_DIR}/lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Dumping ABI information of version ${LIB_SOVERSION_FULL} for abi-check")
# get URL for fetching origin
execute_process(COMMAND git remote get-url origin OUTPUT_VARIABLE ORIGIN_URL OUTPUT_STRIP_TRAILING_WHITESPACE)
# generate script for generating the base ABI dump
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/abibase.sh CONTENT "#!/bin/sh
if [ ! -d abibase ]; then mkdir abibase; fi
cd abibase
if [ ! -f build/lib${LIB_TARGET}.*.dump ]; then
if [ -d .git ] && [ \"${ABI_BASE_HASH}\" != \"`git log --pretty=oneline | cut -d' ' -f1`\" ]; then rm -rf .* 2> /dev/null; fi
if [ ! -d .git ]; then
git init --initial-branch=master
git remote add origin ${ORIGIN_URL}
git fetch origin --depth 1 ${ABI_BASE_HASH}
git reset --hard FETCH_HEAD
fi
if [ ! -d build ]; then mkdir build; fi
cd build
cmake -DCMAKE_BUILD_TYPE=ABICheck ..
make abi-dump
fi
")
# abi-check target - check ABI compatibility of current version and the base hash version
add_custom_target(abi-check
COMMAND bash ./abibase.sh
COMMAND ${ABI_CHECKER} -l lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}
-old abibase/build/lib${LIB_TARGET}.*.dump
-new ./lib${LIB_TARGET}.${LIB_SOVERSION_FULL}.dump -s
DEPENDS ${LIB_TARGET} abi-dump
BYPRODUCTS ${CMAKE_BINARY_DIR}/compat_reports/lib${LIB_TARGET}${CMAKE_SHARED_LIBRARY_SUFFIX}/*_to_${LIB_SOVERSION_FULL}/compat_report.html
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Checking ABI compatibility of version ${LIB_SOVERSION_FULL} and revision ${ABI_BASE_HASH_SHORT}")
endmacro()

View file

@ -0,0 +1,49 @@
# - Try to find CMocka
# Once done this will define
#
# CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka
#
# Read-Only variables:
# CMOCKA_FOUND - system has CMocka
# CMOCKA_INCLUDE_DIR - the CMocka include directory
# CMOCKA_LIBRARIES - Link these to use CMocka
# CMOCKA_DEFINITIONS - Compiler switches required for using CMocka
#
#=============================================================================
# Copyright (c) 2011-2012 Andreas Schneider <asn@cryptomilk.org>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
#
find_path(CMOCKA_INCLUDE_DIR
NAMES
cmocka.h
PATHS
${CMOCKA_ROOT_DIR}/include
)
find_library(CMOCKA_LIBRARY
NAMES
cmocka
PATHS
${CMOCKA_ROOT_DIR}/include
)
if(CMOCKA_LIBRARY)
set(CMOCKA_LIBRARIES
${CMOCKA_LIBRARIES}
${CMOCKA_LIBRARY}
)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR)
# show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view
mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES)

View file

@ -0,0 +1,117 @@
# - Try to find LibSSH
# Once done this will define
#
# LIBSSH_FOUND - system has LibSSH
# LIBSSH_INCLUDE_DIRS - the LibSSH include directory
# LIBSSH_LIBRARIES - link these to use LibSSH
# LIBSSH_VERSION -
#
# Author Michal Vasko <mvasko@cesnet.cz>
# Copyright (c) 2020 CESNET, z.s.p.o.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
include(FindPackageHandleStandardArgs)
if(LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
# in cache already
set(LIBSSH_FOUND TRUE)
else()
find_path(LIBSSH_INCLUDE_DIR
NAMES
libssh/libssh.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INCLUDE_PATH}
${CMAKE_INSTALL_PREFIX}/include
)
find_library(LIBSSH_LIBRARY
NAMES
ssh.so
libssh.so
libssh.dylib
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
${CMAKE_LIBRARY_PATH}
${CMAKE_INSTALL_PREFIX}/lib
)
if(LIBSSH_INCLUDE_DIR AND LIBSSH_LIBRARY)
# learn libssh version
if(EXISTS ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h)
set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h)
else()
set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h)
endif()
file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MAJOR
REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+")
if(NOT LIBSSH_VERSION_MAJOR)
message(STATUS "LIBSSH_VERSION_MAJOR not found, assuming libssh is too old and cannot be used!")
set(LIBSSH_INCLUDE_DIR "LIBSSH_INCLUDE_DIR-NOTFOUND")
set(LIBSSH_LIBRARY "LIBSSH_LIBRARY-NOTFOUND")
else()
string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR})
file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MINOR
REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+")
string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR})
file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_PATCH
REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+")
string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH})
set(LIBSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH})
if(LIBSSH_VERSION VERSION_LESS 0.8.0)
# libssh_threads also needs to be linked for these versions
string(REPLACE "libssh.so" "libssh_threads.so"
LIBSSH_THREADS_LIBRARY
${LIBSSH_LIBRARY}
)
string(REPLACE "libssh.dylib" "libssh_threads.dylib"
LIBSSH_THREADS_LIBRARY
${LIBSSH_THREADS_LIBRARY}
)
string(REPLACE "ssh.so" "ssh_threads.so"
LIBSSH_THREADS_LIBRARY
${LIBSSH_THREADS_LIBRARY}
)
endif()
endif()
endif()
set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR})
set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY} ${LIBSSH_THREADS_LIBRARY})
mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES)
find_package_handle_standard_args(LibSSH FOUND_VAR LIBSSH_FOUND
REQUIRED_VARS LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES
VERSION_VAR LIBSSH_VERSION)
endif()

View file

@ -0,0 +1,102 @@
# - Try to find LibVAL
# Once done this will define
#
# LIBVAL_FOUND - system has LibVAL
# LIBVAL_INCLUDE_DIRS - the LibVAL include directory
# LIBVAL_LIBRARIES - link these to use LibVAL
#
# Author Michal Vasko <mvasko@cesnet.cz>
# Copyright (c) 2015 CESNET, z.s.p.o.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
if(LIBVAL_LIBRARIES AND LIBVAL_INCLUDE_DIRS)
# in cache already
set(LIBVAL_FOUND TRUE)
else()
find_path(LIBVAL_INCLUDE_DIR
NAMES
validator/validator.h
validator/resolver.h
validator/validator-compat.h
validator/val_dane.h
validator/val_errors.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INCLUDE_PATH}
${CMAKE_INSTALL_PREFIX}/include
)
find_library(LIBVAL_LIBRARY
NAMES
libval-threads
val-threads
PATHS
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
/opt/local/lib
/sw/lib
${CMAKE_LIBRARY_PATH}
${CMAKE_INSTALL_PREFIX}/lib
)
find_library(LIBSRES_LIBRARY
NAMES
libsres
sres
PATHS
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
/opt/local/lib
/sw/lib
${CMAKE_LIBRARY_PATH}
${CMAKE_INSTALL_PREFIX}/lib
)
if(LIBVAL_INCLUDE_DIR AND LIBVAL_LIBRARY AND LIBSRES_LIBRARY)
set(LIBVAL_FOUND TRUE)
else()
set(LIBVAL_FOUND FALSE)
endif()
set(LIBVAL_INCLUDE_DIRS ${LIBVAL_INCLUDE_DIR})
set(LIBVAL_LIBRARIES ${LIBSRES_LIBRARY} ${LIBVAL_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibVAL DEFAULT_MSG LIBVAL_LIBRARIES LIBVAL_INCLUDE_DIRS)
# show the LIBVAL_INCLUDE_DIRS and LIBVAL_LIBRARIES variables only in the advanced view
mark_as_advanced(LIBVAL_INCLUDE_DIRS LIBVAL_LIBRARIES)
endif()

View file

@ -0,0 +1,89 @@
# - Try to find LibYANG
# Once done this will define
#
# LIBYANG_FOUND - system has LibYANG
# LIBYANG_INCLUDE_DIRS - the LibYANG include directory
# LIBYANG_LIBRARIES - Link these to use LibYANG
# LIBYANG_VERSION - SO version of the found libyang library
#
# Author Michal Vasko <mvasko@cesnet.cz>
# Copyright (c) 2021 CESNET, z.s.p.o.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
include(FindPackageHandleStandardArgs)
if(LIBYANG_LIBRARIES AND LIBYANG_INCLUDE_DIRS)
# in cache already
set(LIBYANG_FOUND TRUE)
else()
find_path(LIBYANG_INCLUDE_DIR
NAMES
libyang/libyang.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INCLUDE_PATH}
${CMAKE_INSTALL_PREFIX}/include
)
find_library(LIBYANG_LIBRARY
NAMES
yang
libyang
PATHS
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
/opt/local/lib
/sw/lib
${CMAKE_LIBRARY_PATH}
${CMAKE_INSTALL_PREFIX}/lib
)
if(LIBYANG_INCLUDE_DIR)
find_path(LY_VERSION_PATH "libyang/version.h" HINTS ${LIBYANG_INCLUDE_DIR})
if(LY_VERSION_PATH)
file(READ "${LY_VERSION_PATH}/libyang/version.h" LY_VERSION_FILE)
else()
find_path(LY_HEADER_PATH "libyang/libyang.h" HINTS ${LIBYANG_INCLUDE_DIR})
file(READ "${LY_HEADER_PATH}/libyang/libyang.h" LY_VERSION_FILE)
endif()
string(REGEX MATCH "#define LY_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" LY_VERSION_MACRO "${LY_VERSION_FILE}")
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBYANG_VERSION "${LY_VERSION_MACRO}")
endif()
set(LIBYANG_INCLUDE_DIRS ${LIBYANG_INCLUDE_DIR})
set(LIBYANG_LIBRARIES ${LIBYANG_LIBRARY})
mark_as_advanced(LIBYANG_INCLUDE_DIRS LIBYANG_LIBRARIES)
# handle the QUIETLY and REQUIRED arguments and set LIBYANG_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(LibYANG FOUND_VAR LIBYANG_FOUND
REQUIRED_VARS LIBYANG_LIBRARY LIBYANG_INCLUDE_DIR
VERSION_VAR LIBYANG_VERSION)
endif()

View file

@ -0,0 +1,21 @@
# - Find uncrustify
# Find the uncrustify binary.
#
# UNCRUSTIFY - path ot the binary
# UNCRUSTIFY_VERSION - found version
# UNCRUSTIFY_FOUND - True if uncrustify found.
include(FindPackageHandleStandardArgs)
find_program(UNCRUSTIFY uncrustify)
if(UNCRUSTIFY)
execute_process(COMMAND ${UNCRUSTIFY} --version OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE VERSION)
string(FIND ${VERSION} "-" START_IDX)
math(EXPR START_IDX "${START_IDX} + 1")
string(SUBSTRING "${VERSION}" ${START_IDX} -1 VERSION)
string(FIND ${VERSION} "-" LEN)
string(SUBSTRING "${VERSION}" 0 ${LEN} UNCRUSTIFY_VERSION)
endif()
# Handle the QUIETLY and REQUIRED arguments and set UNCRUSTIFY_FOUND to TRUE if all listed variables are TRUE.
find_package_handle_standard_args(Uncrustify REQUIRED_VARS UNCRUSTIFY VERSION_VAR UNCRUSTIFY_VERSION)

View file

@ -0,0 +1,118 @@
# generate test code coverage report
# check that coverage tools are available - always use before GEN_COVERAGE
macro(GEN_COVERAGE_ENABLE ENABLE_TESTS)
# make into normal variable
set(TESTS_ENABLED ${ENABLE_TESTS})
set(GEN_COVERAGE_ENABLED ON)
if(NOT TESTS_ENABLED)
message(WARNING "You cannot generate coverage when tests are disabled. Enable test by additing parameter -DENABLE_BUILD_TESTS=ON or run cmake with Debug build target.")
set(GEN_COVERAGE_ENABLED OFF)
endif()
if(GEN_COVERAGE_ENABLED)
find_program(PATH_GCOV NAMES gcov)
if(NOT PATH_GCOV)
message(WARNING "gcov executable not found! Disabling building code coverage report.")
set(GEN_COVERAGE_ENABLED OFF)
endif()
endif()
if(GEN_COVERAGE_ENABLED)
find_program(PATH_LCOV NAMES lcov)
if(NOT PATH_LCOV)
message(WARNING "lcov executable not found! Disabling building code coverage report.")
set(GEN_COVERAGE_ENABLED OFF)
endif()
endif()
if(GEN_COVERAGE_ENABLED)
find_program(PATH_GENHTML NAMES genhtml)
if(NOT PATH_GENHTML)
message(WARNING "genhtml executable not found! Disabling building code coverage report.")
set(GEN_COVERAGE_ENABLED OFF)
endif()
endif()
if(GEN_COVERAGE_ENABLED)
if(NOT CMAKE_COMPILER_IS_GNUCC)
message(WARNING "Compiler is not gcc! Coverage may break the tests!")
endif()
execute_process(
COMMAND bash "-c" "${CMAKE_C_COMPILER} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\""
OUTPUT_VARIABLE GCC_VERSION_FULL
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND bash "-c" "${PATH_GCOV} --version | head -n1 | sed \"s/.* (.*) \\([0-9]\\+.[0-9]\\+.[0-9]\\+ .*\\)/\\1/\""
OUTPUT_VARIABLE GCOV_VERSION_FULL
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT GCC_VERSION_FULL STREQUAL GCOV_VERSION_FULL)
message(WARNING "gcc and gcov versions do not match! Generating coverage may fail with errors.")
endif()
# add specific required compile flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -fprofile-arcs -ftest-coverage")
endif()
endmacro()
# tests are always expected to be in ${CMAKE_SOURCE_DIR}/tests
function(GEN_COVERAGE MATCH_TEST_REGEX EXCLUDE_TEST_REGEX)
if(NOT GEN_COVERAGE_ENABLED)
return()
endif()
# destination
set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/code_coverage/")
set(COVERAGE_FILE_RAW "${CMAKE_BINARY_DIR}/coverage_raw.info")
set(COVERAGE_FILE_CLEAN "${CMAKE_BINARY_DIR}/coverage_clean.info")
# test match/exclude
if(MATCH_TEST_REGEX)
set(MATCH_TEST_ARGS -R \"${MATCH_TEST_REGEX}\")
endif()
if(EXCLUDE_TEST_REGEX)
set(EXCLUDE_TEST_ARGS -E \"${EXCLUDE_TEST_REGEX}\")
endif()
# coverage target
add_custom_target(coverage
COMMENT "Generating code coverage..."
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
# Cleanup code counters
COMMAND "${PATH_LCOV}" --directory . --zerocounters --quiet
# Run tests
COMMAND "${CMAKE_CTEST_COMMAND}" --quiet ${MATCH_TEST_ARGS} ${EXCLUDE_TEST_ARGS}
# Capture the counters
COMMAND "${PATH_LCOV}"
--directory .
--rc lcov_branch_coverage=1
--rc 'lcov_excl_line=assert'
--capture --quiet
--output-file "${COVERAGE_FILE_RAW}"
# Remove coverage of tests, system headers, etc.
COMMAND "${PATH_LCOV}"
--remove "${COVERAGE_FILE_RAW}" '${CMAKE_SOURCE_DIR}/tests/*'
--rc lcov_branch_coverage=1
--quiet --output-file "${COVERAGE_FILE_CLEAN}"
# Generate HTML report
COMMAND "${PATH_GENHTML}"
--branch-coverage --function-coverage --quiet --title "${PROJECT_NAME}"
--legend --show-details --output-directory "${COVERAGE_DIR}"
"${COVERAGE_FILE_CLEAN}"
# Delete the counters
COMMAND "${CMAKE_COMMAND}" -E remove
${COVERAGE_FILE_RAW} ${COVERAGE_FILE_CLEAN}
)
add_custom_command(TARGET coverage POST_BUILD
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
COMMENT "To see the code coverage report, open ${COVERAGE_DIR}index.html"
COMMAND ;
)
endfunction()

28
CMakeModules/GenDoc.cmake Normal file
View file

@ -0,0 +1,28 @@
# Prepare building doxygen documentation
macro(GEN_DOC INPUT_FILES PROJECT_VERSION PROJECT_DESCRIPTION DOC_LOGO)
find_package(Doxygen)
if(DOXYGEN_FOUND)
find_program(DOT_PATH dot PATH_SUFFIXES graphviz2.38/bin graphviz/bin)
if(DOT_PATH)
set(HAVE_DOT "YES")
else()
set(HAVE_DOT "NO")
message(AUTHOR_WARNING "Doxygen: to generate UML diagrams please install graphviz")
endif()
# target doc
add_custom_target(doc
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# generate list with spaces as separators
string(REPLACE ";" " " DOXY_INPUT "${INPUT_FILES}")
# make other arguments into variables
set(PROJECT_VERSION ${PROJECT_VERSION})
set(PROJECT_DESCRIPTION ${PROJECT_DESCRIPTION})
set(DOC_LOGO ${DOC_LOGO})
configure_file(Doxyfile.in Doxyfile)
endif()
endmacro()

View file

@ -0,0 +1,32 @@
# format source files with uncrustify
# check that format checking is available - always use before SOURCE_FORMAT
macro(SOURCE_FORMAT_ENABLE)
find_package(Uncrustify 0.71)
if(UNCRUSTIFY_FOUND)
set(SOURCE_FORMAT_ENABLED TRUE)
else()
set(SOURCE_FORMAT_ENABLED FALSE)
endif()
endmacro()
# files are expected to be a list and relative paths are resolved wtih respect to CMAKE_SOURCE DIR
macro(SOURCE_FORMAT)
if(NOT ${ARGC})
message(FATAL_ERROR "source_format() needs a list of files to format!")
endif()
if(SOURCE_FORMAT_ENABLED)
add_custom_target(format
COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --no-backup --replace ${ARGN}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Formating sources with ${UNCRUSTIFY} ...")
add_custom_target(format-check
COMMAND ${UNCRUSTIFY} -c ${CMAKE_SOURCE_DIR}/uncrustify.cfg --check ${ARGN}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Checking format of the sources with ${UNCRUSTIFY} ...")
set(SOURCE_FORMAT_ENABLED TRUE)
endif()
endmacro()

View file

@ -0,0 +1,57 @@
# - Use compat library providing various functions and macros that may be missing on some systems
# Once done this will define
#
# compatsrc - sources to add to compilation
#
# Additionally, "compat.h" include directory is added and can be included.
#
# Author Michal Vasko <mvasko@cesnet.cz>
# Copyright (c) 2021 CESNET, z.s.p.o.
#
# This source code is licensed under BSD 3-Clause License (the "License").
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://opensource.org/licenses/BSD-3-Clause
#
include(CheckSymbolExists)
include(CheckFunctionExists)
include(CheckIncludeFile)
include(TestBigEndian)
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
macro(USE_COMPAT)
# compatibility checks
set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1)
set(CMAKE_REQUIRED_LIBRARIES pthread)
check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF)
check_symbol_exists(asprintf "stdio.h" HAVE_ASPRINTF)
check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF)
check_symbol_exists(getline "stdio.h" HAVE_GETLINE)
check_symbol_exists(strndup "string.h" HAVE_STRNDUP)
check_symbol_exists(strnstr "string.h" HAVE_STRNSTR)
check_symbol_exists(strdupa "string.h" HAVE_STRDUPA)
check_symbol_exists(strchrnul "string.h" HAVE_STRCHRNUL)
check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME)
check_function_exists(pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
check_include_file("stdatomic.h" HAVE_STDATOMIC)
unset(CMAKE_REQUIRED_DEFINITIONS)
unset(CMAKE_REQUIRED_LIBRARIES)
# header and source file (adding the source directly allows for hiding its symbols)
configure_file(${PROJECT_SOURCE_DIR}/compat/compat.h.in ${PROJECT_BINARY_DIR}/compat/compat.h @ONLY)
include_directories(${PROJECT_BINARY_DIR}/compat)
set(compatsrc ${PROJECT_SOURCE_DIR}/compat/compat.c)
endmacro()

View file

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.0.2)
set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
if(NOT EXISTS ${MANIFEST})
message(FATAL_ERROR "Cannot find install manifest: ${MANIFEST}")
endif()
file(STRINGS ${MANIFEST} files)
foreach(file ${files})
if(EXISTS ${file} OR IS_SYMLINK ${file})
message(STATUS "Removing: ${file}")
execute_process(COMMAND rm -f ${file}
RESULT_VARIABLE result
OUTPUT_QUIET
ERROR_VARIABLE stderr
ERROR_STRIP_TRAILING_WHITESPACE
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR "${stderr}")
endif()
else()
message(STATUS "Does-not-exist: ${file}")
endif()
endforeach(file)

184
CODINGSTYLE.md Normal file
View file

@ -0,0 +1,184 @@
# libnetconf2 Coding Style
This file describes the coding style used in most C files in the libnetconf2
library.
## Basics
- Use space instead of tabs for indentations.
- There is no strict limit for the line length, However, try to keep lines in a
reasonable length (120 characters).
- Avoid trailing spaces on lines.
- Put one blank line between function definitions.
- Don't mix declarations and code within a block. Similarly, don't use
declarations in iteration statements.
## Naming
Use underscores to separate words in an identifier: `multi_word_name`.
Use lowercase for most names. Use uppercase for macros, macro parameters and
members of enumerations.
Do not use names that begin with `_`. If you need a name for "internal use
only", use `__` as a suffix instead of a prefix.
## Comments
Avoid `//` comments. Use `/* ... */` comments, write block comments with the
leading asterisk on each line. You may put the `/*` and `*/` on the same line as
comment text if you prefer.
```c
/*
* comment text
*/
```
## Functions
Put the return type, function name, and the braces that surround the function's
code on separate lines, all starting in column 0.
```c
static int
foo(int arg)
{
...
}
```
When you need to put the function parameters on multiple lines, start new line
at column after the opening parenthesis from the initial line.
```c
static int
my_function(struct my_struct *p1, struct another_struct *p2,
int size)
{
...
}
```
In the absence of good reasons for another order, the following parameter order
is preferred. One notable exception is that data parameters and their
corresponding size parameters should be paired.
1. The primary object being manipulated, if any (equivalent to the "this"
pointer in C++).
2. Input-only parameters.
3. Input/output parameters.
4. Output-only parameters.
5. Status parameter.
Functions that destroy an instance of a dynamically-allocated type should accept
and ignore a null pointer argument. Code that calls such a function (including
the C standard library function `free()`) should omit a null-pointer check. We
find that this usually makes code easier to read.
### Function Prototypes
Put the return type and function name on the same line in a function prototype:
```c
static const struct int foo(int arg);
```
## Statements
- Indent each level of code with 4 spaces.
- Put single space between `if`, `while`, `for`, etc. statements and the
expression that follow them. On the other hand, function calls has no space
between the function name and opening parenthesis.
- Opening code block brace is kept at the same line with the `if`, `while`,
`for` or `switch` statements.
```c
if (a) {
x = exp(a);
} else {
return 1;
}
```
- Start switch's cases at the same column as the switch.
```c
switch (conn->state) {
case 0:
return "data found";
case 1:
return "data not found";
default:
return "unknown error";
}
```
- Do not put gratuitous parentheses around the expression in a return statement,
that is, write `return 0;` and not `return(0);`
## Types
Use typedefs sparingly. Code is clearer if the actual type is visible at the
point of declaration. Do not, in general, declare a typedef for a struct, union,
or enum. Do not declare a typedef for a pointer type, because this can be very
confusing to the reader.
Use the `int<N>_t` and `uint<N>_t` types from `<stdint.h>` for exact-width
integer types. Use the `PRId<N>`, `PRIu<N>`, and `PRIx<N>` macros from
`<inttypes.h>` for formatting them with `printf()` and related functions.
Pointer declarators bind to the variable name, not the type name. Write
`int *x`, not `int* x` and definitely not `int * x`.
## Expresions
Put one space on each side of infix binary and ternary operators:
```c
* / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>=
```
Do not put any white space around postfix, prefix, or grouping operators with
one exception - `sizeof`, see the note below.
```c
() [] -> . ! ~ ++ -- + - * &
```
The "sizeof" operator is unique among C operators in that it accepts two very
different kinds of operands: an expression or a type. In general, prefer to
specify an expression
```c
int *x = calloc(1, sizeof *x);
```
When the operand of sizeof is an expression, there is no need to parenthesize
that operand, and please don't. There is an exception to this rule when you need
to work with partially compatible structures:
```c
struct a_s {
uint8_t type;
}
struct b_s {
uint8_t type;
char *str;
}
struct c_s {
uint8_t type;
uint8_t *u8;
}
...
struct a_s *a;
switch (type) {
case 1:
a = (struct a_s *)calloc(1, sizeof(struct b_s));
break;
case 2:
a = (struct a_s *)calloc(1, sizeof(struct c_s));
break;
...
```

2457
Doxyfile.in Normal file

File diff suppressed because it is too large Load diff

38
FAQ.md Normal file
View file

@ -0,0 +1,38 @@
# Frequently Asked Questions
__Q: Having a fresh installation of *netopeer2-server*, when I connect to it I see (or something similar):__
```
[ERR]: LN: Failed to set hostkey "genkey" (/tmp/dvcjwz).
```
__A:__ You are using *libssh* that was compiled with *gcrypt* library
as the crypto backend. It does not support default SSH keys generated
during *netopeer2-server* installation. To fix, disable suport for this
backend when compiling *libssh* so that some other one is used.
__Q: When a new NETCONF session is being created, I see the error:__
```
Starting the SSH session failed ()
```
__A:__ The most likely reason for this is that the SSH key that is used
for this session authentication uses an algorithm not supported by
your system. The supported algorithms are automatically loaded by *libssh*
from OpenSSH configuration files (more info in `ssh_config(5)` and `sshd_config(5)`).
__Q: When I try to connect to a server I immediately get a timeout after authenticating:__
__A:__ You are probably using *libssh* version 0.9.3 that includes this
[regression bug](https://bugs.libssh.org/T211). To solve it, you must use another version.
__Q: When I connect to a server, after around 10-20 seconds I get disconnected with an error:__
```
[ERR]: LN: Session 1: inactive read timeout elapsed.
```
__A:__ There are 2 most common reasons for this error. Either you are not using
a NETCONF client to connect (but `ssh(1)`, for example) and the messages received
by the server are not properly formatted (even an additional `\n` can cause this problem).
To fix, use a NETCONF client instead. Another reason may be that you are using *libssh*
version 0.9.4. It includes a [regression bug](https://gitlab.com/libssh/libssh-mirror/-/merge_requests/101)
that causes this problem and you must use another version to fix it.

85
FindLibNETCONF2.cmake Normal file
View file

@ -0,0 +1,85 @@
# - Try to find LibNETCONF2
# Once done this will define
#
# LIBNETCONF2_FOUND - system has LibNETCONF2
# LIBNETCONF2_INCLUDE_DIRS - the LibNETCONF2 include directory
# LIBNETCONF2_LIBRARIES - Link these to use LibNETCONF2
# LIBNETCONF2_ENABLED_SSH - LibNETCONF2 was compiled with SSH support
# LIBNETCONF2_ENABLED_TLS - LibNETCONF2 was compiled with TLS support
#
# Author Michal Vasko <mvasko@cesnet.cz>
# Copyright (c) 2020 CESNET, z.s.p.o.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
include(FindPackageHandleStandardArgs)
include(CheckSymbolExists)
if(LIBNETCONF2_LIBRARIES AND LIBNETCONF2_INCLUDE_DIRS)
# in cache already
set(LIBNETCONF2_FOUND TRUE)
else()
find_path(LIBNETCONF2_INCLUDE_DIR
NAMES
nc_client.h
nc_server.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
${CMAKE_INCLUDE_PATH}
${CMAKE_INSTALL_PREFIX}/include
)
find_library(LIBNETCONF2_LIBRARY
NAMES
netconf2
libnetconf2
PATHS
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
/opt/local/lib
/sw/lib
${CMAKE_LIBRARY_PATH}
${CMAKE_INSTALL_PREFIX}/lib
)
set(LIBNETCONF2_INCLUDE_DIRS ${LIBNETCONF2_INCLUDE_DIR})
set(LIBNETCONF2_LIBRARIES ${LIBNETCONF2_LIBRARY})
mark_as_advanced(LIBNETCONF2_INCLUDE_DIRS LIBNETCONF2_LIBRARIES)
# handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(LibNETCONF2 DEFAULT_MSG LIBNETCONF2_LIBRARY LIBNETCONF2_INCLUDE_DIR)
# check the configured options and make them available through cmake
list(INSERT CMAKE_REQUIRED_INCLUDES 0 "${LIBNETCONF2_INCLUDE_DIR}")
check_symbol_exists("NC_ENABLED_SSH" "nc_client.h" LIBNETCONF2_ENABLED_SSH)
check_symbol_exists("NC_ENABLED_TLS" "nc_client.h" LIBNETCONF2_ENABLED_TLS)
list(REMOVE_AT CMAKE_REQUIRED_INCLUDES 0)
endif()

27
LICENSE Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2015-2020, CESNET
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of libnetconf2 nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

285
README.md Normal file
View file

@ -0,0 +1,285 @@
# libnetconf2 The NETCONF protocol library
[![BSD license](https://img.shields.io/badge/License-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Build](https://github.com/CESNET/libnetconf2/workflows/libnetconf2%20CI/badge.svg)](https://github.com/CESNET/libnetconf2/actions?query=workflow%3A%22libnetconf2+CI%22)
[![Docs](https://img.shields.io/badge/docs-link-blue)](https://netopeer.liberouter.org/doc/libnetconf2/)
[![Coverity](https://scan.coverity.com/projects/7642/badge.svg)](https://scan.coverity.com/projects/7642)
[![Codecov](https://codecov.io/gh/CESNET/libnetconf2/branch/master/graph/badge.svg?token=HpKeW36N9D)](https://codecov.io/gh/CESNET/libnetconf2)
**libnetconf2** is a NETCONF library in C intended for building NETCONF clients
and servers. NETCONF is the [NETwork CONFiguration protocol](http://trac.tools.ietf.org/wg/netconf/trac/wiki)
introduced by IETF.
**libnetconf2** is a NETCONF library in C handling NETCONF authentication and all NETCONF RPC communication both server
and client-side. Note that NETCONF datastore implementation is not a part of this library. The library supports both
NETCONF 1.0 ([RFC 4741](https://tools.ietf.org/html/rfc4741)) as well as NETCONF 1.1
([RFC 6241](https://tools.ietf.org/html/rfc6241)). The main features include:
* NETCONF over SSH ([RFC 4742](https://tools.ietf.org/html/rfc4742), [RFC 6242](https://tools.ietf.org/html/rfc6242)),
using [libssh](https://www.libssh.org/).
* NETCONF over TLS ([RFC 7589](https://tools.ietf.org/html/rfc7589)), using [OpenSSL](https://www.openssl.org/).
* DNSSEC SSH Key Fingerprints ([RFC 4255](https://tools.ietf.org/html/rfc4255))
* NETCONF over pre-established transport sessions (using this mechanism the communication can be tunneled through
sshd(8), for instance).
* NETCONF Call Home ([RFC 8071](https://tools.ietf.org/html/rfc8071)).
* NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)),
**libnetconf2** is maintained and further developed by the [Tools for
Monitoring and Configuration](https://www.liberouter.org/) department of
[CESNET](http://www.ces.net/). Any testing or improving/fixing the library
is welcome. Please inform us about your experiences with using **libnetconf2**
via the [issue tracker](https://github.com/CESNET/libnetconf/issues).
Besides the [**libyang**](https://github.com/CESNET/libyang), **libnetconf2** is
another basic building block for the [**Netopeer2** toolset](https://github.com/CESNET/Netopeer2).
For a reference implementation of NETCONF client and server, check the
**Netopeer2** project.
## Branches
The project uses 2 main branches `master` and `devel`. Other branches should not be cloned. In `master` there are files of the
last official *release*. Any latest improvements and changes, which were tested at least briefly are found in `devel`. On every
new *release*, `devel` is merged into `master`.
This means that when only stable official releases are to be used, either `master` can be used or specific *releases* downloaded.
If all the latest bugfixes should be applied, `devel` branch is the one to be used. Note that whenever **a new issue is created**
and it occurs on the `master` branch, the **first response will likely be** to use `devel` before any further provided support.
## libnetconf vs libnetconf2
**libnetconf2** was developed with experiences gained from the development
of the [**libnetconf**](https://github.com/CESNET/libnetconf) library, which
is now obsolete and should not be used.
# Installation
## Required Dependencies
Install the following libraries and tools the libnetconf2 depends on.
### libyang
Follow the [libyang instructions](https://github.com/CESNET/libyang/blob/master/README.md),
in short:
```
$ git clone https://github.com/CESNET/libyang.git
$ cd libyang; mkdir build; cd build
$ cmake ..
$ make
# make install
```
### libssh
Required version is at least 0.7.1. This dependency can be removed by disabling
SSH support (see the [Build Options](#build-options) section). Below si the basic
sequence of commands for compiling and installing it from source. However, there
are packages for certain Linux distributions available [here](https://www.libssh.org/get-it/).
```
$ git clone http://git.libssh.org/projects/libssh.git
$ cd libssh; mkdir build; cd build
$ cmake ..
$ make
# make install
```
### OpenSSL
This dependency is required when the TLS support is enabled, which it is by
default but libssh requires it too. So, to remove this dependency, you need
to disable both SSH and TLS (see the [Build Options](#build-options) section).
OpenSSL is a standard part of the most distribution, so ask your package
manager for OpenSSL package including the necessary development files
(usually -dev or -devel package).
## Optional Dependencies
### libval (part of the DNSSEC-Tools suite)
It is required only if DNSSEC SSHFP retrieval is enabled (it is disabled by
default, see the [Build Options](#build-options) section).
The easier way of installing it is as the libval-dev package (or a part of
the dnssec-tools package), if you can find it for your distribution. Otherwise,
compile and install it from [source](https://github.com/DNSSEC-Tools/DNSSEC-Tools/).
Only the validator component (`DNSSEC-Tools/dnssec-tools/validator`) is needed.
### cmocka
For running the tests cmocka 1.0.1 is required (see the [Tests](#tests) section for more information).
```
$ sudo apt-get install libcmocka-dev
```
### Doxygen
For building the library documentation.
Doxygen is a standard part of the most distribution, so ask your package
manager for doxygen package.
### gcov
For code coverage, `gcov`, `lcov`, and `genhtml` are needed.
## Building libnetconf2
```
$ mkdir build; cd build
$ cmake ..
$ make
# install
```
The library documentation can be generated directly from the source codes using
Doxygen tool:
```
$ make doc
```
## Build Options
There are various options to change result of building.
### Changing Compiler
Set `CC` environment variable:
```
$ CC=/usr/bin/clang cmake ..
```
### Installation Prefix
By default, the library is installed with the `/usr/local` prefix, to change
it, use the following option:
```
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ..
```
### Transport Protocol Support
The NETCONF protocol specification allows to use the protocol on top of
several transport protocols. **libnetconf2** provides support for SSH and
TLS transport. By default, both SSH and TLS transport is enabled. Disabling
and enabling both the transport protocols can be made
in the same way. The following command has actually the same effect as
specifying no option since it specifies the default settings.
```
$ cmake -DENABLE_TLS=ON -DENABLE_SSH=ON ..
```
### DNSSEC SSHFP Retrieval
In SSH connections, if the remote NETCONF server supports it and it is
enabled, it is possible to safely retrieve server host key fingerprints
using DNSSEC and automatically consider them to be trusted without any
interaction. Enable it with the following command.
```
$ cmake -DENABLE_DNSSEC=ON ..
```
### Build Modes
There are two build modes:
* Release.
This generates library for the production use without any debug information.
* Debug.
This generates library with the debug information and disables optimization
of the code.
The `Debug` mode is currently used as the default one. to switch to the
`Release` mode, enter at the command line:
```
$ cmake -D CMAKE_BUILD_TYPE:String="Release" ..
```
### Inactive Read Timeout
It is possible to adjust inactive read timeout. It is used when a new message is
being read and no new data had arrived for this amount of seconds. 20 is the default value.
```
$ cmake -D READ_INACTIVE_TIMEOUT:String="20" ..
```
### Active Read Timeout
Active read timeout is used to limit the maximum number of seconds a message is given
to arrive in its entirety once a beginning is read. The default is 300 (5 minutes).
```
$ cmake -D READ_ACTIVE_TIMEOUT:String="300" ..
```
### PSPoll Thread Count
This value limits the maximum number of threads that can concurrently access
(wait for access) a single pspoll structure. To simplify, how many threads could
simultaneously call a function whose parameter is one and the same pspoll structure.
If using **netopeer2-server**, it will warn that this value needs to be adjusted if
too small.
```
$ cmake -D MAX_PSPOLL_THREAD_COUNT:String="6" ..
```
### Code Coverage
Based on the tests run, it is possible to generate code coverage report. But
it must be enabled and these commands are needed to generate the report:
```
$ cmake -DENABLE_COVERAGE=ON ..
$ make
$ make coverage
```
Note that `gcc` compiler is required for this option and additional tools are required:
* gcov
* lcov
* genhtml
### CMake Notes
Note that, with CMake, if you want to change the compiler or its options after
you already ran CMake, you need to clear its cache first - the most simple way
to do it is to remove all content from the 'build' directory.
## Usage
All public functions are available via 2 headers:
```
#include <nc_server.h>
#include <nc_client.h>
```
You need to include either one if imeplementing a NETCONF server or a NETCONF client,
respectively.
To compile your program with libnetconf2, it is necessary to link it with it using the
following linker parameters:
```
-lnetconf2
```
## Tests
The repository includes several tests built with [cmocka](https://cmocka.org/).
The tests can be found in `tests` subdirectory and they are designed for
checking library functionality after code changes.
The tests are by default built in the `Debug` build mode by running
```
$ make
```
In case of the `Release` mode, the tests are not built by default (it requires
additional dependency), but it can be enabled via cmake option:
```
$ cmake -DENABLE_TESTS=ON ..
```
Note that if the necessary [cmocka](https://cmocka.org/) headers are not present
in the system include paths, tests are not available despite the build mode or
cmake's options.
Tests can be run by the make's `test` target:
```
$ make test
```

32
codecov.yml Normal file
View file

@ -0,0 +1,32 @@
comment:
layout: header, changes, diff
coverage:
precision: 2
round: nearest
ignore:
- compat/.*
- tests/.*
- examples/.*
status:
project:
default:
target: auto
if_no_uploads: error
patch:
default:
if_no_uploads: error
changes: true
parsers:
gcov:
branch_detection:
macro: no
loop: no
conditional: no
method: no

44
compat/check_includes.sh Executable file
View file

@ -0,0 +1,44 @@
#!/bin/sh
RETVAL=0
# params - paths to the source files to search
SRC="$*"
# param FUNC - name of the function in compat to check
check_compat_func () {
FILES=`grep -rE "([^[:alnum:]]|^)$1\([^\)]+\)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
for f in $FILES; do
grep -q "#include \"compat.h\"" $f
if [ $? -ne 0 ]; then
echo "Missing #include \"compat.h\" in file $f for function $1()"
RETVAL=$((RETVAL+1))
fi
done
}
check_compat_macro () {
FILES=`grep -rE "([^[:alnum:]]|^)$1([^[:alnum:]]|$)" --include=\*.{c,h} $SRC | cut -d: -f1 | uniq`
for f in $FILES; do
grep -q "#include \"compat.h\"" $f
if [ $? -ne 0 ]; then
echo "Missing #include \"compat.h\" in file $f for macro $1"
RETVAL=$((RETVAL+1))
fi
done
}
check_compat_func vdprintf
check_compat_func asprintf
check_compat_func vasprintf
check_compat_func getline
check_compat_func strndup
check_compat_func strnstr
check_compat_func strdupa
check_compat_func strchrnul
check_compat_func get_current_dir_name
check_compat_func pthread_mutex_timedlock
check_compat_func UNUSED
check_compat_macro _PACKED
exit $RETVAL

250
compat/compat.c Normal file
View file

@ -0,0 +1,250 @@
/**
* @file compat.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief compatibility functions
*
* Copyright (c) 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _POSIX_C_SOURCE 200809L /* fdopen, _POSIX_PATH_MAX, strdup */
#define _ISOC99_SOURCE /* vsnprintf */
#include "compat.h"
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#ifndef HAVE_VDPRINTF
int
vdprintf(int fd, const char *format, va_list ap)
{
FILE *stream;
int count = 0;
stream = fdopen(dup(fd), "a+");
if (stream) {
count = vfprintf(stream, format, ap);
fclose(stream);
}
return count;
}
#endif
#ifndef HAVE_ASPRINTF
int
asprintf(char **strp, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = vasprintf(strp, fmt, ap);
va_end(ap);
return ret;
}
#endif
#ifndef HAVE_VASPRINTF
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap2;
va_copy(ap2, ap);
int l = vsnprintf(0, 0, fmt, ap2);
va_end(ap2);
if ((l < 0) || !(*strp = malloc(l + 1U))) {
return -1;
}
return vsnprintf(*strp, l + 1U, fmt, ap);
}
#endif
#ifndef HAVE_GETLINE
ssize_t
getline(char **lineptr, size_t *n, FILE *stream)
{
static char line[256];
char *ptr;
ssize_t len;
if (!lineptr || !n) {
errno = EINVAL;
return -1;
}
if (ferror(stream) || feof(stream)) {
return -1;
}
if (!fgets(line, 256, stream)) {
return -1;
}
ptr = strchr(line, '\n');
if (ptr) {
*ptr = '\0';
}
len = strlen(line);
if (len + 1 < 256) {
ptr = realloc(*lineptr, 256);
if (!ptr) {
return -1;
}
*lineptr = ptr;
*n = 256;
}
strcpy(*lineptr, line);
return len;
}
#endif
#ifndef HAVE_STRNDUP
char *
strndup(const char *s, size_t n)
{
char *buf;
size_t len = 0;
/* strnlen */
for ( ; (len < n) && (s[len] != '\0'); ++len) {}
if (!(buf = malloc(len + 1U))) {
return NULL;
}
memcpy(buf, s, len);
buf[len] = '\0';
return buf;
}
#endif
#ifndef HAVE_STRNSTR
char *
strnstr(const char *s, const char *find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if ((slen-- < 1) || ((sc = *s++) == '\0')) {
return NULL;
}
} while (sc != c);
if (len > slen) {
return NULL;
}
} while (strncmp(s, find, len));
s--;
}
return (char *)s;
}
#endif
#ifndef HAVE_STRCHRNUL
char *
strchrnul(const char *s, int c)
{
char *p = strchr(s, c);
return p ? p : (char *)s + strlen(s);
}
#endif
#ifndef HAVE_GET_CURRENT_DIR_NAME
char *
get_current_dir_name(void)
{
char tmp[_POSIX_PATH_MAX];
char *retval = NULL;
if (getcwd(tmp, sizeof(tmp))) {
retval = strdup(tmp);
if (!retval) {
errno = ENOMEM;
}
}
return retval;
}
#endif
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
int
pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
{
int64_t nsec_diff;
int32_t diff;
struct timespec cur, dur;
int rc;
/* try to acquire the lock and, if we fail, sleep for 5ms. */
while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
/* get real time */
#ifdef CLOCK_REALTIME
clock_gettime(CLOCK_REALTIME, &cur);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
cur.tv_sec = (time_t)tv.tv_sec;
cur.tv_nsec = 1000L * (long)tv.tv_usec;
#endif
/* get time diff */
nsec_diff = 0;
nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L;
nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec);
diff = (nsec_diff ? nsec_diff / 1000000L : 0);
if (diff < 1) {
/* timeout */
break;
} else if (diff < 5) {
/* sleep until timeout */
dur.tv_sec = 0;
dur.tv_nsec = (long)diff * 1000000;
} else {
/* sleep 5 ms */
dur.tv_sec = 0;
dur.tv_nsec = 5000000;
}
nanosleep(&dur, NULL);
}
return rc;
}
#endif

158
compat/compat.h.in Normal file
View file

@ -0,0 +1,158 @@
/**
* @file compat.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief compatibility functions header
*
* Copyright (c) 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef _COMPAT_H_
#define _COMPAT_H_
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#ifndef __WORDSIZE
# if defined __x86_64__ && !defined __ILP32__
# define __WORDSIZE 64
# else
# define __WORDSIZE 32
# endif
#endif
#ifndef __INT64_C
# if __WORDSIZE == 64
# define __INT64_C(c) c ## L
# define __UINT64_C(c) c ## UL
# else
# define __INT64_C(c) c ## LL
# define __UINT64_C(c) c ## ULL
# endif
#endif
#if (@CMAKE_C_COMPILER_ID@ == GNU) || (@CMAKE_C_COMPILER_ID@ == Clang)
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
# define _PACKED __attribute__((__packed__))
#else
# define UNUSED(x) UNUSED_ ## x
# define _PACKED
#endif
#cmakedefine HAVE_VDPRINTF
#cmakedefine HAVE_ASPRINTF
#cmakedefine HAVE_VASPRINTF
#cmakedefine HAVE_GETLINE
#cmakedefine HAVE_STRNDUP
#cmakedefine HAVE_STRNSTR
#cmakedefine HAVE_STRDUPA
#cmakedefine HAVE_STRCHRNUL
#cmakedefine HAVE_GET_CURRENT_DIR_NAME
#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK
#ifndef bswap64
#define bswap64(val) \
( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \
(((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \
(((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \
(((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) )
#endif
#undef le64toh
#undef htole64
#cmakedefine IS_BIG_ENDIAN
#ifdef IS_BIG_ENDIAN
# define le64toh(x) bswap64(x)
# define htole64(x) bswap64(x)
#else
# define le64toh(x) (x)
# define htole64(x) (x)
#endif
#cmakedefine HAVE_STDATOMIC
#ifdef HAVE_STDATOMIC
# include <stdatomic.h>
# define ATOMIC_T atomic_uint_fast32_t
# define ATOMIC_T_MAX UINT_FAST32_MAX
# define ATOMIC_STORE_RELAXED(var, x) atomic_store_explicit(&(var), x, memory_order_relaxed)
# define ATOMIC_LOAD_RELAXED(var) atomic_load_explicit(&(var), memory_order_relaxed)
# define ATOMIC_INC_RELAXED(var) atomic_fetch_add_explicit(&(var), 1, memory_order_relaxed)
# define ATOMIC_ADD_RELAXED(var, x) atomic_fetch_add_explicit(&(var), x, memory_order_relaxed)
# define ATOMIC_DEC_RELAXED(var) atomic_fetch_sub_explicit(&(var), 1, memory_order_relaxed)
# define ATOMIC_SUB_RELAXED(var, x) atomic_fetch_sub_explicit(&(var), x, memory_order_relaxed)
#else
# include <stdint.h>
# define ATOMIC_T uint32_t
# define ATOMIC_T_MAX UINT32_MAX
# define ATOMIC_STORE_RELAXED(var, x) ((var) = (x))
# define ATOMIC_LOAD_RELAXED(var) (var)
# define ATOMIC_INC_RELAXED(var) __sync_fetch_and_add(&(var), 1)
# define ATOMIC_ADD_RELAXED(var, x) __sync_fetch_and_add(&(var), x)
# define ATOMIC_DEC_RELAXED(var) __sync_fetch_and_sub(&(var), 1)
# define ATOMIC_SUB_RELAXED(var, x) __sync_fetch_and_sub(&(var), x)
#endif
#ifndef HAVE_VDPRINTF
int vdprintf(int fd, const char *format, va_list ap);
#endif
#ifndef HAVE_ASPRINTF
int asprintf(char **strp, const char *fmt, ...);
#endif
#ifndef HAVE_VASPRINTF
int vasprintf(char **strp, const char *fmt, va_list ap);
#endif
#ifndef HAVE_GETLINE
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
#endif
#ifndef HAVE_STRNDUP
char *strndup(const char *s, size_t n);
#endif
#ifndef HAVE_STRNSTR
char *strnstr(const char *s, const char *find, size_t slen);
#endif
#ifndef HAVE_STRDUPA
#define strdupa(s) ( \
{ \
char *buf; \
size_t len = strlen(s); \
buf = alloca(len + 1); \
buf[len] = '\0'; \
(char *)memcpy(buf, s, len); \
})
#endif
#ifndef HAVE_STRCHRNUL
char *strchrnul(const char *s, int c);
#endif
#ifndef HAVE_GET_CURRENT_DIR_NAME
char *get_current_dir_name(void);
#endif
#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime);
#endif
#endif /* _COMPAT_H_ */

10
distro/config/apkg.toml Normal file
View file

@ -0,0 +1,10 @@
[project]
name = "libnetconf2"
make_archive_script = "distro/scripts/make-archive.sh"
[upstream]
archive_url = "https://github.com/CESNET/libnetconf2/archive/refs/tags/v{{ version }}.tar.gz"
version_script = "distro/scripts/upstream-version.sh"
[apkg]
compat = 1

5
distro/pkg/deb/changelog Normal file
View file

@ -0,0 +1,5 @@
libnetconf2 ({{ version }}-{{ release }}) unstable; urgency=medium
* upstream packaging
-- Michal Vaško <mvasko@cesnet.cz> Fri, 01 Oct 2021 14:29:03 +0200

1
distro/pkg/deb/compat Normal file
View file

@ -0,0 +1 @@
10

44
distro/pkg/deb/control Normal file
View file

@ -0,0 +1,44 @@
Source: libnetconf2
Section: libs
Homepage: https://github.com/CESNET/libnetconf2/
Maintainer: CESNET <mvasko@cesnet.cz>
Priority: optional
Standards-Version: 4.5.0
Build-Depends: cmake,
debhelper (>= 10),
libyang2-dev,
libssl-dev,
libssh-dev (>= 0.7.1),
pkg-config
Vcs-Browser: https://github.com/CESNET/libnetconf2/tree/master
Vcs-Git: https://github.com/CESNET/libnetconf2.git
Package: libnetconf2-2
Depends: ${misc:Depends},
${shlibs:Depends}
Architecture: any
Description: library implementing NETCONF protocol - runtime
Libnetconf2 implements network communication using NETCONF
protocol specified in IETF RFC 6241. It is based on libnetconf
(which it replaces and makes obsolete) but written from scratch.
.
Both server and client-side functionality is provided.
.
It is implemented in C.
Package: libnetconf2-dev
Depends: libyang2-dev,
libnetconf2-2 (= ${binary:Version}),
${misc:Depends}
Section: libdevel
Architecture: any
Description: library implementing NETCONF protocol - development files
Libnetconf2 implements network communication using NETCONF
protocol specified in IETF RFC 6241. It is based on libnetconf
(which it replaces and makes obsolete) but written from scratch.
.
Both server and client-side functionality is provided.
.
This package contains the C headers, a pkgconfig file, and .so entry
point for libnetconf2.

46
distro/pkg/deb/copyright Normal file
View file

@ -0,0 +1,46 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: libnetconf2
License: BSD-3-clause
Files: *
Copyright: 2015-2021 by CESNET, z.s.p.o.
License: BSD-3-clause
Files: */ietf-*.yang */ietf-*.h
Copyright: 2011-2018 by the IETF Trust and the persons identified as authors
License: IETF-BSD-3-clause
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
.
This version of this YANG module is part of RFC 6536; see
the RFC itself for full legal notices.
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
* Neither the name of libyang nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
distro/pkg/deb/gbp.conf Normal file
View file

@ -0,0 +1,4 @@
[DEFAULT]
pristine-tar = False
debian-branch = master
upstream-tree = SLOPPY

View file

@ -0,0 +1 @@
usr/lib/*/libnetconf2.so.*

View file

@ -0,0 +1,5 @@
usr/lib/*/libnetconf2.so
usr/lib/*/pkgconfig/libnetconf2.pc
usr/include/libnetconf2/*
usr/include/nc_client.h
usr/include/nc_server.h

12
distro/pkg/deb/rules Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/make -f
#export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
include /usr/share/dpkg/default.mk
%:
dh $@
override_dh_auto_configure:
dh_auto_configure -- \
-DCMAKE_BUILD_TYPE:String="Release"

View file

@ -0,0 +1 @@
3.0 (quilt)

4
distro/pkg/deb/watch Normal file
View file

@ -0,0 +1,4 @@
version=4
opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%libnetconf2-$1.tar.gz%" \
https://github.com/CESNET/libnetconf2/releases \
(?:.*?/)?v?(\d[\d.]*)(?:-r\d+)?\.tar\.gz debian uupdate

View file

@ -0,0 +1,61 @@
Name: libnetconf2
Version: {{ version }}
Release: {{ release }}%{?dist}
Summary: NETCONF protocol library
Url: https://github.com/CESNET/libnetconf2
Source: libnetconf2-%{version}.tar.gz
License: BSD
BuildRequires: cmake
BuildRequires: make
BuildRequires: gcc
BuildRequires: libssh-devel
BuildRequires: openssl-devel
BuildRequires: pkgconfig(libyang) >= 2
%package devel
Summary: Headers of libnetconf2 library
Conflicts: libnetconf-devel
Requires: %{name}%{?_isa} = %{version}-%{release}
%description devel
Headers of libnetconf library.
%description
libnetconf2 is a NETCONF library in C intended for building NETCONF clients and
servers. NETCONF is the NETwork CONFiguration protocol introduced by IETF.
%prep
%autosetup -p1
mkdir build
%build
cd build
cmake \
-DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
-DCMAKE_BUILD_TYPE:String="Release" \
-DCMAKE_C_FLAGS="${RPM_OPT_FLAGS}" \
-DCMAKE_CXX_FLAGS="${RPM_OPT_FLAGS}" \
..
make
%install
cd build
make DESTDIR=%{buildroot} install
%files
%license LICENSE
%{_libdir}/libnetconf2.so.2*
%files devel
%{_libdir}/libnetconf2.so
%{_libdir}/pkgconfig/libnetconf2.pc
%{_includedir}/*.h
%{_includedir}/libnetconf2/*.h
%dir %{_includedir}/libnetconf2/
%changelog
* Tue Oct 12 2021 Jakub Ružička <jakub.ruzicka@nic.cz> - {{ version }}-{{ release }}
- upstream package

17
distro/scripts/make-archive.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
# create archive from current source using git
VERSION=$(git describe --tags --always)
# skip "v" from start of version number (if it exists) and replace - with .
VERSION=${VERSION#v}
VERSION=${VERSION//[-]/.}
NAMEVER=libnetconf2-$VERSION
ARCHIVE=$NAMEVER.tar.gz
git archive --format tgz --output $ARCHIVE --prefix $NAMEVER/ HEAD
mkdir -p pkg/archives/dev/
mv $ARCHIVE pkg/archives/dev/
# apkg expects stdout to list archive files
echo pkg/archives/dev/$ARCHIVE

View file

@ -0,0 +1,7 @@
#!/bin/bash
# get latest upstream libnetconf2 version from github
RLS_URL=https://api.github.com/repos/CESNET/libnetconf2/releases
VERSION=$(curl -s $RLS_URL | grep tag_name | cut -d '"' -f 4 | sort --version-sort | tail -n 1)
VERSION=${VERSION#v}
echo $VERSION

106
doc/cesnet-style.css Normal file
View file

@ -0,0 +1,106 @@
/* CESNET blue: #0068a2 */
body {
background-color: #fff;
}
div.header {
background-image: none;
background-color: #fff;
}
div.contents {
background-color: #fff;
padding: 1.618em 3.236em;
max-width: 60em;
margin: auto;
margin-left: 0;
text-align: justify;
}
.sm-dox {
background-image: none;
background-color: #0068a2;
border-bottom: 1px solid white;
}
.sm-dox a {
background-image: none;
border-right: 1px solid white;
color: white;
text-shadow: none;
}
.sm-dox a:hover {
background-image: none;
background-color: rgba(0,0,0,0.3);
}
.sm-dox ul a:hover {
background-image: none;
background-color: #ddd;
text-shadow: none;
color: #555;
}
.navpath ul {
background-image: none;
background-color: #0068a2;
}
.navpath li.footer {
color: white;
}
img.footer {
height: 20px;
}
.navpath li.navelem a {
color: white;
text-shadow: none;
}
#side-nav {
background-color: #343131;
}
#nav-tree::-webkit-scrollbar {
width: 5px;
}
#nav-tree::-webkit-scrollbar-track {
background: #333;
border-radius: 50px;
}
#nav-tree::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 50px;
}
#nav-tree {
background: none;
}
#nav-tree .item {
padding-top: 10px;
padding-bottom: 10px;
}
#nav-tree .item:hover {
background-color: rgba(255,255,255,0.2);
}
#nav-tree a {
color: #fff;
font-size: 1.2em;
}
#nav-tree .selected {
background-image: none;
background-color: #0068a2;
}
#nav-tree-contents {
margin: 0;
}

12
libnetconf2.pc.in Normal file
View file

@ -0,0 +1,12 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
Name: @PROJECT_NAME@
Description: @LIBNETCONF2_DESCRIPTION@
Version: @LIBNETCONF2_VERSION@
Libs: -L${libdir} -lnetconf2
Cflags: -I${includedir}
LNC2_MAX_THREAD_COUNT=@MAX_PSPOLL_THREAD_COUNT@
LNC2_SCHEMAS_DIR=@SCHEMAS_DIR@

View file

@ -0,0 +1,464 @@
module ietf-netconf-acm {
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
prefix nacm;
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
Author: Andy Bierman
<mailto:andy@yumaworks.com>
Author: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"Network Configuration Access Control Model.
Copyright (c) 2012 - 2018 IETF Trust and the persons
identified as authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8341; see
the RFC itself for full legal notices.";
revision "2018-02-14" {
description
"Added support for YANG 1.1 actions and notifications tied to
data nodes. Clarified how NACM extensions can be used by
other data models.";
reference
"RFC 8341: Network Configuration Access Control Model";
}
revision "2012-02-22" {
description
"Initial version.";
reference
"RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model";
}
/*
* Extension statements
*/
extension default-deny-write {
description
"Used to indicate that the data model node
represents a sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have write access to the node. An
explicit access control rule is required for all other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.";
}
extension default-deny-all {
description
"Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have read, write, or execute access to
the node. An explicit access control rule is required for all
other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.";
}
/*
* Derived types
*/
typedef user-name-type {
type string {
length "1..max";
}
description
"General-purpose username string.";
}
typedef matchall-string-type {
type string {
pattern '\*';
}
description
"The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.";
}
typedef access-operations-type {
type bits {
bit create {
description
"Any protocol operation that creates a
new data node.";
}
bit read {
description
"Any protocol operation or notification that
returns the value of a data node.";
}
bit update {
description
"Any protocol operation that alters an existing
data node.";
}
bit delete {
description
"Any protocol operation that removes a data node.";
}
bit exec {
description
"Execution access to the specified protocol operation.";
}
}
description
"Access operation.";
}
typedef group-name-type {
type string {
length "1..max";
pattern '[^\*].*';
}
description
"Name of administrative group to which
users can be assigned.";
}
typedef action-type {
type enumeration {
enum permit {
description
"Requested action is permitted.";
}
enum deny {
description
"Requested action is denied.";
}
}
description
"Action taken by the server when a particular
rule matches.";
}
typedef node-instance-identifier {
type yang:xpath1.0;
description
"Path expression used to represent a special
data node, action, or notification instance-identifier
string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply,
except that predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XML Path Language (XPath) expression is evaluated in the
following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the
current session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.
The accessible tree includes actions and notifications tied
to data nodes.";
}
/*
* Data definition statements
*/
container nacm {
nacm:default-deny-all;
description
"Parameters for NETCONF access control model.";
leaf enable-nacm {
type boolean;
default "true";
description
"Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.";
}
leaf read-default {
type action-type;
default "permit";
description
"Controls whether read access is granted if
no appropriate rule is found for a
particular read request.";
}
leaf write-default {
type action-type;
default "deny";
description
"Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.";
}
leaf exec-default {
type action-type;
default "permit";
description
"Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.";
}
leaf enable-external-groups {
type boolean;
default "true";
description
"Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.";
}
leaf denied-operations {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request was denied.";
}
leaf denied-data-writes {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.";
}
leaf denied-notifications {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.";
}
container groups {
description
"NETCONF access control groups.";
list group {
key name;
description
"One NACM group entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.";
leaf name {
type group-name-type;
description
"Group name associated with this entry.";
}
leaf-list user-name {
type user-name-type;
description
"Each entry identifies the username of
a member of the group associated with
this entry.";
}
}
}
list rule-list {
key name;
ordered-by user;
description
"An ordered collection of access control rules.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule-list.";
}
leaf-list group {
type union {
type matchall-string-type;
type group-name-type;
}
description
"List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.";
}
list rule {
key name;
ordered-by user;
description
"One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines whether or not
access is granted.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule.";
}
leaf module-name {
type union {
type matchall-string-type;
type string;
}
default "*";
description
"Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.";
}
choice rule-type {
description
"This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.";
case protocol-operation {
leaf rpc-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.";
}
}
case notification {
leaf notification-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if its
value equals the requested notification name.";
}
}
case data-node {
leaf path {
type node-instance-identifier;
mandatory true;
description
"Data node instance-identifier associated with the
data node, action, or notification controlled by
this rule.
Configuration data or state data
instance-identifiers start with a top-level
data node. A complete instance-identifier is
required for this type of path value.
The special value '/' refers to all possible
datastore contents.";
}
}
}
leaf access-operations {
type union {
type matchall-string-type;
type access-operations-type;
}
default "*";
description
"Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.";
}
leaf action {
type action-type;
mandatory true;
description
"The access control action associated with the
rule. If a rule has been determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.";
}
leaf comment {
type string;
description
"A textual description of the access rule.";
}
}
}
}
}

View file

@ -0,0 +1,558 @@
module ietf-netconf-monitoring {
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
prefix "ncm";
import ietf-yang-types { prefix yang; }
import ietf-inet-types { prefix inet; }
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
WG Chair: Mehmet Ersue
<mailto:mehmet.ersue@nsn.com>
WG Chair: Bert Wijnen
<mailto:bertietf@bwijnen.net>
Editor: Mark Scott
<mailto:mark.scott@ericsson.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"NETCONF Monitoring Module.
All elements in this module are read-only.
Copyright (c) 2010 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6022; see
the RFC itself for full legal notices.";
revision 2010-10-04 {
description
"Initial revision.";
reference
"RFC 6022: YANG Module for NETCONF Monitoring";
}
typedef netconf-datastore-type {
type enumeration {
enum running;
enum candidate;
enum startup;
}
description
"Enumeration of possible NETCONF datastore types.";
reference
"RFC 4741: NETCONF Configuration Protocol";
}
identity transport {
description
"Base identity for NETCONF transport types.";
}
identity netconf-ssh {
base transport;
description
"NETCONF over Secure Shell (SSH).";
reference
"RFC 4742: Using the NETCONF Configuration Protocol
over Secure SHell (SSH)";
}
identity netconf-soap-over-beep {
base transport;
description
"NETCONF over Simple Object Access Protocol (SOAP) over
Blocks Extensible Exchange Protocol (BEEP).";
reference
"RFC 4743: Using NETCONF over the Simple Object
Access Protocol (SOAP)";
}
identity netconf-soap-over-https {
base transport;
description
"NETCONF over Simple Object Access Protocol (SOAP)
over Hypertext Transfer Protocol Secure (HTTPS).";
reference
"RFC 4743: Using NETCONF over the Simple Object
Access Protocol (SOAP)";
}
identity netconf-beep {
base transport;
description
"NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
reference
"RFC 4744: Using the NETCONF Protocol over the
Blocks Extensible Exchange Protocol (BEEP)";
}
identity netconf-tls {
base transport;
description
"NETCONF over Transport Layer Security (TLS).";
reference
"RFC 5539: NETCONF over Transport Layer Security (TLS)";
}
identity schema-format {
description
"Base identity for data model schema languages.";
}
identity xsd {
base schema-format;
description
"W3C XML Schema Definition.";
reference
"W3C REC REC-xmlschema-1-20041028:
XML Schema Part 1: Structures";
}
identity yang {
base schema-format;
description
"The YANG data modeling language for NETCONF.";
reference
"RFC 6020: YANG - A Data Modeling Language for the
Network Configuration Protocol (NETCONF)";
}
identity yin {
base schema-format;
description
"The YIN syntax for YANG.";
reference
"RFC 6020: YANG - A Data Modeling Language for the
Network Configuration Protocol (NETCONF)";
}
identity rng {
base schema-format;
description
"Regular Language for XML Next Generation (RELAX NG).";
reference
"ISO/IEC 19757-2:2008: RELAX NG";
}
identity rnc {
base schema-format;
description
"Relax NG Compact Syntax";
reference
"ISO/IEC 19757-2:2008: RELAX NG";
}
grouping common-counters {
description
"Counters that exist both per session, and also globally,
accumulated from all sessions.";
leaf in-rpcs {
type yang:zero-based-counter32;
description
"Number of correct <rpc> messages received.";
}
leaf in-bad-rpcs {
type yang:zero-based-counter32;
description
"Number of messages received when an <rpc> message was expected,
that were not correct <rpc> messages. This includes XML parse
errors and errors on the rpc layer.";
}
leaf out-rpc-errors {
type yang:zero-based-counter32;
description
"Number of <rpc-reply> messages sent that contained an
<rpc-error> element.";
}
leaf out-notifications {
type yang:zero-based-counter32;
description
"Number of <notification> messages sent.";
}
}
container netconf-state {
config false;
description
"The netconf-state container is the root of the monitoring
data model.";
container capabilities {
description
"Contains the list of NETCONF capabilities supported by the
server.";
leaf-list capability {
type inet:uri;
description
"List of NETCONF capabilities supported by the server.";
}
}
container datastores {
description
"Contains the list of NETCONF configuration datastores.";
list datastore {
key name;
description
"List of NETCONF configuration datastores supported by
the NETCONF server and related information.";
leaf name {
type netconf-datastore-type;
description
"Name of the datastore associated with this list entry.";
}
container locks {
presence
"This container is present only if the datastore
is locked.";
description
"The NETCONF <lock> and <partial-lock> operations allow
a client to lock specific resources in a datastore. The
NETCONF server will prevent changes to the locked
resources by all sessions except the one that acquired
the lock(s).
Monitoring information is provided for each datastore
entry including details such as the session that acquired
the lock, the type of lock (global or partial) and the
list of locked resources. Multiple locks per datastore
are supported.";
grouping lock-info {
description
"Lock related parameters, common to both global and
partial locks.";
leaf locked-by-session {
type uint32;
mandatory true;
description
"The session ID of the session that has locked
this resource. Both a global lock and a partial
lock MUST contain the NETCONF session-id.
If the lock is held by a session that is not managed
by the NETCONF server (e.g., a CLI session), a session
id of 0 (zero) is reported.";
reference
"RFC 4741: NETCONF Configuration Protocol";
}
leaf locked-time {
type yang:date-and-time;
mandatory true;
description
"The date and time of when the resource was
locked.";
}
}
choice lock-type {
description
"Indicates if a global lock or a set of partial locks
are set.";
container global-lock {
description
"Present if the global lock is set.";
uses lock-info;
}
list partial-lock {
key lock-id;
description
"List of partial locks.";
reference
"RFC 5717: Partial Lock Remote Procedure Call (RPC) for
NETCONF";
leaf lock-id {
type uint32;
description
"This is the lock id returned in the <partial-lock>
response.";
}
uses lock-info;
leaf-list select {
type yang:xpath1.0;
min-elements 1;
description
"The xpath expression that was used to request
the lock. The select expression indicates the
original intended scope of the lock.";
}
leaf-list locked-node {
type instance-identifier;
description
"The list of instance-identifiers (i.e., the
locked nodes).
The scope of the partial lock is defined by the list
of locked nodes.";
}
}
}
}
}
}
container schemas {
description
"Contains the list of data model schemas supported by the
server.";
list schema {
key "identifier version format";
description
"List of data model schemas supported by the server.";
leaf identifier {
type string;
description
"Identifier to uniquely reference the schema. The
identifier is used in the <get-schema> operation and may
be used for other purposes such as file retrieval.
For modeling languages that support or require a data
model name (e.g., YANG module name) the identifier MUST
match that name. For YANG data models, the identifier is
the name of the module or submodule. In other cases, an
identifier such as a filename MAY be used instead.";
}
leaf version {
type string;
description
"Version of the schema supported. Multiple versions MAY be
supported simultaneously by a NETCONF server. Each
version MUST be reported individually in the schema list,
i.e., with same identifier, possibly different location,
but different version.
For YANG data models, version is the value of the most
recent YANG 'revision' statement in the module or
submodule, or the empty string if no 'revision' statement
is present.";
}
leaf format {
type identityref {
base schema-format;
}
description
"The data modeling language the schema is written
in (currently xsd, yang, yin, rng, or rnc).
For YANG data models, 'yang' format MUST be supported and
'yin' format MAY also be provided.";
}
leaf namespace {
type inet:uri;
mandatory true;
description
"The XML namespace defined by the data model.
For YANG data models, this is the module's namespace.
If the list entry describes a submodule, this field
contains the namespace of the module to which the
submodule belongs.";
}
leaf-list location {
type union {
type enumeration {
enum "NETCONF";
}
type inet:uri;
}
description
"One or more locations from which the schema can be
retrieved. This list SHOULD contain at least one
entry per schema.
A schema entry may be located on a remote file system
(e.g., reference to file system for ftp retrieval) or
retrieved directly from a server supporting the
<get-schema> operation (denoted by the value 'NETCONF').";
}
}
}
container sessions {
description
"The sessions container includes session-specific data for
NETCONF management sessions. The session list MUST include
all currently active NETCONF sessions.";
list session {
key session-id;
description
"All NETCONF sessions managed by the NETCONF server
MUST be reported in this list.";
leaf session-id {
type uint32 {
range "1..max";
}
description
"Unique identifier for the session. This value is the
NETCONF session identifier, as defined in RFC 4741.";
reference
"RFC 4741: NETCONF Configuration Protocol";
}
leaf transport {
type identityref {
base transport;
}
mandatory true;
description
"Identifies the transport for each session, e.g.,
'netconf-ssh', 'netconf-soap', etc.";
}
leaf username {
type string;
mandatory true;
description
"The username is the client identity that was authenticated
by the NETCONF transport protocol. The algorithm used to
derive the username is NETCONF transport protocol specific
and in addition specific to the authentication mechanism
used by the NETCONF transport protocol.";
}
leaf source-host {
type inet:host;
description
"Host identifier of the NETCONF client. The value
returned is implementation specific (e.g., hostname,
IPv4 address, IPv6 address)";
}
leaf login-time {
type yang:date-and-time;
mandatory true;
description
"Time at the server at which the session was established.";
}
uses common-counters {
description
"Per-session counters. Zero based with following reset
behaviour:
- at start of a session
- when max value is reached";
}
}
}
container statistics {
description
"Statistical data pertaining to the NETCONF server.";
leaf netconf-start-time {
type yang:date-and-time;
description
"Date and time at which the management subsystem was
started.";
}
leaf in-bad-hellos {
type yang:zero-based-counter32;
description
"Number of sessions silently dropped because an
invalid <hello> message was received. This includes <hello>
messages with a 'session-id' attribute, bad namespace, and
bad capability declarations.";
}
leaf in-sessions {
type yang:zero-based-counter32;
description
"Number of sessions started. This counter is incremented
when a <hello> message with a <session-id> is sent.
'in-sessions' - 'in-bad-hellos' =
'number of correctly started netconf sessions'";
}
leaf dropped-sessions {
type yang:zero-based-counter32;
description
"Number of sessions that were abnormally terminated, e.g.,
due to idle timeout or transport close. This counter is not
incremented when a session is properly closed by a
<close-session> operation, or killed by a <kill-session>
operation.";
}
uses common-counters {
description
"Global counters, accumulated from all sessions.
Zero based with following reset behaviour:
- re-initialization of NETCONF server
- when max value is reached";
}
}
}
rpc get-schema {
description
"This operation is used to retrieve a schema from the
NETCONF server.
Positive Response:
The NETCONF server returns the requested schema.
Negative Response:
If requested schema does not exist, the <error-tag> is
'invalid-value'.
If more than one schema matches the requested parameters, the
<error-tag> is 'operation-failed', and <error-app-tag> is
'data-not-unique'.";
input {
leaf identifier {
type string;
mandatory true;
description
"Identifier for the schema list entry.";
}
leaf version {
type string;
description
"Version of the schema requested. If this parameter is not
present, and more than one version of the schema exists on
the server, a 'data-not-unique' error is returned, as
described above.";
}
leaf format {
type identityref {
base schema-format;
}
description
"The data modeling language of the schema. If this
parameter is not present, and more than one formats of
the schema exists on the server, a 'data-not-unique' error
is returned, as described above.";
}
}
output {
anyxml data {
description
"Contains the schema content.";
}
}
}
}

View file

@ -0,0 +1,939 @@
module ietf-netconf {
// the namespace for NETCONF XML definitions is unchanged
// from RFC 4741, which this document replaces
namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
prefix nc;
import ietf-inet-types {
prefix inet;
}
import ietf-netconf-acm { prefix nacm; }
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netconf/>
WG List: <netconf@ietf.org>
WG Chair: Bert Wijnen
<bertietf@bwijnen.net>
WG Chair: Mehmet Ersue
<mehmet.ersue@nsn.com>
Editor: Martin Bjorklund
<mbj@tail-f.com>
Editor: Juergen Schoenwaelder
<j.schoenwaelder@jacobs-university.de>
Editor: Andy Bierman
<andy.bierman@brocade.com>";
description
"NETCONF Protocol Data Types and Protocol Operations.
Copyright (c) 2011 IETF Trust and the persons identified as
the document authors. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6241; see
the RFC itself for full legal notices.";
revision 2013-09-29 {
description
"Updated to include NACM attributes";
reference
"RFC 6536: sec 3.2.5 and 3.2.8";
}
revision 2011-06-01 {
description
"Initial revision";
reference
"RFC 6241: Network Configuration Protocol";
}
extension get-filter-element-attributes {
description
"If this extension is present within an 'anyxml'
statement named 'filter', which must be conceptually
defined within the RPC input section for the <get>
and <get-config> protocol operations, then the
following unqualified XML attribute is supported
within the <filter> element, within a <get> or
<get-config> protocol operation:
type : optional attribute with allowed
value strings 'subtree' and 'xpath'.
If missing, the default value is 'subtree'.
If the 'xpath' feature is supported, then the
following unqualified XML attribute is
also supported:
select: optional attribute containing a
string representing an XPath expression.
The 'type' attribute must be equal to 'xpath'
if this attribute is present.";
}
// NETCONF capabilities defined as features
feature writable-running {
description
"NETCONF :writable-running capability;
If the server advertises the :writable-running
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.2";
}
feature candidate {
description
"NETCONF :candidate capability;
If the server advertises the :candidate
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.3";
}
feature confirmed-commit {
if-feature candidate;
description
"NETCONF :confirmed-commit:1.1 capability;
If the server advertises the :confirmed-commit:1.1
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.4";
}
feature rollback-on-error {
description
"NETCONF :rollback-on-error capability;
If the server advertises the :rollback-on-error
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.5";
}
feature validate {
description
"NETCONF :validate:1.1 capability;
If the server advertises the :validate:1.1
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.6";
}
feature startup {
description
"NETCONF :startup capability;
If the server advertises the :startup
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.7";
}
feature url {
description
"NETCONF :url capability;
If the server advertises the :url
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.8";
}
feature xpath {
description
"NETCONF :xpath capability;
If the server advertises the :xpath
capability for a session, then this feature must
also be enabled for that session. Otherwise,
this feature must not be enabled.";
reference "RFC 6241, Section 8.9";
}
// NETCONF Simple Types
typedef session-id-type {
type uint32 {
range "1..max";
}
description
"NETCONF Session Id";
}
typedef session-id-or-zero-type {
type uint32;
description
"NETCONF Session Id or Zero to indicate none";
}
typedef error-tag-type {
type enumeration {
enum in-use {
description
"The request requires a resource that
already is in use.";
}
enum invalid-value {
description
"The request specifies an unacceptable value for one
or more parameters.";
}
enum too-big {
description
"The request or response (that would be generated) is
too large for the implementation to handle.";
}
enum missing-attribute {
description
"An expected attribute is missing.";
}
enum bad-attribute {
description
"An attribute value is not correct; e.g., wrong type,
out of range, pattern mismatch.";
}
enum unknown-attribute {
description
"An unexpected attribute is present.";
}
enum missing-element {
description
"An expected element is missing.";
}
enum bad-element {
description
"An element value is not correct; e.g., wrong type,
out of range, pattern mismatch.";
}
enum unknown-element {
description
"An unexpected element is present.";
}
enum unknown-namespace {
description
"An unexpected namespace is present.";
}
enum access-denied {
description
"Access to the requested protocol operation or
data model is denied because authorization failed.";
}
enum lock-denied {
description
"Access to the requested lock is denied because the
lock is currently held by another entity.";
}
enum resource-denied {
description
"Request could not be completed because of
insufficient resources.";
}
enum rollback-failed {
description
"Request to roll back some configuration change (via
rollback-on-error or <discard-changes> operations)
was not completed for some reason.";
}
enum data-exists {
description
"Request could not be completed because the relevant
data model content already exists. For example,
a 'create' operation was attempted on data that
already exists.";
}
enum data-missing {
description
"Request could not be completed because the relevant
data model content does not exist. For example,
a 'delete' operation was attempted on
data that does not exist.";
}
enum operation-not-supported {
description
"Request could not be completed because the requested
operation is not supported by this implementation.";
}
enum operation-failed {
description
"Request could not be completed because the requested
operation failed for some reason not covered by
any other error condition.";
}
enum partial-operation {
description
"This error-tag is obsolete, and SHOULD NOT be sent
by servers conforming to this document.";
}
enum malformed-message {
description
"A message could not be handled because it failed to
be parsed correctly. For example, the message is not
well-formed XML or it uses an invalid character set.";
}
}
description "NETCONF Error Tag";
reference "RFC 6241, Appendix A";
}
typedef error-severity-type {
type enumeration {
enum error {
description "Error severity";
}
enum warning {
description "Warning severity";
}
}
description "NETCONF Error Severity";
reference "RFC 6241, Section 4.3";
}
typedef edit-operation-type {
type enumeration {
enum merge {
description
"The configuration data identified by the
element containing this attribute is merged
with the configuration at the corresponding
level in the configuration datastore identified
by the target parameter.";
}
enum replace {
description
"The configuration data identified by the element
containing this attribute replaces any related
configuration in the configuration datastore
identified by the target parameter. If no such
configuration data exists in the configuration
datastore, it is created. Unlike a
<copy-config> operation, which replaces the
entire target configuration, only the configuration
actually present in the config parameter is affected.";
}
enum create {
description
"The configuration data identified by the element
containing this attribute is added to the
configuration if and only if the configuration
data does not already exist in the configuration
datastore. If the configuration data exists, an
<rpc-error> element is returned with an
<error-tag> value of 'data-exists'.";
}
enum delete {
description
"The configuration data identified by the element
containing this attribute is deleted from the
configuration if and only if the configuration
data currently exists in the configuration
datastore. If the configuration data does not
exist, an <rpc-error> element is returned with
an <error-tag> value of 'data-missing'.";
}
enum remove {
description
"The configuration data identified by the element
containing this attribute is deleted from the
configuration if the configuration
data currently exists in the configuration
datastore. If the configuration data does not
exist, the 'remove' operation is silently ignored
by the server.";
}
}
default "merge";
description "NETCONF 'operation' attribute values";
reference "RFC 6241, Section 7.2";
}
// NETCONF Standard Protocol Operations
rpc get-config {
description
"Retrieve all or part of a specified configuration.";
reference "RFC 6241, Section 7.1";
input {
container source {
description
"Particular configuration to retrieve.";
choice config-source {
mandatory true;
description
"The configuration to retrieve.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config source.";
}
leaf running {
type empty;
description
"The running configuration is the config source.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config source.
This is optional-to-implement on the server because
not all servers will support filtering for this
datastore.";
}
}
}
anyxml filter {
description
"Subtree or XPath filter to use.";
nc:get-filter-element-attributes;
}
}
output {
anyxml data {
description
"Copy of the source datastore subset that matched
the filter criteria (if any). An empty data container
indicates that the request did not produce any results.";
}
}
}
rpc edit-config {
description
"The <edit-config> operation loads all or part of a specified
configuration to the specified target configuration.";
reference "RFC 6241, Section 7.2";
input {
container target {
description
"Particular configuration to edit.";
choice config-target {
mandatory true;
description
"The configuration target.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config target.";
}
leaf running {
if-feature writable-running;
type empty;
description
"The running configuration is the config source.";
}
}
}
leaf default-operation {
type enumeration {
enum merge {
description
"The default operation is merge.";
}
enum replace {
description
"The default operation is replace.";
}
enum none {
description
"There is no default operation.";
}
}
default "merge";
description
"The default operation to use.";
}
leaf test-option {
if-feature validate;
type enumeration {
enum test-then-set {
description
"The server will test and then set if no errors.";
}
enum set {
description
"The server will set without a test first.";
}
enum test-only {
description
"The server will only test and not set, even
if there are no errors.";
}
}
default "test-then-set";
description
"The test option to use.";
}
leaf error-option {
type enumeration {
enum stop-on-error {
description
"The server will stop on errors.";
}
enum continue-on-error {
description
"The server may continue on errors.";
}
enum rollback-on-error {
description
"The server will roll back on errors.
This value can only be used if the 'rollback-on-error'
feature is supported.";
}
}
default "stop-on-error";
description
"The error option to use.";
}
choice edit-content {
mandatory true;
description
"The content for the edit operation.";
anyxml config {
description
"Inline Config content.";
}
leaf url {
if-feature url;
type inet:uri;
description
"URL-based config content.";
}
}
}
}
rpc copy-config {
description
"Create or replace an entire configuration datastore with the
contents of another complete configuration datastore.";
reference "RFC 6241, Section 7.3";
input {
container target {
description
"Particular configuration to copy to.";
choice config-target {
mandatory true;
description
"The configuration target of the copy operation.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config target.";
}
leaf running {
if-feature writable-running;
type empty;
description
"The running configuration is the config target.
This is optional-to-implement on the server.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config target.";
}
leaf url {
if-feature url;
type inet:uri;
description
"The URL-based configuration is the config target.";
}
}
}
container source {
description
"Particular configuration to copy from.";
choice config-source {
mandatory true;
description
"The configuration source for the copy operation.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config source.";
}
leaf running {
type empty;
description
"The running configuration is the config source.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config source.";
}
leaf url {
if-feature url;
type inet:uri;
description
"The URL-based configuration is the config source.";
}
anyxml config {
description
"Inline Config content: <config> element. Represents
an entire configuration datastore, not
a subset of the running datastore.";
}
}
}
}
}
rpc delete-config {
nacm:default-deny-all;
description
"Delete a configuration datastore.";
reference "RFC 6241, Section 7.4";
input {
container target {
description
"Particular configuration to delete.";
choice config-target {
mandatory true;
description
"The configuration target to delete.";
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config target.";
}
leaf url {
if-feature url;
type inet:uri;
description
"The URL-based configuration is the config target.";
}
}
}
}
}
rpc lock {
description
"The lock operation allows the client to lock the configuration
system of a device.";
reference "RFC 6241, Section 7.5";
input {
container target {
description
"Particular configuration to lock.";
choice config-target {
mandatory true;
description
"The configuration target to lock.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config target.";
}
leaf running {
type empty;
description
"The running configuration is the config target.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config target.";
}
}
}
}
}
rpc unlock {
description
"The unlock operation is used to release a configuration lock,
previously obtained with the 'lock' operation.";
reference "RFC 6241, Section 7.6";
input {
container target {
description
"Particular configuration to unlock.";
choice config-target {
mandatory true;
description
"The configuration target to unlock.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config target.";
}
leaf running {
type empty;
description
"The running configuration is the config target.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config target.";
}
}
}
}
}
rpc get {
description
"Retrieve running configuration and device state information.";
reference "RFC 6241, Section 7.7";
input {
anyxml filter {
description
"This parameter specifies the portion of the system
configuration and state data to retrieve.";
nc:get-filter-element-attributes;
}
}
output {
anyxml data {
description
"Copy of the running datastore subset and/or state
data that matched the filter criteria (if any).
An empty data container indicates that the request did not
produce any results.";
}
}
}
rpc close-session {
description
"Request graceful termination of a NETCONF session.";
reference "RFC 6241, Section 7.8";
}
rpc kill-session {
nacm:default-deny-all;
description
"Force the termination of a NETCONF session.";
reference "RFC 6241, Section 7.9";
input {
leaf session-id {
type session-id-type;
mandatory true;
description
"Particular session to kill.";
}
}
}
rpc commit {
if-feature candidate;
description
"Commit the candidate configuration as the device's new
current configuration.";
reference "RFC 6241, Section 8.3.4.1";
input {
leaf confirmed {
if-feature confirmed-commit;
type empty;
description
"Requests a confirmed commit.";
reference "RFC 6241, Section 8.3.4.1";
}
leaf confirm-timeout {
if-feature confirmed-commit;
type uint32 {
range "1..max";
}
units "seconds";
default "600"; // 10 minutes
description
"The timeout interval for a confirmed commit.";
reference "RFC 6241, Section 8.3.4.1";
}
leaf persist {
if-feature confirmed-commit;
type string;
description
"This parameter is used to make a confirmed commit
persistent. A persistent confirmed commit is not aborted
if the NETCONF session terminates. The only way to abort
a persistent confirmed commit is to let the timer expire,
or to use the <cancel-commit> operation.
The value of this parameter is a token that must be given
in the 'persist-id' parameter of <commit> or
<cancel-commit> operations in order to confirm or cancel
the persistent confirmed commit.
The token should be a random string.";
reference "RFC 6241, Section 8.3.4.1";
}
leaf persist-id {
if-feature confirmed-commit;
type string;
description
"This parameter is given in order to commit a persistent
confirmed commit. The value must be equal to the value
given in the 'persist' parameter to the <commit> operation.
If it does not match, the operation fails with an
'invalid-value' error.";
reference "RFC 6241, Section 8.3.4.1";
}
}
}
rpc discard-changes {
if-feature candidate;
description
"Revert the candidate configuration to the current
running configuration.";
reference "RFC 6241, Section 8.3.4.2";
}
rpc cancel-commit {
if-feature confirmed-commit;
description
"This operation is used to cancel an ongoing confirmed commit.
If the confirmed commit is persistent, the parameter
'persist-id' must be given, and it must match the value of the
'persist' parameter.";
reference "RFC 6241, Section 8.4.4.1";
input {
leaf persist-id {
type string;
description
"This parameter is given in order to cancel a persistent
confirmed commit. The value must be equal to the value
given in the 'persist' parameter to the <commit> operation.
If it does not match, the operation fails with an
'invalid-value' error.";
}
}
}
rpc validate {
if-feature validate;
description
"Validates the contents of the specified configuration.";
reference "RFC 6241, Section 8.6.4.1";
input {
container source {
description
"Particular configuration to validate.";
choice config-source {
mandatory true;
description
"The configuration source to validate.";
leaf candidate {
if-feature candidate;
type empty;
description
"The candidate configuration is the config source.";
}
leaf running {
type empty;
description
"The running configuration is the config source.";
}
leaf startup {
if-feature startup;
type empty;
description
"The startup configuration is the config source.";
}
leaf url {
if-feature url;
type inet:uri;
description
"The URL-based configuration is the config source.";
}
anyxml config {
description
"Inline Config content: <config> element. Represents
an entire configuration datastore, not
a subset of the running datastore.";
}
}
}
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

35
nc_client.h.in Normal file
View file

@ -0,0 +1,35 @@
/**
* \file nc_client.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2's main public header for NETCONF clients.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_CLIENT_H_
#define NC_CLIENT_H_
@SSH_MACRO@
@TLS_MACRO@
#ifdef __cplusplus
extern "C" {
#endif
#include <libnetconf2/netconf.h>
#include <libnetconf2/log.h>
#include <libnetconf2/messages_client.h>
#include <libnetconf2/session_client.h>
#include <libnetconf2/session_client_ch.h>
#ifdef __cplusplus
}
#endif
#endif /* NC_CLIENT_H_ */

35
nc_server.h.in Normal file
View file

@ -0,0 +1,35 @@
/**
* \file nc_server.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2's main public header for NETCONF servers.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SERVER_H_
#define NC_SERVER_H_
@SSH_MACRO@
@TLS_MACRO@
#ifdef __cplusplus
extern "C" {
#endif
#include <libnetconf2/netconf.h>
#include <libnetconf2/log.h>
#include <libnetconf2/messages_server.h>
#include <libnetconf2/session_server.h>
#include <libnetconf2/session_server_ch.h>
#ifdef __cplusplus
}
#endif
#endif /* NC_SERVER_H_ */

69
src/config.h.in Normal file
View file

@ -0,0 +1,69 @@
/**
* \file config.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 various configuration settings.
*
* Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_CONFIG_H_
#define NC_CONFIG_H_
/*
* Mark all objects as hidden and export only objects explicitly marked to be part of the public API or
* those marked as mock objects for testing purpose
*/
#define API __attribute__((visibility("default")))
#define MOCK __attribute__((visibility("default")))
/*
* Support for getpeereid
*/
#cmakedefine HAVE_GETPEEREID
/*
* Support for shadow file manipulation
*/
#cmakedefine HAVE_SHADOW
/*
* Support for crypt.h
*/
#cmakedefine HAVE_CRYPT
/*
* Location of installed basic YANG modules on the system
*/
#define NC_YANG_DIR "@YANG_MODULE_DIR@"
/*
* Inactive read timeout
*/
#define NC_READ_INACT_TIMEOUT @READ_INACTIVE_TIMEOUT@
/*
* Active read timeout in seconds
* (also used for internal <get-schema> RPC reply timeout)
*/
#define NC_READ_ACT_TIMEOUT @READ_ACTIVE_TIMEOUT@
/*
* pspoll structure queue size (also found in nc_server.h)
*/
#define NC_PS_QUEUE_SIZE @MAX_PSPOLL_THREAD_COUNT@
/* Microseconds after which tasks are repeated until the full timeout elapses.
* A millisecond (1000) should be divisible by this number without remain.
*/
#define NC_TIMEOUT_STEP @TIMEOUT_STEP@
/* Portability feature-check macros. */
#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
#endif /* NC_CONFIG_H_ */

1212
src/io.c Normal file

File diff suppressed because it is too large Load diff

669
src/libnetconf.h Normal file
View file

@ -0,0 +1,669 @@
/**
* @file libnetconf.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 main internal header.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LIBNETCONF_H_
#define NC_LIBNETCONF_H_
#include "config.h"
#include "log_p.h"
#include "messages_p.h"
#include "netconf.h"
#include "session_p.h"
/* Tests whether string is empty or non-empty. */
#define strisempty(str) ((str)[0] == '\0')
#define strnonempty(str) ((str)[0] != '\0')
/**
* @mainpage About
*
* libnetconf2 is a NETCONF library in C handling NETCONF authentication and all NETCONF
* RPC communication both server and client-side. Note that NETCONF datastore implementation
* is not a part of this library. The library supports both NETCONF 1.0
* ([RFC 4741](https://tools.ietf.org/html/rfc4741)) as well as NETCONF 1.1
* ([RFC 6241](https://tools.ietf.org/html/rfc6241)).
*
* @section about-features Main Features
*
* - Creating SSH ([RFC 4742](https://tools.ietf.org/html/rfc4742), [RFC 6242](https://tools.ietf.org/html/rfc6242)),
* using [libssh](https://www.libssh.org/), or TLS ([RFC 7589](https://tools.ietf.org/html/rfc7589)),
* using [OpenSSL](https://www.openssl.org/), authenticated NETCONF sessions.
* - Creating NETCONF sessions with a pre-established transport protocol
* (using this mechanism the communication can be tunneled through sshd(8), for instance).
* - Creating NETCONF Call Home sessions ([RFC 8071](https://tools.ietf.org/html/rfc8071)).
* - Creating, sending, receiving, and replying to RPCs ([RFC 4741](https://tools.ietf.org/html/rfc4741),
* [RFC 6241](https://tools.ietf.org/html/rfc6241)).
* - Creating, sending and receiving NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)),
*
* @section about-license License
*
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
*
* (The BSD 3-Clause License)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*/
/**
* @page howto How To ...
*
* - @subpage howtoinit
* - @subpage howtoclient
* - @subpage howtoserver
* - @subpage howtoclientcomm
* - @subpage howtoservercomm
* - @subpage howtotimeouts
*/
/**
* @page howtoinit Init and Thread-safety Information
*
* Before working with the library, it must be initialized using ::nc_client_init()
* or ::nc_server_init(). Based on how the library was compiled, also _libssh_ and/or
* _libssh_/_libcrypto_ are initialized (for multi-threaded use) too. To prevent
* any reachable memory at the end of your application, there are complementary
* destroy functions (::nc_server_destroy() and ::nc_client_destroy() available. If your
* application is multi-threaded, call the destroy functions in the main thread,
* after all the other threads have ended. In every other thread you should call
* ::nc_thread_destroy() just before it exits.
*
* If _libnetconf2_ is used in accordance with this information, there should
* not be memory leaks of any kind at program exit. For thread-safety details
* of _libssh_, _libssl_, and _libcrypto_, please refer to the corresponding project
* documentation. _libnetconf2_ thread-safety information is below.
*
* Client
* ------
*
* Optionally, a client can specify two alternative ways to get schemas needed when connecting
* with a server. The primary way is to read local files in searchpath (and its subdirectories)
* specified via ::nc_client_set_schema_searchpath(). Alternatively, _libnetconf2_ can use callback
* provided via ::nc_client_set_schema_callback(). If these ways do not succeed and the server
* implements NETCONF \<get-schema\> operation, the schema is retrieved from the server and stored
* localy into the searchpath (if specified) for a future use. If none of these methods succeed to
* load particular schema, the data from this schema are ignored during the communication with the
* server.
*
* Besides the mentioned setters, there are many other @ref howtoclientssh "SSH", @ref howtoclienttls "TLS"
* and @ref howtoclientch "Call Home" getter/setter functions to manipulate with various settings. All these
* settings are internally placed in a thread-specific context so they are independent and
* initialized to the default values within each new thread. However, the context can be shared among
* the threads using ::nc_client_get_thread_context() and ::nc_client_set_thread_context() functions. In such
* a case, be careful and avoid concurrent execution of the mentioned setters/getters and functions
* creating connection (no matter if it is a standard NETCONF connection or Call Home).
*
* In the client, it is thread-safe to work with distinguish NETCONF sessions since the client
* settings are thread-specific as described above.
*
* Server
* ------
*
* Server is __FULLY__ thread-safe meaning you can set all the (thread-shared in contrast to
* client) options simultaneously while listening for or accepting new sessions or
* polling the existing ones. It is even safe to poll one session in several
* pollsession structures or one pollsession structure in several threads. Generally,
* servers can use more threads without any problems as long as they keep their workflow sane
* (behavior such as freeing sessions only after no thread uses them or similar).
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_init()
* - ::nc_client_destroy()
*
* - ::nc_client_set_schema_searchpath()
* - ::nc_client_get_schema_searchpath()
* - ::nc_client_set_schema_callback()
* - ::nc_client_get_schema_callback()
*
* - ::nc_client_set_thread_context()
* - ::nc_client_get_thread_context()
*
* Available in __nc_server.h__.
*
* - ::nc_server_init()
* - ::nc_server_destroy()
*
* Available in both __nc_client.h__ and __nc_server.h__.
*
* - ::nc_thread_destroy()
*/
/**
* @page howtoclient Client sessions
*
* To connect to a NETCONF server, a NETCONF session must be established,
* which requires a working transport session. It is possible to create
* NETCONF sessions with SSH (using _libssh_) or TLS (using _libssl/libcrypto_)
* as the underlying transport protocol. It is also possible to establish
* the transport protocol outside _libnetconf2_ and then provide these file
* descriptors (FD) for full NETCONF session creation.
*
* There are a lot of options for both an SSH and a TLS client. All of them
* have setters and getters so that there is no need to duplicate them in
* a client.
*
* @anchor howtoclientssh
* SSH
* ===
*
* Connecting to a server using SSH does not strictly require to set any
* options, there are sensible default values for all the basic ones.
* Except all the SSH options, optionally some authetication callbacks can be set,
* which are particulary useful in automated clients (passwords cannot be
* asked a user) or simply if any additional information is retrieved some
* other way than from standard terminal input.
*
* Having the default options or changing any unsuitable ones, there are 2 functions
* to use for a new server connection. ::nc_connect_ssh() is the standard function
* that creates sessions using the set options. If there are some options, which
* cannot be changed with the provided API, there is ::nc_connect_libssh() available.
* It requires a _libssh_ session, in which all the SSH options can be modified
* and even the connection established. This allows for full customization and
* should fit any specific situation.
*
* New NETCONF sessions can also be created on existing authenticated SSH sessions.
* There is a new SSH channel needed, on which the NETCONF session is then created.
* Use ::nc_connect_ssh_channel() for this purpose.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_ssh_set_auth_hostkey_check_clb()
* - ::nc_client_ssh_get_auth_hostkey_check_clb()
* - ::nc_client_ssh_set_auth_password_clb()
* - ::nc_client_ssh_get_auth_password_clb()
* - ::nc_client_ssh_set_auth_interactive_clb()
* - ::nc_client_ssh_get_auth_interactive_clb()
* - ::nc_client_ssh_set_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_get_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_add_keypair()
* - ::nc_client_ssh_del_keypair()
* - ::nc_client_ssh_get_keypair_count()
* - ::nc_client_ssh_get_keypair()
* - ::nc_client_ssh_set_auth_pref()
* - ::nc_client_ssh_get_auth_pref()
* - ::nc_client_ssh_set_username()
* - ::nc_client_ssh_get_username()
*
* - ::nc_connect_ssh()
* - ::nc_connect_libssh()
* - ::nc_connect_ssh_channel()
*
* @anchor howtoclienttls
* TLS
* ===
*
* To connect to a server using TLS, there must be some client identification
* options set. Client must specify its certificate with a private key using
* ::nc_client_tls_set_cert_key_paths(). Also, the Certificate Authority of
* a server certificate must be considered trusted. Paths to all the trusted
* CA certificates can be set by ::nc_client_tls_set_trusted_ca_paths().
*
* Then there are again 2 functions for connecting, ::nc_connect_tls() being
* the standard way of connecting. ::nc_connect_libssl() again enables
* to customize the TLS session in every way _libssl_ allows.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_tls_set_cert_key_paths()
* - ::nc_client_tls_get_cert_key_paths()
* - ::nc_client_tls_set_trusted_ca_paths()
* - ::nc_client_tls_get_trusted_ca_paths()
* - ::nc_client_tls_set_crl_paths()
* - ::nc_client_tls_get_crl_paths()
*
* - ::nc_connect_tls()
* - ::nc_connect_libssl()
*
*
* FD and UNIX socket
* ==================
*
* If you authenticated the connection using some tunneling software, you
* can pass its file descriptors to _libnetconf2_ using ::nc_connect_inout(),
* which will continue to establish a full NETCONF session. To connect locally
* on a UNIX socket avoiding all cryptography use ::nc_connect_unix().
*
* Funtions List
* -------------
*
* Available in __nc_client.h__.
*
* - ::nc_connect_inout()
* - ::nc_connect_unix()
*
*
* @anchor howtoclientch
* Call Home
* =========
*
* Call Home needs the same options set as standard SSH or TLS and the functions
* reflect it exactly. However, to accept a connection, the client must first
* specify addresses and ports, which to listen on by ::nc_client_ssh_ch_add_bind_listen()
* and ::nc_client_tls_ch_add_bind_listen(). Then connections can be
* accepted using ::nc_accept_callhome().
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_ssh_ch_set_auth_hostkey_check_clb()
* - ::nc_client_ssh_ch_set_auth_password_clb()
* - ::nc_client_ssh_ch_set_auth_interactive_clb()
* - ::nc_client_ssh_ch_set_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_ch_add_bind_listen()
* - ::nc_client_ssh_ch_del_bind()
* - ::nc_client_ssh_ch_add_keypair()
* - ::nc_client_ssh_ch_del_keypair()
* - ::nc_client_ssh_ch_get_keypair_count()
* - ::nc_client_ssh_ch_get_keypair()
* - ::nc_client_ssh_ch_set_auth_pref()
* - ::nc_client_ssh_ch_get_auth_pref()
* - ::nc_client_ssh_ch_set_username()
* - ::nc_client_ssh_ch_get_username()
*
* - ::nc_client_tls_ch_add_bind_listen()
* - ::nc_client_tls_ch_del_bind()
* - ::nc_client_tls_ch_set_cert_key_paths()
* - ::nc_client_tls_ch_get_cert_key_paths()
* - ::nc_client_tls_ch_set_trusted_ca_paths()
* - ::nc_client_tls_ch_get_trusted_ca_paths()
* - ::nc_client_tls_ch_set_crl_paths()
* - ::nc_client_tls_ch_get_crl_paths()
*
* - ::nc_accept_callhome()
*
*
* Cleanup
* =======
*
* These options and the schema searchpath are stored in dynamically
* allocated memory. They are freed as a part of [destroying the client](@ref howtoinit).
*/
/**
* @page howtoserver Server sessions
*
* Init
* ====
*
* Server takes an argument for its [initialization function](@ref howtoinit).
* In it, you set the server context, which determines what modules it
* supports and what capabilities to advertise. Few capabilities that
* cannot be learnt from the context are set with separate functions
* ::nc_server_set_capab_withdefaults() and generally ::nc_server_set_capability().
* Timeout for receiving the _hello_ message on a new session can be set
* by ::nc_server_set_hello_timeout() and the timeout for disconnecting
* an inactive session by ::nc_server_set_idle_timeout().
*
* Context does not only determine server modules, but its overall
* functionality as well. For every RPC the server should support,
* an nc_rpc_clb callback should be set on that node in the context using ::nc_set_rpc_callback().
* Server then calls these as appropriate [during poll](@ref howtoservercomm).
*
* Just like in the [client](@ref howtoclient), you can let _libnetconf2_
* establish SSH or TLS transport or do it yourself and only provide the file
* descriptors of the connection.
*
* Server options can be only set, there are no getters.
*
* To be able to accept any connections, endpoints must first be added
* with ::nc_server_add_endpt() and configured with ::nc_server_endpt_set_address()
* and ::nc_server_endpt_set_port(). For unix sockets, ::nc_server_endpt_set_perms()
* is available to set the unix socket file permissions, and ::nc_server_endpt_set_port()
* is invalid.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_set_capab_withdefaults()
* - ::nc_server_set_capability()
* - ::nc_server_set_hello_timeout()
* - ::nc_server_set_idle_timeout()
*
* - ::nc_server_add_endpt()
* - ::nc_server_del_endpt()
* - ::nc_server_endpt_set_address()
* - ::nc_server_endpt_set_port()
* - ::nc_server_endpt_set_perms()
*
*
* SSH
* ===
*
* To successfully accept an SSH session you must set at least the host key using
* ::nc_server_ssh_endpt_add_hostkey(), which are ordered. This way you simply add
* some hostkey identifier, but the key itself will be retrieved always when needed
* by calling the callback set by ::nc_server_ssh_set_hostkey_clb().
*
* There are also some other optional settings. Note that authorized
* public keys are set for the server as a whole, not endpoint-specifically.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_ssh_endpt_add_hostkey()
* - ::nc_server_ssh_endpt_del_hostkey()
* - ::nc_server_ssh_endpt_mov_hostkey()
* - ::nc_server_ssh_endpt_mod_hostkey()
* - ::nc_server_ssh_endpt_set_auth_methods()
* - ::nc_server_ssh_endpt_set_auth_attempts()
* - ::nc_server_ssh_endpt_set_auth_timeout()
*
* - ::nc_server_ssh_set_hostkey_clb()
*
* - ::nc_server_ssh_add_authkey()
* - ::nc_server_ssh_add_authkey_path()
* - ::nc_server_ssh_del_authkey()
*
*
* TLS
* ===
*
* TLS works with endpoints too, but its options differ
* significantly from the SSH ones, especially in the _cert-to-name_
* options that TLS uses to derive usernames from client certificates.
* So, after starting listening on an endpoint you need to set the server
* certificate (::nc_server_tls_endpt_set_server_cert()). Its actual content
* together with the matching private key will be loaded using a callback
* from ::nc_server_tls_set_server_cert_clb(). Additional certificates needed
* for the client to verify the server's certificate chain can be loaded using
* a callback from ::nc_server_tls_set_server_cert_chain_clb().
*
* To accept client certificates, they must first be considered trusted,
* which you have three ways of achieving. You can add each of their Certificate Authority
* certificates to the trusted ones or mark a specific client certificate
* as trusted. Lastly, you can set paths with all the trusted CA certificates
* with ::nc_server_tls_endpt_set_trusted_ca_paths(). Adding specific certificates
* is also performed only as an arbitrary identificator and later retrieved from
* callback set by ::nc_server_tls_set_trusted_cert_list_clb(). But, you can add
* certficates as whole lists, not one-by-one.
*
* Then, from each trusted client certificate a username must be derived
* for the NETCONF session. This is accomplished by finding a matching
* _cert-to-name_ entry. They are added using ::nc_server_tls_endpt_add_ctn().
*
* If you need to remove trusted certificates, you can do so with ::nc_server_tls_endpt_del_trusted_cert_list().
* To clear all Certificate Revocation Lists use ::nc_server_tls_endpt_clear_crls().
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_tls_endpt_set_server_cert()
* - ::nc_server_tls_endpt_add_trusted_cert_list()
* - ::nc_server_tls_endpt_del_trusted_cert_list()
* - ::nc_server_tls_endpt_set_trusted_ca_paths()
* - ::nc_server_tls_endpt_set_crl_paths()
* - ::nc_server_tls_endpt_clear_crls()
* - ::nc_server_tls_endpt_add_ctn()
* - ::nc_server_tls_endpt_del_ctn()
* - ::nc_server_tls_endpt_get_ctn()
*
* - ::nc_server_tls_set_server_cert_clb()
* - ::nc_server_tls_set_server_cert_chain_clb()
* - ::nc_server_tls_set_trusted_cert_list_clb()
*
* FD
* ==
*
* If you used a tunneling software, which does its own authentication,
* you can accept a NETCONF session on its file descriptors with
* ::nc_accept_inout().
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_accept_inout()
*
*
* Call Home
* =========
*
* _Call Home_ works with endpoints just like standard sessions, but
* the options are organized a bit differently and endpoints are added
* for CH clients. However, one important difference is that
* once all the mandatory options are set, _libnetconf2_ __will not__
* immediately start connecting to a client. It will do so only after
* calling ::nc_connect_ch_client_dispatch() in a separate thread.
*
* Lastly, monitoring of these sessions is up to the application.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_ch_add_client()
* - ::nc_server_ch_del_client()
* - ::nc_server_ch_is_client()
* - ::nc_server_ch_client_add_endpt()
* - ::nc_server_ch_client_del_endpt()
* - ::nc_server_ch_client_is_endpt()
* - ::nc_server_ch_client_endpt_set_address()
* - ::nc_server_ch_client_endpt_set_port()
* - ::nc_server_ch_client_endpt_enable_keepalives()
* - ::nc_server_ch_client_endpt_set_keepalives()
* - ::nc_server_ch_client_set_conn_type()
* - ::nc_server_ch_client_periodic_set_period()
* - ::nc_server_ch_client_periodic_set_anchor_time()
* - ::nc_server_ch_client_periodic_set_idle_timeout()
* - ::nc_server_ch_client_set_start_with()
* - ::nc_server_ch_client_set_max_attempts()
* - ::nc_connect_ch_client_dispatch()
*
* - ::nc_server_ssh_ch_client_endpt_add_hostkey()
* - ::nc_server_ssh_ch_client_endpt_del_hostkey()
* - ::nc_server_ssh_ch_client_endpt_mov_hostkey()
* - ::nc_server_ssh_ch_client_endpt_set_auth_methods()
* - ::nc_server_ssh_ch_client_endpt_get_auth_methods()
* - ::nc_server_ssh_ch_client_endpt_set_auth_attempts()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
*
* - ::nc_server_tls_ch_client_endpt_set_server_cert()
* - ::nc_server_tls_ch_client_endpt_add_trusted_cert_list()
* - ::nc_server_tls_ch_client_endpt_del_trusted_cert_list()
* - ::nc_server_tls_ch_client_endpt_set_trusted_ca_paths()
* - ::nc_server_tls_ch_client_endpt_set_crl_paths()
* - ::nc_server_tls_ch_client_endpt_clear_crls()
* - ::nc_server_tls_ch_client_endpt_add_ctn()
* - ::nc_server_tls_ch_client_endpt_del_ctn()
* - ::nc_server_tls_ch_client_endpt_get_ctn()
*
*
* Connecting And Cleanup
* ======================
*
* When accepting connections with ::nc_accept(), all the endpoints are examined
* and the first with a pending connection is used. To remove all CH clients,
* endpoints, and free any used dynamic memory, [destroy](@ref howtoinit) the server.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_accept()
*/
/**
* @page howtoclientcomm Client communication
*
* To send RPCs on a session, you simply create an RPC, send it using ::nc_send_rpc(),
* and then wait for a reply using ::nc_recv_reply(). If you are subscribed, there are 2 ways
* of receiving notifications. Either you wait for them the same way
* as for standard replies with ::nc_recv_notif() or you create a dispatcher
* with ::nc_recv_notif_dispatch() that asynchronously (in a separate thread)
* reads notifications and passes them to your callback.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_rpc_act_generic()
* - ::nc_rpc_act_generic_xml()
* - ::nc_rpc_getconfig()
* - ::nc_rpc_edit()
* - ::nc_rpc_copy()
* - ::nc_rpc_delete()
* - ::nc_rpc_lock()
* - ::nc_rpc_unlock()
* - ::nc_rpc_get()
* - ::nc_rpc_kill()
* - ::nc_rpc_commit()
* - ::nc_rpc_discard()
* - ::nc_rpc_cancel()
* - ::nc_rpc_validate()
* - ::nc_rpc_getschema()
* - ::nc_rpc_subscribe()
* - ::nc_rpc_getdata()
* - ::nc_rpc_editdata()
* - ::nc_rpc_establishsub()
* - ::nc_rpc_modifysub()
* - ::nc_rpc_deletesub()
* - ::nc_rpc_killsub()
* - ::nc_rpc_establishpush_periodic()
* - ::nc_rpc_establishpush_onchange()
* - ::nc_rpc_modifypush_periodic()
* - ::nc_rpc_modifypush_onchange()
* - ::nc_rpc_resyncsub()
*
* - ::nc_send_rpc()
* - ::nc_recv_reply()
* - ::nc_recv_notif()
* - ::nc_recv_notif_dispatch()
*/
/**
* @page howtoservercomm Server communication
*
* Once at least one session is established, an nc_pollsession structure
* should be created with ::nc_ps_new(), filled with the session using
* ::nc_ps_add_session() and finally polled with ::nc_ps_poll(). Based on
* the return value from the poll, further actions can be taken. More
* sessions can be polled at the same time and any requests received on
* the sessions are [handled internally](@ref howtoserver).
*
* If an SSH NETCONF session asks for a new channel, you can accept
* this request with ::nc_ps_accept_ssh_channel() or ::nc_session_accept_ssh_channel()
* depending on the structure you want to use as the argument.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_ps_new()
* - ::nc_ps_add_session()
* - ::nc_ps_del_session()
* - ::nc_ps_session_count()
* - ::nc_ps_free()
*
* - ::nc_ps_poll()
* - ::nc_ps_clear()
* - ::nc_ps_accept_ssh_channel()
* - ::nc_session_accept_ssh_channel()
*/
/**
* @page howtotimeouts Timeouts
*
* There are several timeouts which are used throughout _libnetconf2_ to
* assure that it will never indefinitely hang on any operation. Normally,
* you should not need to worry about them much necause they are set by
* default to reasonable values for common systems. However, if your
* platform is not common (embedded, ...), adjusting these timeouts may
* save a lot of debugging and time.
*
* Compile Options
* ---------------
*
* You can adjust active and inactive read timeout using `cmake` variables.
* For details look into `README.md`.
*
* API Functions
* -------------
*
* Once a new connection is established including transport protocol negotiations,
* _hello_ message is exchanged. You can set how long will the server wait for
* receiving this message from a client before dropping it.
*
* Having a NETCONF session working, it may not communicate for a longer time.
* To free up some resources, it is possible to adjust the maximum idle period
* of a session before it is disconnected. In _Call Home_, for both a persistent
* and periodic connection can this idle timeout be specified separately for each
* client using corresponding functions.
*
* Lastly, SSH user authentication timeout can be also modified. It is the time
* a client has to successfully authenticate after connecting before it is disconnected.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_set_hello_timeout()
* - ::nc_server_get_hello_timeout()
* - ::nc_server_set_idle_timeout()
* - ::nc_server_get_idle_timeout()
* - ::nc_server_ch_client_periodic_set_idle_timeout()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
*/
/**
* @defgroup misc Miscellaneous
* @brief Miscellaneous macros, types, structure and functions for a generic use by both server and client applications.
*/
/**
* @defgroup client Client
* @brief NETCONF client functionality.
*/
/**
* @defgroup server Server
* @brief NETCONF server functionality.
*/
#endif /* NC_LIBNETCONF_H_ */

148
src/log.c Normal file
View file

@ -0,0 +1,148 @@
/**
* \file log.c
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 - log functions
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <stdarg.h>
#include <stdio.h>
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
#include <libssh/libssh.h>
#endif
#include "compat.h"
#include "libnetconf.h"
#include "log.h"
/**
* @brief libnetconf verbose level variable
*/
volatile uint8_t verbose_level = 0;
void (*depr_print_clb)(NC_VERB_LEVEL level, const char *msg);
void (*print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg);
API void
nc_verbosity(NC_VERB_LEVEL level)
{
verbose_level = level;
ly_log_level((LY_LOG_LEVEL)level);
}
struct {
NC_VERB_LEVEL level;
const char *label;
} verb[] = {
{NC_VERB_ERROR, "[ERR]"},
{NC_VERB_WARNING, "[WRN]"},
{NC_VERB_VERBOSE, "[INF]"},
{NC_VERB_DEBUG, "[DBG]"},
{NC_VERB_DEBUG_LOWLVL, "[DBL]"}
};
#ifdef NC_ENABLED_SSH
API void
nc_libssh_thread_verbosity(int level)
{
ssh_set_log_level(level);
}
#endif
static void
prv_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args)
{
#define PRV_MSG_INIT_SIZE 256
va_list args2;
char *prv_msg;
void *mem;
int req_len;
prv_msg = malloc(PRV_MSG_INIT_SIZE);
if (!prv_msg) {
return;
}
va_copy(args2, args);
req_len = vsnprintf(prv_msg, PRV_MSG_INIT_SIZE - 1, format, args);
if (req_len == -1) {
goto cleanup;
} else if (req_len >= PRV_MSG_INIT_SIZE - 1) {
/* the length is not enough */
++req_len;
mem = realloc(prv_msg, req_len);
if (!mem) {
goto cleanup;
}
prv_msg = mem;
/* now print the full message */
req_len = vsnprintf(prv_msg, req_len, format, args2);
if (req_len == -1) {
goto cleanup;
}
}
if (print_clb) {
print_clb(session, level, prv_msg);
} else if (depr_print_clb) {
depr_print_clb(level, prv_msg);
} else if (session && session->id) {
fprintf(stderr, "Session %u %s: %s\n", session->id, verb[level].label, prv_msg);
} else {
fprintf(stderr, "%s: %s\n", verb[level].label, prv_msg);
}
cleanup:
free(prv_msg);
#undef PRV_MSG_INIT_SIZE
}
void
prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...)
{
va_list ap;
va_start(ap, format);
prv_vprintf(session, level, format, ap);
va_end(ap);
}
static void
nc_ly_log_clb(LY_LOG_LEVEL lvl, const char *msg, const char *UNUSED(path))
{
if (print_clb) {
print_clb(NULL, (NC_VERB_LEVEL)lvl, msg);
} else if (depr_print_clb) {
depr_print_clb((NC_VERB_LEVEL)lvl, msg);
}
}
API void
nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *))
{
print_clb = NULL;
depr_print_clb = clb;
ly_set_log_clb(nc_ly_log_clb, 1);
}
API void
nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *))
{
print_clb = clb;
depr_print_clb = NULL;
ly_set_log_clb(nc_ly_log_clb, 1);
}

96
src/log.h Normal file
View file

@ -0,0 +1,96 @@
/**
* @file log.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2 logger
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LOG_H_
#define NC_LOG_H_
struct nc_session;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup misc
* @{
*/
/**
* @brief Verbosity levels.
*/
typedef enum NC_VERB_LEVEL {
NC_VERB_ERROR = 0, /**< Print only error messages. */
NC_VERB_WARNING = 1, /**< Print error and warning messages. */
NC_VERB_VERBOSE = 2, /**< Besides errors and warnings, print some other verbose messages. */
NC_VERB_DEBUG = 3, /**< Print almost all messages including some development debug messages. */
NC_VERB_DEBUG_LOWLVL = 4 /**< Print all messages including low level debug messages. */
} NC_VERB_LEVEL;
/**
* @brief Set libnetconf's verbosity level.
*
* This level is set for libnetconf2 and alo libyang that is used internally. libyang
* verbose level can be set explicitly, but must be done so after calling this function.
* However, if debug verbosity is used, selecting displayed libyang debug message groups
* must be done explicitly.
*
* @param[in] level Enabled verbosity level (includes all the levels with higher priority).
*/
void nc_verbosity(NC_VERB_LEVEL level);
#ifdef NC_ENABLED_SSH
/**
* @brief Set libssh verbosity level.
*
* libssh verbosity is set separately because it defines more verbose levels than libnetconf2.
* Also, you need to set this for every thread unlike libnetconf verbosity.
*
* Values:
* - 0 - no logging,
* - 1 - rare conditions or warnings,
* - 2 - API-accessible entrypoints,
* - 3 - packet id and size,
* - 4 - functions entering and leaving.
*
* @param[in] level libssh verbosity level.
*/
void nc_libssh_thread_verbosity(int level);
#endif
/**
* @brief Deprecated, use ::nc_set_print_clb_session() instead.
*
* @param[in] clb Callback that is called for every message.
*/
void nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *));
/**
* @brief Set libnetconf print callback.
*
* This callback is set for libnetconf2 and also libyang that is used internally. libyang
* callback can be set explicitly, but must be done so after calling this function.
*
* @param[in] clb Callback that is called for every message.
*/
void nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *));
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NC_LOG_H_ */

54
src/log_p.h Normal file
View file

@ -0,0 +1,54 @@
/**
* @file log.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2 logger
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LOG_PRIVATE_H_
#define NC_LOG_PRIVATE_H_
#include <stdint.h>
#include "log.h"
/*
* libnetconf's message printing
*/
/**
* @brief Internal printing function
*
* @param[in] session Optional NETCONF session that generated the message
* @param[in] level Verbose level
* @param[in] format Formatting string
*/
void prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...);
/**
* @brief Verbose level variable
*/
extern volatile uint8_t verbose_level;
/*
* Verbose printing macros
*/
#define ERR(session, format, args ...) prv_printf(session,NC_VERB_ERROR,format,##args)
#define WRN(session, format, args ...) if(verbose_level>=NC_VERB_WARNING){prv_printf(session,NC_VERB_WARNING,format,##args);}
#define VRB(session, format, args ...) if(verbose_level>=NC_VERB_VERBOSE){prv_printf(session,NC_VERB_VERBOSE,format,##args);}
#define DBG(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG){prv_printf(session,NC_VERB_DEBUG,format,##args);}
#define DBL(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG_LOWLVL){prv_printf(session,NC_VERB_DEBUG_LOWLVL,format,##args);}
#define ERRMEM ERR(NULL, "%s: memory reallocation failed (%s:%d).", __func__, __FILE__, __LINE__)
#define ERRARG(arg) ERR(NULL, "%s: invalid argument (%s).", __func__, arg)
#define ERRINIT ERR(NULL, "%s: libnetconf2 not initialized.", __func__)
#define ERRINT ERR(NULL, "%s: internal error (%s:%d).", __func__, __FILE__, __LINE__)
#endif /* NC_LOG_PRIVATE_H_ */

1234
src/messages_client.c Normal file

File diff suppressed because it is too large Load diff

555
src/messages_client.h Normal file
View file

@ -0,0 +1,555 @@
/**
* @file messages_client.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's public functions and structures of NETCONF client messages.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_CLIENT_H_
#define NC_MESSAGES_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "netconf.h"
/**
* @defgroup client_msg Client Messages
* @ingroup client
*
* @brief Functions to create NETCONF RPCs (or actions) and process replies received from the server.
* @{
*/
/**
* @brief Enumeration of RPC types
*
* Note that NC_RPC_CLOSE is not defined since sending \<close-session\> is done implicitly by nc_session_free()
*/
typedef enum {
NC_RPC_UNKNOWN = 0, /**< invalid RPC. */
NC_RPC_ACT_GENERIC, /**< user-defined generic RPC/action. */
/* ietf-netconf */
NC_RPC_GETCONFIG, /**< \<get-config\> RPC. */
NC_RPC_EDIT, /**< \<edit-config\> RPC. */
NC_RPC_COPY, /**< \<copy-config\> RPC. */
NC_RPC_DELETE, /**< \<delete-config\> RPC. */
NC_RPC_LOCK, /**< \<lock\> RPC. */
NC_RPC_UNLOCK, /**< \<unlock\> RPC. */
NC_RPC_GET, /**< \<get\> RPC. */
NC_RPC_KILL, /**< \<kill-session\> RPC. */
NC_RPC_COMMIT, /**< \<commit\> RPC. */
NC_RPC_DISCARD, /**< \<discard-changes\> RPC. */
NC_RPC_CANCEL, /**< \<cancel-commit\> RPC. */
NC_RPC_VALIDATE, /**< \<validate\> RPC. */
/* ietf-netconf-monitoring */
NC_RPC_GETSCHEMA, /**< \<get-schema\> RPC. */
/* notifications */
NC_RPC_SUBSCRIBE, /**< \<create-subscription\> RPC. */
/* ietf-netconf-nmda */
NC_RPC_GETDATA, /**< \<get-data\> RPC. */
NC_RPC_EDITDATA, /**< \<edit-data\> RPC. */
/* ietf-subscribed-notifications */
NC_RPC_ESTABLISHSUB, /**< \<establish-subscription\> RPC. */
NC_RPC_MODIFYSUB, /**< \<modify-subscription\> RPC. */
NC_RPC_DELETESUB, /**< \<delete-subscription\> RPC. */
NC_RPC_KILLSUB, /**< \<kill-subscription\> RPC. */
/* ietf-yang-push */
NC_RPC_ESTABLISHPUSH, /**< \<establish-subscription\> RPC with augments. */
NC_RPC_MODIFYPUSH, /**< \<modify-subscription\> RPC with augments. */
NC_RPC_RESYNCSUB /**< \<resync-subscription\> RPC. */
} NC_RPC_TYPE;
/**
* @brief Enumeration of \<edit-config\> default operation
*/
typedef enum {
NC_RPC_EDIT_DFLTOP_UNKNOWN = 0, /**< unknown default operation */
NC_RPC_EDIT_DFLTOP_MERGE, /**< default operation merge */
NC_RPC_EDIT_DFLTOP_REPLACE, /**< default operation replace */
NC_RPC_EDIT_DFLTOP_NONE /**< default operation none */
} NC_RPC_EDIT_DFLTOP;
/**
* @brief Enumeration of \<edit-config\> test option
*/
typedef enum {
NC_RPC_EDIT_TESTOPT_UNKNOWN = 0, /**< unknown test option */
NC_RPC_EDIT_TESTOPT_TESTSET, /**< test-then-set option */
NC_RPC_EDIT_TESTOPT_SET, /**< set option */
NC_RPC_EDIT_TESTOPT_TEST /**< test-only option */
} NC_RPC_EDIT_TESTOPT;
/**
* @brief Enumeration of \<edit-config\> error option
*/
typedef enum {
NC_RPC_EDIT_ERROPT_UNKNOWN = 0, /**< unknown error option */
NC_RPC_EDIT_ERROPT_STOP, /**< stop-on-error option */
NC_RPC_EDIT_ERROPT_CONTINUE, /**< continue-on-error option */
NC_RPC_EDIT_ERROPT_ROLLBACK /**< rollback-on-error option */
} NC_RPC_EDIT_ERROPT;
/**
* @brief NETCONF error structure representation
*/
struct nc_err {
/** @brief \<error-type\>, error layer where the error occurred. */
const char *type;
/** @brief \<error-tag\>. */
const char *tag;
/** @brief \<error-severity\>. */
const char *severity;
/** @brief \<error-app-tag\>, the data-model-specific or implementation-specific error condition, if one exists. */
const char *apptag;
/** @brief \<error-path\>, XPATH expression identifying the element with the error. */
const char *path;
/** @brief \<error-message\>, Human-readable description of the error. */
const char *message;
/** @brief xml:lang attribute of the error-message. */
const char *message_lang;
/* <error-info> */
/** @brief \<session-id\>, session ID of the session holding the requested lock. Part of \<error-info\>. */
const char *sid;
/** @brief \<bad-attr\>, array of the names of the data-model-specific XML attributes that caused the error. Part of \<error-info\>. */
const char **attr;
/** @brief \<bad-element\>, array of the names of the data-model-specific XML element that caused the error. Part of \<error-info\>. */
const char **elem;
/** @brief \<bad-namespace\>, array of the unexpected XML namespaces that caused the error. Part of \<error-info\>. */
const char **ns;
/** @brief List of the remaining non-standard opaque nodes. */
struct lyd_node *other;
/** @brief Number of items in the attr array */
uint16_t attr_count;
/** @brief Number of items in the elem array */
uint16_t elem_count;
/** @brief Number of items in the ns array */
uint16_t ns_count;
/** @brief Number of items in the other array */
uint16_t other_count;
};
/**
* @struct nc_rpc
* @brief NETCONF client RPC object
*
* Note that any stored parameters are not checked for validity because it is performed later,
* while sending the RPC via a specific NETCONF session (::nc_send_rpc()) since the NETCONF
* capabilities of the session are needed for such a check. An RPC object can be sent via any
* NETCONF session which supports all the needed NETCONF capabilities for the RPC.
*/
struct nc_rpc;
/**
* @brief Get the type of the RPC
*
* @param[in] rpc RPC to check the type of.
* @return Type of @p rpc.
*/
NC_RPC_TYPE nc_rpc_get_type(const struct nc_rpc *rpc);
/**
* @brief Create a generic NETCONF RPC or action
*
* Note that created object can be sent via any NETCONF session that shares the context
* of the @p data.
*
* @note In case of action, the \<action\> element is added automatically and should not be in @p data.
*
* @param[in] data NETCONF RPC data as a data tree.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype);
/**
* @brief Create a generic NETCONF RPC or action from an XML string
*
* For details, see ::nc_rpc.
*
* @note In case of action, the \<action\> element is added automatically and should not be in @p xml_str.
*
* @param[in] xml_str NETCONF RPC data as an XML string.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] source Source datastore being queried.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<edit-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore being edited.
* @param[in] default_op Optional default operation.
* @param[in] test_opt Optional test option.
* @param[in] error_opt Optional error option.
* @param[in] edit_content Config or URL where the config to perform is to be found.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
NC_RPC_EDIT_ERROPT error_opt, const char *edit_content, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<copy-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore.
* @param[in] url_trg Used instead @p target if the target is an URL.
* @param[in] source Source datastore.
* @param[in] url_or_config_src Used instead @p source if the source is an URL or a config.
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source,
const char *url_or_config_src, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<delete-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore to delete.
* @param[in] url Used instead @p target if the target is an URL.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_delete(NC_DATASTORE target, const char *url, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<lock\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore of the operation.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_lock(NC_DATASTORE target);
/**
* @brief Create NETCONF RPC \<unlock\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore of the operation.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_unlock(NC_DATASTORE target);
/**
* @brief Create NETCONF RPC \<get\>
*
* For details, see ::nc_rpc.
*
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_get(const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<kill-session\>
*
* For details, see ::nc_rpc.
*
* @param[in] session_id Session ID of the session to kill.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_kill(uint32_t session_id);
/**
* @brief Create NETCONF RPC \<commit\>
*
* For details, see ::nc_rpc.
*
* @param[in] confirmed Whether the commit is to be confirmed.
* @param[in] confirm_timeout Optional confirm timeout.
* @param[in] persist Optional identification string of a new persistent confirmed commit.
* @param[in] persist_id Optional identification string of a persistent confirmed commit to be commited.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<discard-changes\>
*
* For details, see ::nc_rpc.
*
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_discard(void);
/**
* @brief Create NETCONF RPC \<cancel-commit\>
*
* For details, see ::nc_rpc.
*
* @param[in] persist_id Optional identification string of a persistent confirmed commit.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_cancel(const char *persist_id, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<validate\>
*
* For details, see ::nc_rpc.
*
* @param[in] source Source datastore being validated.
* @param[in] url_or_config Used instead @p source if the source is an URL or a config.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-schema\>
*
* For details, see ::nc_rpc.
*
* @param[in] identifier Requested model identifier.
* @param[in] version Optional model version, either YANG version (1.0/1.1) or revision date.
* @param[in] format Optional format of the model (default is YANG).
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getschema(const char *identifier, const char *version, const char *format, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<create-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] stream_name Optional name of a NETCONF stream to subscribe to.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] start_time Optional YANG datetime identifying the start of the subscription.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_time,
const char *stop_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-data\>
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] config_filter Optional config filter, "true" for config-only data, "false" for state-only data.
* @param[in] origin_filter Optional origin filter array, selects only nodes of this or derived origin.
* @param[in] origin_filter_count Count of filters is @p origin_filter.
* @param[in] neg_origin_filter Whether origin filters are negated or not.
* @param[in] max_depth Maximum depth of returned subtrees, 0 for unlimited.
* @param[in] with_origin Whether return data origin.
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getdata(const char *datastore, const char *filter, const char *config_filter, char **origin_filter,
int origin_filter_count, int neg_origin_filter, uint16_t max_depth, int with_origin, NC_WD_MODE wd_mode,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-data\>
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] default_op Optional default operation.
* @param[in] edit_content Config or URL where the config to perform is to be found.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char *edit_content,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<establish-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stream_name Name of a NETCONF stream to subscribe to.
* @param[in] start_time Optional YANG datetime identifying the start of the subscription.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishsub(const char *filter, const char *stream_name, const char *start_time,
const char *stop_time, const char *encoding, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] filter Optional new filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional new YANG datetime identifying the end of the subscription.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<delete-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to delete.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_deletesub(uint32_t id);
/**
* @brief Create NETCONF RPC \<kill-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to kill.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_killsub(uint32_t id);
/**
* @brief Create NETCONF RPC \<establish-subscription\> with augments from ietf-yang-push for a periodic subscription
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] period Subscription period in centiseconds (0.01s).
* @param[in] anchor_time Optional anchor datetime for the period.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const char *stop_time,
const char *encoding, uint32_t period, const char *anchor_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<establish-subscription\> with augments from ietf-yang-push for an on-change subscription
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] dampening_period Optional dampening period of the notifications.
* @param[in] sync_on_start Whether to send a full push-update notification on subscription start.
* @param[in] excluded_change Optional NULL-terminated array of excluded changes.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const char *stop_time,
const char *encoding, uint32_t dampening_period, int sync_on_start, const char **excluded_change,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\> with augments from ietf-yang-push for a periodic subscription
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] period Subscription period in centiseconds (0.01s).
* @param[in] anchor_time Optional anchor datetime for the period.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filter, const char *stop_time,
uint32_t period, const char *anchor_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\> with augments from ietf-yang-push for an on-change subscription
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] dampening_period Optional dampening period of the notifications.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filter, const char *stop_time,
uint32_t dampening_period, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<resync-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to resync.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_resyncsub(uint32_t id);
/**
* @brief Free the NETCONF RPC object.
*
* @param[in] rpc Object to free.
*/
void nc_rpc_free(struct nc_rpc *rpc);
/** @} Client Messages */
#ifdef __cplusplus
}
#endif
#endif /* NC_MESSAGES_CLIENT_H_ */

263
src/messages_p.h Normal file
View file

@ -0,0 +1,263 @@
/**
* @file messages_p.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2's private functions and structures of NETCONF messages.
*
* Copyright (c) 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_P_H_
#define NC_MESSAGES_P_H_
#include <libyang/libyang.h>
#include "messages_client.h"
#include "messages_server.h"
extern const char *rpcedit_dfltop2str[];
extern const char *rpcedit_testopt2str[];
extern const char *rpcedit_erropt2str[];
struct nc_server_reply {
NC_RPL type;
};
struct nc_server_reply_data {
NC_RPL type;
struct lyd_node *data;
int free;
NC_WD_MODE wd;
};
struct nc_server_reply_error {
NC_RPL type;
struct lyd_node *err;
};
struct nc_server_rpc {
struct lyd_node *envp; /**< NETCONF-specific RPC envelopes */
struct lyd_node *rpc; /**< RPC data tree */
};
struct nc_server_notif {
char *eventtime; /**< eventTime of the notification */
struct lyd_node *ntf; /**< notification data tree of the message */
int free;
};
struct nc_client_reply_error {
NC_RPL type;
struct nc_err *err;
uint32_t count;
struct ly_ctx *ctx;
};
struct nc_rpc {
NC_RPC_TYPE type;
};
struct nc_rpc_act_generic {
NC_RPC_TYPE type; /**< NC_RPC_ACT_GENERIC */
int has_data; /**< 1 for content.data, 0 for content.xml_str */
union {
struct lyd_node *data; /**< parsed RPC data */
char *xml_str; /**< raw XML string */
} content;
char free;
};
struct nc_rpc_getconfig {
NC_RPC_TYPE type; /**< NC_RPC_GETCONFIG */
NC_DATASTORE source; /**< NETCONF datastore being queried */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_edit {
NC_RPC_TYPE type; /**< NC_RPC_EDIT */
NC_DATASTORE target;
NC_RPC_EDIT_DFLTOP default_op;
NC_RPC_EDIT_TESTOPT test_opt;
NC_RPC_EDIT_ERROPT error_opt;
char *edit_cont; /**< either URL (starts with aplha) or config (starts with '<') */
char free;
};
struct nc_rpc_copy {
NC_RPC_TYPE type; /**< NC_RPC_COPY */
NC_DATASTORE target;
char *url_trg;
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with aplha) or config (starts with '<') */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_delete {
NC_RPC_TYPE type; /**< NC_RPC_DELETE */
NC_DATASTORE target;
char *url;
char free;
};
struct nc_rpc_lock {
NC_RPC_TYPE type; /**< NC_RPC_LOCK or NC_RPC_UNLOCK */
NC_DATASTORE target;
};
struct nc_rpc_get {
NC_RPC_TYPE type; /**< NC_RPC_GET */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_kill {
NC_RPC_TYPE type; /**< NC_RPC_KILL */
uint32_t sid;
};
struct nc_rpc_commit {
NC_RPC_TYPE type; /**< NC_RPC_COMMIT */
int confirmed;
uint32_t confirm_timeout;
char *persist;
char *persist_id;
char free;
};
struct nc_rpc_cancel {
NC_RPC_TYPE type; /**< NC_RPC_CANCEL */
char *persist_id;
char free;
};
struct nc_rpc_validate {
NC_RPC_TYPE type; /**< NC_RPC_VALIDATE */
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with alpha) or config (starts with '<') */
char free;
};
struct nc_rpc_getschema {
NC_RPC_TYPE type; /**< NC_RPC_GETSCHEMA */
char *identifier; /**< requested model identifier */
char *version; /**< either YANG version (1.0/1.1) or revision date */
char *format; /**< model format */
char free;
};
struct nc_rpc_subscribe {
NC_RPC_TYPE type; /**< NC_RPC_SUBSCRIBE */
char *stream; /**< stream name */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
char *start;
char *stop;
char free;
};
struct nc_rpc_getdata {
NC_RPC_TYPE type; /**< NC_RPC_GETDATA */
char *datastore; /**< target datastore identity */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
char *config_filter; /**< config filter ("true"/"false") */
char **origin_filter; /**< origin filters */
int origin_filter_count; /**< origin filter count */
int negated_origin_filter; /**< whether origin filter is negated or not */
int max_depth; /**< max depth of returned subtrees, 0 for unlimited */
int with_origin; /**< whether to return origin of data */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_editdata {
NC_RPC_TYPE type; /**< NC_RPC_EDITDATA */
char *datastore; /**< target datastore identity */
NC_RPC_EDIT_DFLTOP default_op;
char *edit_cont; /**< either URL (starts with aplha) or config (starts with '<') */
char free;
};
struct nc_rpc_establishsub {
NC_RPC_TYPE type; /**< NC_RPC_ESTABLISHSUB */
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stream; /**< stream name */
char *start;
char *stop;
char *encoding;
char free;
};
struct nc_rpc_modifysub {
NC_RPC_TYPE type; /**< NC_RPC_MODIFYSUB */
uint32_t id;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
char free;
};
struct nc_rpc_deletesub {
NC_RPC_TYPE type; /**< NC_RPC_DELETESUB */
uint32_t id;
};
struct nc_rpc_killsub {
NC_RPC_TYPE type; /**< NC_RPC_KILLSUB */
uint32_t id;
};
struct nc_rpc_establishpush {
NC_RPC_TYPE type; /**< NC_RPC_ESTABLISHPUSH */
char *datastore;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
char *encoding;
int periodic;
union {
struct {
uint32_t period;
char *anchor_time;
};
struct {
uint32_t dampening_period;
int sync_on_start;
char **excluded_change;
};
};
char free;
};
struct nc_rpc_modifypush {
NC_RPC_TYPE type; /**< NC_RPC_MODIFYPUSH */
uint32_t id;
char *datastore;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
int periodic;
union {
struct {
uint32_t period;
char *anchor_time;
};
uint32_t dampening_period;
};
char free;
};
struct nc_rpc_resyncsub {
NC_RPC_TYPE type; /**< NC_RPC_RESYNCSUB */
uint32_t id;
};
void nc_server_rpc_free(struct nc_server_rpc *rpc);
void nc_client_err_clean(struct nc_err *err, struct ly_ctx *ctx);
#endif /* NC_MESSAGES_P_H_ */

901
src/messages_server.c Normal file
View file

@ -0,0 +1,901 @@
/**
* \file messages_server.c
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 - server NETCONF messages functions
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <ctype.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <libyang/libyang.h>
#include "compat.h"
#include "libnetconf.h"
#include "session_server.h"
extern struct nc_server_opts server_opts;
API struct nc_server_reply *
nc_server_reply_ok(void)
{
struct nc_server_reply *ret;
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_OK;
return ret;
}
API struct nc_server_reply *
nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtype)
{
struct nc_server_reply_data *ret;
if (!data) {
ERRARG("data");
return NULL;
}
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_DATA;
ret->wd = wd;
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
if (lyd_dup_single(data, NULL, LYD_DUP_RECURSIVE, &ret->data)) {
free(ret);
return NULL;
}
} else {
ret->data = data;
}
if (paramtype != NC_PARAMTYPE_CONST) {
ret->free = 1;
} else {
ret->free = 0;
}
return (struct nc_server_reply *)ret;
}
API struct nc_server_reply *
nc_server_reply_err(struct lyd_node *err)
{
struct nc_server_reply_error *ret;
if (!err) {
ERRARG("err");
return NULL;
}
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_ERROR;
ret->err = err;
return (struct nc_server_reply *)ret;
}
API int
nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err)
{
struct nc_server_reply_error *err_rpl;
if (!reply || (reply->type != NC_RPL_ERROR)) {
ERRARG("reply");
return -1;
} else if (!err) {
ERRARG("err");
return -1;
}
err_rpl = (struct nc_server_reply_error *)reply;
lyd_insert_sibling(err_rpl->err, err, &err_rpl->err);
return 0;
}
API const struct lyd_node *
nc_server_reply_get_last_err(const struct nc_server_reply *reply)
{
struct nc_server_reply_error *err_rpl;
if (!reply || (reply->type != NC_RPL_ERROR)) {
ERRARG("reply");
return NULL;
}
err_rpl = (struct nc_server_reply_error *)reply;
if (!err_rpl->err) {
return NULL;
}
return err_rpl->err->prev;
}
static const char *
nc_err_tag2str(NC_ERR tag)
{
switch (tag) {
case NC_ERR_IN_USE:
return "in-use";
case NC_ERR_INVALID_VALUE:
return "invalid-value";
case NC_ERR_ACCESS_DENIED:
return "access-denied";
case NC_ERR_ROLLBACK_FAILED:
return "rollback-failed";
case NC_ERR_OP_NOT_SUPPORTED:
return "operation-not-supported";
case NC_ERR_TOO_BIG:
return "too-big";
case NC_ERR_RES_DENIED:
return "resource-denied";
case NC_ERR_MISSING_ATTR:
return "missing-attribute";
case NC_ERR_BAD_ATTR:
return "bad-attribute";
case NC_ERR_UNKNOWN_ATTR:
return "unknown-attribute";
case NC_ERR_MISSING_ELEM:
return "missing-element";
case NC_ERR_BAD_ELEM:
return "bad-element";
case NC_ERR_UNKNOWN_ELEM:
return "unknown-element";
case NC_ERR_UNKNOWN_NS:
return "unknown-namespace";
case NC_ERR_LOCK_DENIED:
return "lock-denied";
case NC_ERR_DATA_EXISTS:
return "data-exists";
case NC_ERR_DATA_MISSING:
return "data-missing";
case NC_ERR_OP_FAILED:
return "operation-failed";
case NC_ERR_MALFORMED_MSG:
return "malformed-message";
default:
break;
}
return NULL;
}
static NC_ERR
nc_err_str2tag(const char *str)
{
if (!strcmp(str, "in-use")) {
return NC_ERR_IN_USE;
} else if (!strcmp(str, "invalid-value")) {
return NC_ERR_INVALID_VALUE;
} else if (!strcmp(str, "access-denied")) {
return NC_ERR_ACCESS_DENIED;
} else if (!strcmp(str, "rollback-failed")) {
return NC_ERR_ROLLBACK_FAILED;
} else if (!strcmp(str, "operation-not-supported")) {
return NC_ERR_OP_NOT_SUPPORTED;
} else if (!strcmp(str, "too-big")) {
return NC_ERR_TOO_BIG;
} else if (!strcmp(str, "resource-denied")) {
return NC_ERR_RES_DENIED;
} else if (!strcmp(str, "missing-attribute")) {
return NC_ERR_MISSING_ATTR;
} else if (!strcmp(str, "bad-attribute")) {
return NC_ERR_BAD_ATTR;
} else if (!strcmp(str, "unknown-attribute")) {
return NC_ERR_UNKNOWN_ATTR;
} else if (!strcmp(str, "missing-element")) {
return NC_ERR_MISSING_ELEM;
} else if (!strcmp(str, "bad-element")) {
return NC_ERR_BAD_ELEM;
} else if (!strcmp(str, "unknown-element")) {
return NC_ERR_UNKNOWN_ELEM;
} else if (!strcmp(str, "unknown-namespace")) {
return NC_ERR_UNKNOWN_NS;
} else if (!strcmp(str, "lock-denied")) {
return NC_ERR_LOCK_DENIED;
} else if (!strcmp(str, "data-exists")) {
return NC_ERR_DATA_EXISTS;
} else if (!strcmp(str, "data-missing")) {
return NC_ERR_DATA_MISSING;
} else if (!strcmp(str, "operation-failed")) {
return NC_ERR_OP_FAILED;
} else if (!strcmp(str, "malformed-message")) {
return NC_ERR_MALFORMED_MSG;
}
return 0;
}
static const char *
nc_err_type2str(NC_ERR_TYPE type)
{
switch (type) {
case NC_ERR_TYPE_TRAN:
return "transport";
case NC_ERR_TYPE_RPC:
return "rpc";
case NC_ERR_TYPE_PROT:
return "protocol";
case NC_ERR_TYPE_APP:
return "application";
default:
break;
}
return NULL;
}
static NC_ERR_TYPE
nc_err_str2type(const char *str)
{
if (!strcmp(str, "transport")) {
return NC_ERR_TYPE_TRAN;
} else if (!strcmp(str, "rpc")) {
return NC_ERR_TYPE_RPC;
} else if (!strcmp(str, "protocol")) {
return NC_ERR_TYPE_PROT;
} else if (!strcmp(str, "application")) {
return NC_ERR_TYPE_APP;
}
return 0;
}
API struct lyd_node *
nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
{
va_list ap;
struct lyd_node *err = NULL;
NC_ERR_TYPE type;
const char *arg1, *arg2;
uint32_t sid;
if (!tag) {
ERRARG("tag");
return NULL;
}
/* rpc-error */
if (lyd_new_opaq2(NULL, ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &err)) {
return NULL;
}
va_start(ap, tag);
/* error-type */
switch (tag) {
case NC_ERR_IN_USE:
case NC_ERR_INVALID_VALUE:
case NC_ERR_ACCESS_DENIED:
case NC_ERR_ROLLBACK_FAILED:
case NC_ERR_OP_NOT_SUPPORTED:
type = (NC_ERR_TYPE)va_arg(ap, int); /* NC_ERR_TYPE enum is automatically promoted to int */
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_TOO_BIG:
case NC_ERR_RES_DENIED:
type = (NC_ERR_TYPE)va_arg(ap, int);
break;
case NC_ERR_MISSING_ATTR:
case NC_ERR_BAD_ATTR:
case NC_ERR_UNKNOWN_ATTR:
type = (NC_ERR_TYPE)va_arg(ap, int);
if (type == NC_ERR_TYPE_TRAN) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_MISSING_ELEM:
case NC_ERR_BAD_ELEM:
case NC_ERR_UNKNOWN_ELEM:
type = (NC_ERR_TYPE)va_arg(ap, int);
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_UNKNOWN_NS:
type = (NC_ERR_TYPE)va_arg(ap, int);
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_LOCK_DENIED:
type = NC_ERR_TYPE_PROT;
break;
case NC_ERR_DATA_EXISTS:
case NC_ERR_DATA_MISSING:
type = NC_ERR_TYPE_APP;
break;
case NC_ERR_OP_FAILED:
type = (NC_ERR_TYPE)va_arg(ap, int);
if (type == NC_ERR_TYPE_TRAN) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_MALFORMED_MSG:
type = NC_ERR_TYPE_RPC;
break;
default:
ERRARG("tag");
goto fail;
}
if (lyd_new_opaq2(err, NULL, "error-type", nc_err_type2str(type), NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-tag */
if (lyd_new_opaq2(err, NULL, "error-tag", nc_err_tag2str(tag), NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-severity */
if (lyd_new_opaq2(err, NULL, "error-severity", "error", NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-message */
switch (tag) {
case NC_ERR_IN_USE:
nc_err_set_msg(err, "The request requires a resource that already is in use.", "en");
break;
case NC_ERR_INVALID_VALUE:
nc_err_set_msg(err, "The request specifies an unacceptable value for one or more parameters.", "en");
break;
case NC_ERR_TOO_BIG:
nc_err_set_msg(err, "The request or response (that would be generated) is too large for the implementation to handle.", "en");
break;
case NC_ERR_MISSING_ATTR:
nc_err_set_msg(err, "An expected attribute is missing.", "en");
break;
case NC_ERR_BAD_ATTR:
nc_err_set_msg(err, "An attribute value is not correct.", "en");
break;
case NC_ERR_UNKNOWN_ATTR:
nc_err_set_msg(err, "An unexpected attribute is present.", "en");
break;
case NC_ERR_MISSING_ELEM:
nc_err_set_msg(err, "An expected element is missing.", "en");
break;
case NC_ERR_BAD_ELEM:
nc_err_set_msg(err, "An element value is not correct.", "en");
break;
case NC_ERR_UNKNOWN_ELEM:
nc_err_set_msg(err, "An unexpected element is present.", "en");
break;
case NC_ERR_UNKNOWN_NS:
nc_err_set_msg(err, "An unexpected namespace is present.", "en");
break;
case NC_ERR_ACCESS_DENIED:
nc_err_set_msg(err, "Access to the requested protocol operation or data model is denied because authorization failed.", "en");
break;
case NC_ERR_LOCK_DENIED:
nc_err_set_msg(err, "Access to the requested lock is denied because the lock is currently held by another entity.", "en");
break;
case NC_ERR_RES_DENIED:
nc_err_set_msg(err, "Request could not be completed because of insufficient resources.", "en");
break;
case NC_ERR_ROLLBACK_FAILED:
nc_err_set_msg(err, "Request to roll back some configuration change was not completed for some reason.", "en");
break;
case NC_ERR_DATA_EXISTS:
nc_err_set_msg(err, "Request could not be completed because the relevant data model content already exists.", "en");
break;
case NC_ERR_DATA_MISSING:
nc_err_set_msg(err, "Request could not be completed because the relevant data model content does not exist.", "en");
break;
case NC_ERR_OP_NOT_SUPPORTED:
nc_err_set_msg(err, "Request could not be completed because the requested operation is not supported by this implementation.", "en");
break;
case NC_ERR_OP_FAILED:
nc_err_set_msg(err, "Request could not be completed because the requested operation failed for a non-specific reason.", "en");
break;
case NC_ERR_MALFORMED_MSG:
nc_err_set_msg(err, "A message could not be handled because it failed to be parsed correctly.", "en");
break;
default:
ERRARG("tag");
goto fail;
}
/* error-info */
switch (tag) {
case NC_ERR_IN_USE:
case NC_ERR_INVALID_VALUE:
case NC_ERR_ACCESS_DENIED:
case NC_ERR_ROLLBACK_FAILED:
case NC_ERR_OP_NOT_SUPPORTED:
case NC_ERR_TOO_BIG:
case NC_ERR_RES_DENIED:
case NC_ERR_DATA_EXISTS:
case NC_ERR_DATA_MISSING:
case NC_ERR_OP_FAILED:
case NC_ERR_MALFORMED_MSG:
break;
case NC_ERR_MISSING_ATTR:
case NC_ERR_BAD_ATTR:
case NC_ERR_UNKNOWN_ATTR:
arg1 = va_arg(ap, const char *);
arg2 = va_arg(ap, const char *);
nc_err_add_bad_attr(err, arg1);
nc_err_add_bad_elem(err, arg2);
break;
case NC_ERR_MISSING_ELEM:
case NC_ERR_BAD_ELEM:
case NC_ERR_UNKNOWN_ELEM:
arg1 = va_arg(ap, const char *);
nc_err_add_bad_elem(err, arg1);
break;
case NC_ERR_UNKNOWN_NS:
arg1 = va_arg(ap, const char *);
arg2 = va_arg(ap, const char *);
nc_err_add_bad_elem(err, arg1);
nc_err_add_bad_ns(err, arg2);
break;
case NC_ERR_LOCK_DENIED:
sid = va_arg(ap, uint32_t);
nc_err_set_sid(err, sid);
break;
default:
ERRARG("tag");
goto fail;
}
va_end(ap);
return err;
fail:
va_end(ap);
lyd_free_siblings(err);
return NULL;
}
API NC_ERR_TYPE
nc_err_get_type(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &match);
if (match) {
return nc_err_str2type(((struct lyd_node_opaq *)match)->value);
}
return 0;
}
API NC_ERR
nc_err_get_tag(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &match);
if (match) {
return nc_err_str2tag(((struct lyd_node_opaq *)match)->value);
}
return 0;
}
API int
nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_app_tag) {
ERRARG("error_app_tag");
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-app-tag", error_app_tag, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API const char *
nc_err_get_app_tag(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return NULL;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_path(struct lyd_node *err, const char *error_path)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_path) {
ERRARG("error_path");
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-path", error_path, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API const char *
nc_err_get_path(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang)
{
struct lyd_node *match;
struct lyd_attr *attr;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_message) {
ERRARG("error_message");
return -1;
}
/* remove previous message */
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-message", error_message, NULL, NC_NS_BASE, &match)) {
return -1;
}
if (lang && lyd_new_attr(match, NULL, "xml:lang", lang, &attr)) {
lyd_free_tree(match);
return -1;
}
return 0;
}
API const char *
nc_err_get_msg(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return NULL;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_sid(struct lyd_node *err, uint32_t session_id)
{
struct lyd_node *match, *info;
char buf[22];
if (!err) {
ERRARG("err");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(info), "session-id", &match);
if (match) {
lyd_free_tree(match);
}
sprintf(buf, "%" PRIu32, session_id);
if (lyd_new_opaq2(info, NULL, "session-id", buf, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!attr_name) {
ERRARG("attr_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-attribute", attr_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!elem_name) {
ERRARG("elem_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-element", elem_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!ns_name) {
ERRARG("ns_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-namespace", ns_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!other) {
ERRARG("other");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
lyd_insert_child(info, other);
return 0;
}
void
nc_server_rpc_free(struct nc_server_rpc *rpc)
{
if (!rpc) {
return;
}
lyd_free_tree(rpc->envp);
/* may be action */
lyd_free_all(rpc->rpc);
free(rpc);
}
API void
nc_server_reply_free(struct nc_server_reply *reply)
{
struct nc_server_reply_data *data_rpl;
struct nc_server_reply_error *error_rpl;
if (!reply) {
return;
}
switch (reply->type) {
case NC_RPL_DATA:
data_rpl = (struct nc_server_reply_data *)reply;
if (data_rpl->free) {
lyd_free_siblings(data_rpl->data);
}
break;
case NC_RPL_OK:
/* nothing to free */
break;
case NC_RPL_ERROR:
error_rpl = (struct nc_server_reply_error *)reply;
lyd_free_siblings(error_rpl->err);
break;
default:
break;
}
free(reply);
}
API struct nc_server_notif *
nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype)
{
struct nc_server_notif *ntf;
struct lyd_node *elem;
int found;
if (!event) {
ERRARG("event");
return NULL;
} else if (!eventtime) {
ERRARG("eventtime");
return NULL;
}
/* check that there is a notification */
found = 0;
LYD_TREE_DFS_BEGIN(event, elem) {
if (elem->schema->nodetype == LYS_NOTIF) {
found = 1;
break;
}
LYD_TREE_DFS_END(event, elem);
}
if (!found) {
ERRARG("event");
return NULL;
}
ntf = malloc(sizeof *ntf);
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
ntf->eventtime = strdup(eventtime);
if (lyd_dup_single(event, NULL, LYD_DUP_RECURSIVE, &ntf->ntf)) {
free(ntf);
return NULL;
}
} else {
ntf->eventtime = eventtime;
ntf->ntf = event;
}
ntf->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
return ntf;
}
API void
nc_server_notif_free(struct nc_server_notif *notif)
{
if (!notif) {
return;
}
if (notif->free) {
lyd_free_tree(notif->ntf);
free(notif->eventtime);
}
free(notif);
}
API const char *
nc_server_notif_get_time(const struct nc_server_notif *notif)
{
if (!notif) {
ERRARG("notif");
return NULL;
}
return notif->eventtime;
}

340
src/messages_server.h Normal file
View file

@ -0,0 +1,340 @@
/**
* @file messages_server.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's functions and structures of server NETCONF messages.
*
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_SERVER_H_
#define NC_MESSAGES_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#include "netconf.h"
#include "session.h"
/**
* @defgroup server_msg Server Messages
* @ingroup server
*
* @brief Functions to create NETCONF Event notifications and replies to the NETCONF RPCs (or actions).
* @{
*/
/**
* @brief Enumeration of NETCONF errors
*/
typedef enum NC_ERROR {
NC_ERR_UNKNOWN = 0, /**< unknown error */
NC_ERR_IN_USE, /**< in-use error */
NC_ERR_INVALID_VALUE, /**< invalid-value error */
NC_ERR_TOO_BIG, /**< too-big error */
NC_ERR_MISSING_ATTR, /**< missing-attribute error */
NC_ERR_BAD_ATTR, /**< bad-attribute error */
NC_ERR_UNKNOWN_ATTR, /**< unknown-attribute error */
NC_ERR_MISSING_ELEM, /**< missing-element error */
NC_ERR_BAD_ELEM, /**< bad-element error */
NC_ERR_UNKNOWN_ELEM, /**< unknown-element error */
NC_ERR_UNKNOWN_NS, /**< unknown-namespace error */
NC_ERR_ACCESS_DENIED, /**< access-denied error */
NC_ERR_LOCK_DENIED, /**< lock-denied error */
NC_ERR_RES_DENIED, /**< resource-denied error */
NC_ERR_ROLLBACK_FAILED, /**< rollback-failed error */
NC_ERR_DATA_EXISTS, /**< data-exists error */
NC_ERR_DATA_MISSING, /**< data-missing error */
NC_ERR_OP_NOT_SUPPORTED, /**< operation-not-supported error */
NC_ERR_OP_FAILED, /**< operation-failed error */
NC_ERR_MALFORMED_MSG /**< malformed-message error */
} NC_ERR;
/**
* @brief Enumeration of NETCONF error type (layer)
*/
typedef enum NC_ERROR_TYPE {
NC_ERR_TYPE_UNKNOWN = 0, /**< unknown layer */
NC_ERR_TYPE_TRAN, /**< transport layer */
NC_ERR_TYPE_RPC, /**< RPC layer */
NC_ERR_TYPE_PROT, /**< protocol layer */
NC_ERR_TYPE_APP /**< application layer */
} NC_ERR_TYPE;
/**
* @brief NETCONF server rpc-reply object
*/
struct nc_server_reply;
/**
* @brief NETCONF server Event Notification object
*/
struct nc_server_notif;
/**
* @brief NETCONF server error structure
*/
struct nc_server_error;
/**
* @brief Create an OK rpc-reply object.
*
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_ok(void);
/**
* @brief Create a DATA rpc-reply object.
*
* @param[in] data Reply data tree. This tree must be valid according to
* the RPC output of the RPC this is a reply to.
* @param[in] wd with-default mode if applicable
* @param[in] paramtype Determines how the @p data parameter is treated.
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtype);
/**
* @brief Create an ERROR rpc-reply object.
*
* @param[in] err Errors as opaque data node tree. It will be freed with the returned object.
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_err(struct lyd_node *err);
/**
* @brief Add another error opaque data node tree to an ERROR rpc-reply object.
*
* @param[in] reply ERROR reply to add to.
* @param[in] err Error as opaque data node tree. It will be freed with the returned object.
* @return 0 on success, -1 on errror.
*/
int nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err);
/**
* @brief Get last error from an ERROR rpc-reply object.
*
* @param[in] reply ERROR reply to read from.
* @return Last error opaque data tree, NULL on failure.
*/
const struct lyd_node *nc_server_reply_get_last_err(const struct nc_server_reply *reply);
/**
* @brief Create a server error structure. Its \<error-message\> is filled with
* a general description of the specific error.
*
* @param[in] ctx libyang context to use.
* @param[in] tag \<error-tag\> of the server error specified as #NC_ERR value. According to the tag, the
* specific additional parameters are required:
* - #NC_ERR_IN_USE
* - #NC_ERR_INVALID_VALUE
* - #NC_ERR_ACCESS_DENIED
* - #NC_ERR_ROLLBACK_FAILED
* - #NC_ERR_OP_NOT_SUPPORTED
* - #NC_ERR_TOO_BIG
* - #NC_ERR_RES_DENIED
* - #NC_ERR_OP_FAILED
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - #NC_ERR_MISSING_ATTR
* - #NC_ERR_BAD_ATTR
* - #NC_ERR_UNKNOWN_ATTR
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *attr_name;` - error \<bad-attribute\> value.
* - `const char *elem_name;` - error \<bad-element\> value.
* - #NC_ERR_MISSING_ELEM
* - #NC_ERR_BAD_ELEM
* - #NC_ERR_UNKNOWN_ELEM
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *elem_name;` - error \<bad-element\> value.
* - #NC_ERR_UNKNOWN_NS
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *elem_name;` - error \<bad-element\> value.
* - `const char *nc_name;` - error \<bad-namespace\> value.
* - #NC_ERR_LOCK_DENIED
* - `uint32_t session_id;` - error \<session-id\> value.
* - #NC_ERR_DATA_EXISTS
* - #NC_ERR_DATA_MISSING
* - #NC_ERR_MALFORMED_MSG
* - no additional arguments
* @return Opaque data node tree representing the error.
*/
struct lyd_node *nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...);
/**
* @brief Get the \<error-type\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error type, 0 on error.
*/
NC_ERR_TYPE nc_err_get_type(const struct lyd_node *err);
/**
* @brief Get the \<error-tag\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error tag, 0 on error.
*/
NC_ERR nc_err_get_tag(const struct lyd_node *err);
/**
* @brief Set the \<error-app-tag\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_app_tag New value of \<error-app-tag\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag);
/**
* @brief Get the \<error-app-tag\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error app tag, NULL on error.
*/
const char *nc_err_get_app_tag(const struct lyd_node *err);
/**
* @brief Set the \<error-path\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_path New value of \<error-path\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_path(struct lyd_node *err, const char *error_path);
/**
* @brief Get the \<error-path\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error path, NULL on error.
*/
const char *nc_err_get_path(const struct lyd_node *err);
/**
* @brief Set the \<error-message\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_message New value of \<error-message\>.
* @param[in] lang Optional language of @p error_message.
* @return 0 on success, -1 on error.
*/
int nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang);
/**
* @brief Get the \<error-message\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error message, NULL on error.
*/
const char *nc_err_get_msg(const struct lyd_node *err);
/**
* @brief Set the \<session-id\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] session_id New value of \<session-id\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_sid(struct lyd_node *err, uint32_t session_id);
/**
* @brief Add a \<bad-attribute\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] attr_name Value of the new \<bad-attribute\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name);
/**
* @brief Add a \<bad-element\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] elem_name Value of the new \<bad-element\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name);
/**
* @brief Add a \<bad-namespace\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] ns_name Value of the new \<bad-namespace\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name);
/**
* @brief Add an additional custom element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] other Other error opaque data node tree.
* @return 0 on success, -1 on error.
*/
int nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other);
/**
* @brief Free a server rpc-reply object.
*
* @param[in] reply Server rpc-reply object to free.
*/
void nc_server_reply_free(struct nc_server_reply *reply);
/**
* @brief Create Event Notification object to be sent to the subscribed client(s).
*
* @param[in] event Notification data tree (valid as LYD_OPT_NOTIF) from libyang. The tree is directly used in created
* object, so the caller is supposed to not free the tree on its own, but only via freeng the created object.
* @param[in] eventtime YANG dateTime format value of the time when the event was generated by the event source.
* Caller can use nc_timespec2datetime() to create the value from a timespec value.
* @param[in] paramtype How to further manage data parameters.
* @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_send_notif()
* and freed using nc_server_notif_free().
*/
struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype);
/**
* @brief Send NETCONF Event Notification via the session.
*
* @param[in] session NETCONF session where the Event Notification will be written.
* @param[in] notif NETCOFN Notification object to send via specified session. Object can be created by
* nc_notif_new() function.
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
* waiting and 0 for return if data cannot be sent immediately.
* @return #NC_MSG_NOTIF on success,
* #NC_MSG_WOULDBLOCK in case of a busy session, and
* #NC_MSG_ERROR on error.
*/
NC_MSG_TYPE nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout);
/**
* @brief Free a server Event Notification object.
*
* @param[in] notif Server Event Notification object to free.
*/
void nc_server_notif_free(struct nc_server_notif *notif);
/**
* @brief Get the notification timestamp.
*
* @param[in] notif Server notification to read from.
* @return Datetime timestamp of the notification, NULL on error.
*/
const char *nc_server_notif_get_time(const struct nc_server_notif *notif);
/** @} Client Messages */
#ifdef __cplusplus
}
#endif
#endif /* NC_MESSAGES_SERVER_H_ */

134
src/netconf.h Normal file
View file

@ -0,0 +1,134 @@
/**
* @file netconf.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2's general public functions and structures definitions.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_NETCONF_H_
#define NC_NETCONF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
/**
* @addtogroup misc
* @{
*/
/** @brief Base NETCONF namespace */
#define NC_NS_BASE "urn:ietf:params:xml:ns:netconf:base:1.0"
/** @brief Notifications namespace */
#define NC_NS_NOTIF "urn:ietf:params:xml:ns:netconf:notification:1.0"
/** @brief Default NETCONF over SSH port */
#define NC_PORT_SSH 830
/** @brief Default NETCONF over SSH Call Home port */
#define NC_PORT_CH_SSH 4334
/** @brief Default NETCONF over TLS port */
#define NC_PORT_TLS 6513
/** @brief Default NETCONF over TLS Call Home port */
#define NC_PORT_CH_TLS 4335
/**
* @brief Set RPC callback to a schema node.
*
* @param[in] node const struct lysc_node *node
* @param[in] cb nc_rpc_clb cb
*/
#define nc_set_rpc_callback(node, cb) (node->priv = cb)
/**
* @brief Enumeration of reasons of the NETCONF session termination as defined in RFC 6470.
*/
typedef enum NC_SESSION_TERM_REASON {
NC_SESSION_TERM_ERR = -1, /**< error return code for function getting the session termination reason */
NC_SESSION_TERM_NONE = 0, /**< session still running */
NC_SESSION_TERM_CLOSED, /**< closed by client in a normal fashion */
NC_SESSION_TERM_KILLED, /**< session was terminated by \<kill-session\> operation */
NC_SESSION_TERM_DROPPED, /**< transport layer connection was unexpectedly closed */
NC_SESSION_TERM_TIMEOUT, /**< terminated because of inactivity */
NC_SESSION_TERM_BADHELLO, /**< \<hello\> message was invalid */
NC_SESSION_TERM_OTHER /**< terminated for some other reason */
} NC_SESSION_TERM_REASON;
/**
* @brief Enumeration of NETCONF message types.
*/
typedef enum NC_MSG_TYPE {
NC_MSG_ERROR, /**< error return value */
NC_MSG_WOULDBLOCK, /**< timeout return value */
NC_MSG_NONE, /**< no message at input or message was processed internally */
NC_MSG_HELLO, /**< \<hello\> message */
NC_MSG_BAD_HELLO, /**< \<hello\> message parsing failed */
NC_MSG_RPC, /**< \<rpc\> message */
NC_MSG_REPLY, /**< \<rpc-reply\> message */
NC_MSG_REPLY_ERR_MSGID, /**< \<rpc-reply\> message with missing or wrong message-id attribute value */
NC_MSG_NOTIF /**< \<notification\> message */
} NC_MSG_TYPE;
/**
* @brief Messages of NETCONF message type enum.
*/
extern const char *nc_msgtype2str[];
/**
* @brief Enumeration of the supported types of datastores defined by NETCONF
*/
typedef enum NC_DATASTORE_TYPE {
NC_DATASTORE_ERROR = 0, /**< error state of functions returning the datastore type */
NC_DATASTORE_CONFIG, /**< value describing that the datastore is set as config */
NC_DATASTORE_URL, /**< value describing that the datastore data should be given from the URL */
NC_DATASTORE_RUNNING, /**< base NETCONF's datastore containing the current device configuration */
NC_DATASTORE_STARTUP, /**< separated startup datastore as defined in Distinct Startup Capability */
NC_DATASTORE_CANDIDATE /**< separated working datastore as defined in Candidate Configuration Capability */
} NC_DATASTORE;
/**
* @brief Enumeration of NETCONF with-defaults capability modes.
*/
typedef enum NC_WITHDEFAULTS_MODE {
NC_WD_UNKNOWN = 0, /**< invalid mode */
NC_WD_ALL, /**< report-all mode */
NC_WD_ALL_TAG, /**< report-all-tagged mode */
NC_WD_TRIM, /**< trim mode */
NC_WD_EXPLICIT /**< explicit mode */
} NC_WD_MODE;
/**
* @brief Enumeration of NETCONF (both server and client) rpc-reply types.
*/
typedef enum NC_REPLY {
NC_RPL_OK, /**< OK rpc-reply */
NC_RPL_DATA, /**< DATA rpc-reply */
NC_RPL_ERROR, /**< ERROR rpc-reply */
NC_RPL_NOTIF /**< notification (client-only) */
} NC_RPL;
/**
* @brief Enumeration of function parameter treatments.
*/
typedef enum NC_PARAMTYPE {
NC_PARAMTYPE_CONST, /**< use the parameter directly, do not free */
NC_PARAMTYPE_FREE, /**< use the parameter directly, free afterwards */
NC_PARAMTYPE_DUP_AND_FREE /**< make a copy of the argument, free afterwards */
} NC_PARAMTYPE;
/** @} Miscellaneous */
#ifdef __cplusplus
}
#endif
#endif /* NC_NETCONF_H_ */

1694
src/session.c Normal file

File diff suppressed because it is too large Load diff

244
src/session.h Normal file
View file

@ -0,0 +1,244 @@
/**
* \file session.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 session manipulation
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_H_
#define NC_SESSION_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "netconf.h"
#ifdef NC_ENABLED_SSH
/**
* @brief Enumeration of NETCONF SSH authentication methods
*/
typedef enum {
NC_SSH_AUTH_PUBLICKEY = 0x01, /**< publickey SSH authentication */
NC_SSH_AUTH_PASSWORD = 0x02, /**< password SSH authentication */
NC_SSH_AUTH_INTERACTIVE = 0x04 /**< interactive SSH authentication */
} NC_SSH_AUTH_TYPE;
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @brief Enumeration of cert-to-name mapping types
*/
typedef enum {
NC_TLS_CTN_UNKNOWN = 0, /**< unknown mapping */
NC_TLS_CTN_SPECIFIED, /**< username explicitly specified */
NC_TLS_CTN_SAN_RFC822_NAME, /**< email address as username */
NC_TLS_CTN_SAN_DNS_NAME, /**< DNS name as username */
NC_TLS_CTN_SAN_IP_ADDRESS, /**< IP address as username */
NC_TLS_CTN_SAN_ANY, /**< any certificate Subject Alternative Name as username */
NC_TLS_CTN_COMMON_NAME /**< common name as username */
} NC_TLS_CTN_MAPTYPE;
#endif /* NC_ENABLED_TLS */
/**
* @brief Enumeration of possible session statuses
*/
typedef enum {
NC_STATUS_ERR = -1, /**< error return code for function getting the session status */
NC_STATUS_STARTING = 0, /**< session is not yet fully initiated */
NC_STATUS_CLOSING, /**< session is being closed */
NC_STATUS_INVALID, /**< session is not running and is supposed to be closed (nc_session_free()) */
NC_STATUS_RUNNING /**< up and running */
} NC_STATUS;
/**
* @brief Enumeration of transport implementations (ways how libnetconf implements NETCONF transport protocol)
*/
typedef enum {
NC_TI_NONE = 0, /**< none - session is not connected yet */
NC_TI_FD, /**< file descriptors - use standard input/output, transport protocol is implemented
outside the current application */
NC_TI_UNIX, /**< unix socket */
#ifdef NC_ENABLED_SSH
NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */
#endif
#ifdef NC_ENABLED_TLS
NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport */
#endif
} NC_TRANSPORT_IMPL;
/**
* @brief Enumeration of Call Home connection types.
*/
typedef enum {
NC_CH_CT_NOT_SET = 0,
NC_CH_PERSIST,
NC_CH_PERIOD
} NC_CH_CONN_TYPE;
/**
* @brief Enumeration of Call Home client priority policy.
*/
typedef enum {
NC_CH_FIRST_LISTED = 0, // default
NC_CH_LAST_CONNECTED,
NC_CH_RANDOM
} NC_CH_START_WITH;
/**
* @brief Enumeration of SSH key types.
*/
typedef enum {
NC_SSH_KEY_UNKNOWN = 0,
NC_SSH_KEY_DSA,
NC_SSH_KEY_RSA,
NC_SSH_KEY_ECDSA
} NC_SSH_KEY_TYPE;
/**
* @brief NETCONF session object
*/
struct nc_session;
/**
* @brief Get session status.
*
* @param[in] session Session to get the information from.
* @return Session status.
*/
NC_STATUS nc_session_get_status(const struct nc_session *session);
/**
* @brief Get session termination reason.
*
* @param[in] session Session to get the information from.
* @return Session termination reason enum value.
*/
NC_SESSION_TERM_REASON nc_session_get_term_reason(const struct nc_session *session);
/**
* @brief Get session killer session ID.
*
* @param[in] session Session to get the information from.
* @return Session killer ID.
*/
uint32_t nc_session_get_killed_by(const struct nc_session *session);
/**
* @brief Get session ID.
*
* @param[in] session Session to get the information from.
* @return Session ID.
*/
uint32_t nc_session_get_id(const struct nc_session *session);
/**
* @brief Get session NETCONF version.
*
* @param[in] session Session to get the information from.
* @return 0 for version 1.0, non-zero for version 1.1.
*/
int nc_session_get_version(const struct nc_session *session);
/**
* @brief Get session transport used.
*
* @param[in] session Session to get the information from.
* @return Session transport.
*/
NC_TRANSPORT_IMPL nc_session_get_ti(const struct nc_session *session);
/**
* @brief Get session username.
*
* @param[in] session Session to get the information from.
* @return Session username.
*/
const char *nc_session_get_username(const struct nc_session *session);
/**
* @brief Get session host.
*
* @param[in] session Session to get the information from.
* @return Session host.
*/
const char *nc_session_get_host(const struct nc_session *session);
/**
* @brief Get session port.
*
* @param[in] session Session to get the information from.
* @return Session port.
*/
uint16_t nc_session_get_port(const struct nc_session *session);
/**
* @brief Get session path (unix socket only).
*
* @param[in] session Session to get the information from.
* @return Session unix socket path.
*/
const char *nc_session_get_path(const struct nc_session *session);
/**
* @brief Get session context.
*
* @param[in] session Session to get the information from.
* @return Session context.
*/
struct ly_ctx *nc_session_get_ctx(const struct nc_session *session);
/**
* @brief Assign arbitrary data to a session.
*
* @param[in] session Session to modify.
* @param[in] data Data to be stored in the session.
*/
void nc_session_set_data(struct nc_session *session, void *data);
/**
* @brief Get the data assigned to a session.
*
* @param[in] session Session to get the data from.
* @return Session-specific data.
*/
void *nc_session_get_data(const struct nc_session *session);
/**
* @brief Free the NETCONF session object.
*
* @param[in] session Object to free.
* @param[in] data_free Session user data destructor.
*/
void nc_session_free(struct nc_session *session, void (*data_free)(void *));
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @brief Free all the dynamically allocated thread-specific libssl/libcrypto
* resources.
*
* This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called.
* Call it in every thread your application creates just before the thread exits. In the last thread
* (usually the main one) call nc_client_destroy(), respectively nc_server_destroy().
*/
void nc_thread_destroy(void);
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_H_ */

2946
src/session_client.c Normal file

File diff suppressed because it is too large Load diff

631
src/session_client.h Normal file
View file

@ -0,0 +1,631 @@
/**
* @file session_client.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session client manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_CLIENT_H_
#define NC_SESSION_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
# include <libssh/libssh.h>
#endif
#ifdef NC_ENABLED_TLS
# include <openssl/ssl.h>
#endif
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
/**
* @addtogroup client
* @{
*/
/**
* @brief Set location where libnetconf tries to search for YANG/YIN schemas.
*
* The location is searched when connecting to a NETCONF server and building
* YANG context for further processing of the NETCONF messages and data.
*
* The searchpath is also used to store schemas retreived via \<get-schema\>
* operation - if the schema is not found in searchpath neither via schema
* callback provided via nc_client_set_schema_callback() and server supports
* the NETCONF \<get-schema\> operation, the schema is retrieved this way and
* stored into the searchpath (if specified).
*
* @param[in] path Directory where to search for YANG/YIN schemas.
* @return 0 on success, 1 on (memory allocation) failure.
*/
int nc_client_set_schema_searchpath(const char *path);
/**
* @brief Get schema searchpath that was set by nc_client_set_schema_searchpath().
*
* @return Schema searchpath directory, NULL if not set.
*/
const char *nc_client_get_schema_searchpath(void);
/**
* @brief Set callback function to get missing schemas.
*
* @param[in] clb Callback responsible for returning the missing model.
* @param[in] user_data Arbitrary data that will always be passed to the callback @p clb.
* @return 0 on success, 1 on (memory allocation) failure.
*/
int nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data);
/**
* @brief Get callback function used to get missing schemas.
*
* @param[out] user_data Optionally return the private data set with the callback.
* Note that the caller is responsible for freeing the private data, so before
* changing the callback, private data used for the previous callback should be
* freed.
* @return Pointer to the set callback, NULL if no such callback was set.
*/
ly_module_imp_clb nc_client_get_schema_callback(void **user_data);
/**
* @brief Use the provided thread-specific client's context in the current thread.
*
* Note that from this point the context is shared with the thread from which the context was taken and any
* nc_client_*set* functions and functions creating connection in these threads should be protected from the
* concurrent execution.
*
* Context contains schema searchpath/callback, call home binds, TLS and SSH authentication data (username, keys,
* various certificates and callbacks).
*
* @param[in] context Client's thread-specific context provided by nc_client_get_thread_context().
*/
void nc_client_set_thread_context(void *context);
/**
* @brief Get thread-specific client context for sharing with some other thread using
* nc_client_set_thread_context().
*
* @return Pointer to the client's context of the current thread.
*/
void *nc_client_get_thread_context(void);
/**
* @brief Initialize libssh and/or libssl/libcrypto for use in the client.
*/
void nc_client_init(void);
/**
* @brief Destroy all libssh and/or libssl/libcrypto dynamic memory and
* the client options, for both SSH and TLS, and for Call Home too.
*/
void nc_client_destroy(void);
/** @} Client */
/**
* @defgroup client_session Client Session
* @ingroup client
*
* @brief Client-side NETCONF session manipulation.
* @{
*/
/**
* @brief Connect to the NETCONF server via proviaded input/output file descriptors.
*
* Transport layer is supposed to be already set. Function do not cover authentication
* or any other manipulation with the transport layer, it only establish NETCONF session
* by sending and processing NETCONF \<hello\> messages.
*
* @param[in] fdin Input file descriptor for reading (clear) data from NETCONF server.
* @param[in] fdout Output file descriptor for writing (clear) data for NETCONF server.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL in case of error.
*/
struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server via unix socket.
*
* Connect to netconf server via an unix socket. Function do not cover authentication
* or any other manipulation with the transport layer, it only establish NETCONF session
* by sending and processing NETCONF \<hello\> messages.
*
* @param[in] address Path to the unix socket.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL in case of error.
*/
struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx);
/** @} Client Session */
#ifdef NC_ENABLED_SSH
/**
* @defgroup client_ssh Client SSH
* @ingroup client
*
* @brief Client-side settings for SSH connections.
* @{
*/
/**
* @brief Set SSH authentication hostkey check (knownhosts) callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_hostkey_check_clb()).
*
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void *priv);
/**
* @brief Get currently set SSH authentication hostkey check (knownhosts) callback and its private data previously set
* by nc_client_ssh_set_auth_hostkey_check_clb().
*
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void **priv);
/**
* @brief Set SSH password authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_password_clb()).
*
* @param[in] auth_password Function to call, returns the password for username\@hostname.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
void *priv);
/**
* @brief Get currently set SSH password authentication callback and its private data previously set
* by nc_client_ssh_set_auth_password_clb().
*
* @param[out] auth_password Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
void **priv);
/**
* @brief Set SSH interactive authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_interactive_clb()).
*
* @param[in] auth_interactive Function to call for every question, returns the answer for
* authentication name with instruction and echoing prompt.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void *priv);
/**
* @brief Get currently set SSH interactive authentication callback and its private data previously set
* by nc_client_ssh_set_auth_interactive_clb().
*
* @param[out] auth_interactive Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void **priv);
/**
* @brief Set SSH publickey authentication encrypted private key passphrase callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_privkey_passphrase_clb()).
*
* @param[in] auth_privkey_passphrase Function to call for every question, returns
* the passphrase for the specific private key.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
void *priv);
/**
* @brief Get currently set SSH publickey authentication encrypted private key passphrase callback and its private data
* previously set by nc_client_ssh_set_auth_privkey_passphrase_clb().
*
* @param[out] auth_privkey_passphrase Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
void **priv);
/**
* @brief Add an SSH public and private key pair to be used for client authentication.
*
* Private key can be encrypted, the passphrase will be asked for before using it.
*
* @param[in] pub_key Path to the public key.
* @param[in] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key);
/**
* @brief Remove an SSH public and private key pair that was used for client authentication.
*
* @param[in] idx Index of the keypair starting with 0.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_del_keypair(int idx);
/**
* @brief Get the number of public an private key pairs set to be used for client authentication.
*
* @return Keypair count.
*/
int nc_client_ssh_get_keypair_count(void);
/**
* @brief Get a specific keypair set to be used for client authentication.
*
* @param[in] idx Index of the specific keypair.
* @param[out] pub_key Path to the public key.
* @param[out] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key);
/**
* @brief Set SSH authentication method preference.
*
* The default preference is as follows:
* - interactive authentication (3)
* - password authentication (2)
* - public key authentication (1)
*
* @param[in] auth_type Authentication method to modify the preference of.
* @param[in] pref Preference of @p auth_type. Higher number increases priority, negative values disable the method.
*/
void nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
/**
* @brief Get SSH authentication method preference.
*
* @param[in] auth_type Authentication method to retrieve the prefrence of.
* @return Preference of the @p auth_type.
*/
int16_t nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type);
/**
* @brief Set client SSH username used for authentication.
*
* @param[in] username Username to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_set_username(const char *username);
/**
* @brief Get client SSH username used for authentication.
*
* @return Username used.
*/
const char *nc_client_ssh_get_username(void);
/**
* @brief Connect to the NETCONF server using SSH transport (via libssh).
*
* SSH session is created with default options. If the caller needs to use specific SSH session properties,
* they are supposed to use nc_connect_libssh().
*
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
* 'localhost' is used by default if NULL is specified.
* @param[in] port Port number of the target server. Default value 830 is used if 0 is specified.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server using the provided SSH (libssh) session.
*
* SSH session can have any options set, they will not be modified. If no options were set,
* host 'localhost', port 22, and the username detected from the EUID is used. If socket is
* set and connected only the host and the username must be set/is detected. Or the @p ssh_session
* can already be authenticated in which case it is used directly.
*
* @param[in] ssh_session libssh structure representing SSH session object. After passing it
* to libnetconf2 this way, it is fully managed by it (including freeing!).
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx);
/**
* @brief Create another NETCONF session on existing SSH session using separated SSH channel.
*
* @param[in] session Existing NETCONF session. The session has to be created on SSH transport layer using libssh -
* it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel().
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx);
/** @} Client SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup client_tls Client TLS
* @ingroup client
*
* @brief Client-side settings for TLS connections.
* @{
*/
/**
* @brief Set client authentication identity - a certificate and a private key.
*
* @param[in] client_cert Path to the file containing the client certificate.
* @param[in] client_key Path to the file containing the private key for the @p client_cert.
* If NULL, key is expected to be stored with @p client_cert.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key);
/**
* @brief Get client authentication identity - a certificate and a private key.
*
* @param[out] client_cert Path to the file containing the client certificate. Can be NULL.
* @param[out] client_key Path to the file containing the private key for the @p client_cert. Can be NULL.
*/
void nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key);
/**
* @brief Set client trusted CA certificates paths.
*
* @param[in] ca_file Location of the CA certificate file used to verify server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
/**
* @brief Get client trusted CA certificates paths.
*
* @param[out] ca_file Location of the CA certificate file used to verify server certificates.
* Can be NULL.
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates.
* Can be NULL.
*/
void nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
/**
* @brief Set client Certificate Revocation List paths.
*
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir);
/**
* @brief Get client Certificate Revocation List paths.
*
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
*/
void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir);
/**
* @brief Connect to the NETCONF server using TLS transport (via libssl)
*
* TLS session is created with the certificates set using nc_client_tls_* functions, which must be called beforehand!
* If the caller needs to use specific TLS session properties, they are supposed to use nc_connect_libssl().
*
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
* 'localhost' is used by default if NULL is specified.
* @param[in] port Port number of the target server. Default value 6513 is used if 0 is specified.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_tls(const char *host, uint16_t port, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server using the provided TLS (libssl) session.
*
* The TLS session supplied is expected to be fully connected and authenticated!
*
* @param[in] tls libssl structure representing the TLS session object.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx);
/** @} Client TLS */
#endif /* NC_ENABLED_TLS */
/**
* @addtogroup client_session
* @{
*/
/**
* @brief Get session capabilities.
*
* @param[in] session Session to get the information from.
* @return NULL-terminated array of the @p session capabilities.
*/
const char * const *nc_session_get_cpblts(const struct nc_session *session);
/**
* @brief Check capability presence in a session.
*
* @param[in] session Session to check.
* @param[in] capab Capability to look for, capability with any additional suffix will match.
* @return Matching capability, NULL if none found.
*/
const char *nc_session_cpblt(const struct nc_session *session, const char *capab);
/**
* @brief Check whether the session has a notification thread running.
*
* @param[in] session Session to check.
* @return 1 if notfication thread is running, 0 otherwise.
*/
int nc_session_ntf_thread_running(const struct nc_session *session);
/**
* @brief Receive NETCONF RPC reply.
*
* @note This function can be called in a single thread only.
*
* @param[in] session NETCONF session from which the function gets data. It must be the
* client side session object.
* @param[in] rpc Original RPC this should be the reply to.
* @param[in] msgid Expected message ID of the reply.
* @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
* waiting and 0 for immediate return if data are not available on the wire.
* @param[out] envp NETCONF rpc-reply XML envelopes.
* @param[out] op Parsed NETCONF reply data, if any (none for \<ok\> or error replies). Set only on #NC_MSG_REPLY
* and #NC_MSG_REPLY_ERR_MSGID return.
* @return #NC_MSG_REPLY for success,
* #NC_MSG_WOULDBLOCK if @p timeout has elapsed,
* #NC_MSG_ERROR if reading has failed,
* #NC_MSG_NOTIF if a notification was read instead (call this function again to get the reply), and
* #NC_MSG_REPLY_ERR_MSGID if a reply with missing or wrong message-id was received.
*/
NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout,
struct lyd_node **envp, struct lyd_node **op);
/**
* @brief Receive NETCONF Notification.
*
* @param[in] session NETCONF session from which the function gets data. It must be the
* client side session object.
* @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
* waiting and 0 for immediate return if data are not available on the wire.
* @param[out] envp NETCONF notification XML envelopes.
* @param[out] op Parsed NETCONF notification data.
* @return #NC_MSG_NOTIF for success,
* #NC_MSG_WOULDBLOCK if @p timeout has elapsed,
* #NC_MSG_ERROR if reading has failed, and
* #NC_MSG_REPLY if a reply was read instead (call this function again to get a notification).
*/
NC_MSG_TYPE nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op);
/**
* @brief Receive NETCONF Notifications in a separate thread until the session is terminated
* or \<notificationComplete\> is received.
*
* @param[in] session Netconf session to read notifications from.
* @param[in] notif_clb Function that is called for every received notification (including
* \<notificationComplete\>). Parameters are the session the notification was received on
* and the notification data.
* @return 0 if the thread was successfully created, -1 on error.
*/
int nc_recv_notif_dispatch(struct nc_session *session,
void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op));
/**
* @brief Send NETCONF RPC message via the session.
*
* @param[in] session NETCONF session where the RPC will be written.
* @param[in] rpc NETCONF RPC object to send via the specified session.
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
* waiting and 0 for return if data cannot be sent immediately.
* @param[out] msgid If RPC was successfully sent, this is it's message ID.
* @return #NC_MSG_RPC on success,
* #NC_MSG_WOULDBLOCK in case of a busy session, and
* #NC_MSG_ERROR on error.
*/
NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid);
/**
* @brief Make a session not strict when sending RPCs and receiving RPC replies. In other words,
* it will silently skip unknown nodes without an error.
*
* Generally, no such data should be worked with, so use this function only when you know what you
* are doing and you understand the consequences.
*
* @param[in] session NETCONF client session.
*/
void nc_client_session_set_not_strict(struct nc_session *session);
/** @} Client Session */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_CLIENT_H_ */

354
src/session_client_ch.h Normal file
View file

@ -0,0 +1,354 @@
/**
* @file session_client_ch.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 Call Home session client manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_CLIENT_CH_H_
#define NC_SESSION_CLIENT_CH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
# include <libssh/libssh.h>
#endif
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @defgroup client_ch Client-side Call Home
* @ingroup client
*
* @brief Call Home functionality for client-side applications.
* @{
*/
/**
* @brief Accept a Call Home connection on any of the listening binds.
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
* non-blocking call, -1 for infinite waiting.
* @param[in] ctx Session context to use. Can be NULL.
* @param[out] session New session.
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session);
/** @} Client-side Call Home */
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
/**
* @defgroup client_ch_ssh Client-side Call Home on SSH
* @ingroup client_ch
*
* @brief SSH settings for the Call Home functionality
* @{
*/
/**
* @brief Set SSH Call Home authentication hostkey check (knownhosts) callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_hostkey_check_clb()).
*
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home authentication hostkey check (knownhosts) callback and its private data
* previously set by nc_client_ssh_ch_set_auth_hostkey_check_clb().
*
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void **priv);
/**
* @brief Set SSH Call Home password authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_password_clb()).
*
* @param[in] auth_password Function to call, returns the password for username\@hostname.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home password authentication callback and its private data
* previously set by nc_client_ssh_ch_set_auth_password_clb().
*
* @param[out] auth_password Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
void **priv);
/**
* @brief Set SSH Call Home interactive authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_interactive_clb()).
*
* @param[in] auth_interactive Function to call for every question, returns the answer for
* authentication name with instruction and echoing prompt.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home interactive authentication callback and its private data
* previously set by nc_client_ssh_ch_set_auth_interactive_clb().
*
* @param[out] auth_interactive Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void **priv);
/**
* @brief Set SSH Call Home publickey authentication encrypted private key passphrase callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_privkey_passphrase_clb()).
*
* @param[in] auth_privkey_passphrase Function to call for every question, returns
* the passphrase for the specific private key.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home publickey authentication encrypted private key passphrase callback and its
* private data previously set by nc_client_ssh_ch_set_auth_privkey_passphrase_clb().
*
* @param[out] auth_privkey_passphrase Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
void **priv);
/**
* @brief Add a new client bind and start listening on it for SSH Call Home connections.
*
* @param[in] address IP address to bind to.
* @param[in] port Port to bind to.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port);
/**
* @brief Remove an SSH listening client bind.
*
* @param[in] address IP address the socket was bound to. NULL matches all.
* @param[in] port Port the socket was bound to. 0 matches all.
* @return 0 on success, -1 on not found.
*/
int nc_client_ssh_ch_del_bind(const char *address, uint16_t port);
/**
* @brief Add an SSH public and private key pair to be used for Call Home client authentication.
*
* Private key can be encrypted, the passphrase will be asked for before using it.
*
* @param[in] pub_key Path to the public key.
* @param[in] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key);
/**
* @brief Remove an SSH public and private key pair that was used for Call Home client authentication.
*
* @param[in] idx Index of the keypair starting with 0.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_del_keypair(int idx);
/**
* @brief Get the number of public an private key pairs set to be used for Call Home client authentication.
*
* @return Keypair count.
*/
int nc_client_ssh_ch_get_keypair_count(void);
/**
* @brief Get a specific keypair set to be used for Call Home client authentication.
*
* @param[in] idx Index of the specific keypair.
* @param[out] pub_key Path to the public key.
* @param[out] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key);
/**
* @brief Set SSH Call Home authentication method preference.
*
* The default preference is as follows:
* - public key authentication (3)
* - password authentication (2)
* - interactive authentication (1)
*
* @param[in] auth_type Authentication method to modify the preference of.
* @param[in] pref Preference of @p auth_type. Higher number increases priority, negative values disable the method.
*/
void nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
/**
* @brief Get SSH Call Home authentication method preference.
*
* @param[in] auth_type Authentication method to retrieve the prefrence of.
* @return Preference of the @p auth_type.
*/
int16_t nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type);
/**
* @brief Set client Call Home SSH username used for authentication.
*
* @param[in] username Username to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_set_username(const char *username);
/**
* @brief Get client Call Home SSH username used for authentication.
*
* @return Username used.
*/
const char *nc_client_ssh_ch_get_username(void);
/** @} Client-side Call Home on SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup client_ch_tls Client-side Call Home on TLS
* @ingroup client_ch
*
* @brief TLS settings for the Call Home functionality
* @{
*/
/**
* @brief Add a new client bind and start listening on it for TLS Call Home connections.
*
* @param[in] address IP address to bind to.
* @param[in] port Port to bind to.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port);
/**
* @brief Remove a TLS listening client bind.
*
* @param[in] address IP address the socket was bound to. NULL matches all.
* @param[in] port Port the socket was bound to. 0 matches all.
* @return 0 on success, -1 on not found.
*/
int nc_client_tls_ch_del_bind(const char *address, uint16_t port);
/**
* @brief Set client Call Home authentication identity - a certificate and a private key.
*
* @param[in] client_cert Path to the file containing the client certificate.
* @param[in] client_key Path to the file containing the private key for the @p client_cert.
* If NULL, key is expected to be stored with @p client_cert.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key);
/**
* @brief Get client Call Home authentication identity - a certificate and a private key.
*
* @param[out] client_cert Path to the file containing the client certificate. Can be NULL.
* @param[out] client_key Path to the file containing the private key for the @p client_cert.
* Can be NULL.
*/
void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key);
/**
* @brief Set client Call Home trusted CA certificates.
*
* @param[in] ca_file Location of the CA certificate file used to verify server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
/**
* @brief Get client Call Home trusted CA certificates.
*
* @param[out] ca_file Location of the CA certificate file used to verify server certificates.
* Can be NULL.
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates.
* Can be NULL.
*/
void nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
/**
* @brief Set client Call Home Certificate Revocation Lists.
*
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir);
/**
* @brief Get client Call Home Certificate Revocation Lists.
*
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
* Can be NULL.
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* Can be NULL.
*/
void nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir);
/** @} Client-side Call Home on TLS */
#endif /* NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_CLIENT_CH_H_ */

1911
src/session_client_ssh.c Normal file

File diff suppressed because it is too large Load diff

848
src/session_client_tls.c Normal file
View file

@ -0,0 +1,848 @@
/**
* \file session_client_tls.c
* \author Radek Krejci <rkrejci@cesnet.cz>
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 - TLS specific session client transport functions
*
* This source is compiled only with libssl.
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <libyang/libyang.h>
#include <openssl/err.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include "libnetconf.h"
#include "session_client.h"
#include "session_client_ch.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
#endif
struct nc_client_context *nc_client_context_location(void);
int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
#define client_opts nc_client_context_location()->opts
#define tls_opts nc_client_context_location()->tls_opts
#define tls_ch_opts nc_client_context_location()->tls_ch_opts
static int tlsauth_ch;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
static int
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
X509_STORE_CTX *store_ctx;
X509_OBJECT *obj;
X509_NAME *subject, *issuer;
X509 *cert;
X509_CRL *crl;
X509_REVOKED *revoked;
EVP_PKEY *pubkey;
int i, n, rc;
const ASN1_TIME *next_update = NULL;
struct nc_client_tls_opts *opts;
if (!preverify_ok) {
return 0;
}
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
if (!opts->crl_store) {
/* nothing to check */
return 1;
}
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
subject = X509_get_subject_name(cert);
issuer = X509_get_issuer_name(cert);
/* try to retrieve a CRL corresponding to the _subject_ of
* the current certificate in order to verify it's integrity */
store_ctx = X509_STORE_CTX_new();
obj = X509_OBJECT_new();
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
X509_STORE_CTX_free(store_ctx);
crl = X509_OBJECT_get0_X509_CRL(obj);
if ((rc > 0) && crl) {
next_update = X509_CRL_get0_nextUpdate(crl);
/* verify the signature on this CRL */
pubkey = X509_get_pubkey(cert);
if (X509_CRL_verify(crl, pubkey) <= 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
X509_OBJECT_free(obj);
if (pubkey) {
EVP_PKEY_free(pubkey);
}
return 0; /* fail */
}
if (pubkey) {
EVP_PKEY_free(pubkey);
}
/* check date of CRL to make sure it's not expired */
if (!next_update) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
X509_OBJECT_free(obj);
return 0; /* fail */
}
if (X509_cmp_current_time(next_update) < 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
X509_OBJECT_free(obj);
return 0; /* fail */
}
X509_OBJECT_free(obj);
}
/* try to retrieve a CRL corresponding to the _issuer_ of
* the current certificate in order to check for revocation */
store_ctx = X509_STORE_CTX_new();
obj = X509_OBJECT_new();
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
X509_STORE_CTX_free(store_ctx);
crl = X509_OBJECT_get0_X509_CRL(obj);
if ((rc > 0) && crl) {
/* check if the current certificate is revoked by this CRL */
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
for (i = 0; i < n; i++) {
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
ERR(NULL, "Certificate revoked!");
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
X509_OBJECT_free(obj);
return 0; /* fail */
}
}
X509_OBJECT_free(obj);
}
return 1; /* success */
}
#else
static int
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
X509_STORE_CTX store_ctx;
X509_OBJECT obj;
X509_NAME *subject, *issuer;
X509 *cert;
X509_CRL *crl;
X509_REVOKED *revoked;
EVP_PKEY *pubkey;
int i, n, rc;
ASN1_TIME *next_update = NULL;
struct nc_client_tls_opts *opts;
if (!preverify_ok) {
return 0;
}
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
if (!opts->crl_store) {
/* nothing to check */
return 1;
}
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
subject = X509_get_subject_name(cert);
issuer = X509_get_issuer_name(cert);
/* try to retrieve a CRL corresponding to the _subject_ of
* the current certificate in order to verify it's integrity */
memset((char *)&obj, 0, sizeof obj);
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
if ((rc > 0) && crl) {
next_update = X509_CRL_get_nextUpdate(crl);
/* verify the signature on this CRL */
pubkey = X509_get_pubkey(cert);
if (X509_CRL_verify(crl, pubkey) <= 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
X509_OBJECT_free_contents(&obj);
if (pubkey) {
EVP_PKEY_free(pubkey);
}
return 0; /* fail */
}
if (pubkey) {
EVP_PKEY_free(pubkey);
}
/* check date of CRL to make sure it's not expired */
if (!next_update) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
if (X509_cmp_current_time(next_update) < 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
X509_OBJECT_free_contents(&obj);
}
/* try to retrieve a CRL corresponding to the _issuer_ of
* the current certificate in order to check for revocation */
memset((char *)&obj, 0, sizeof obj);
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
if ((rc > 0) && crl) {
/* check if the current certificate is revoked by this CRL */
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
for (i = 0; i < n; i++) {
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
ERR(NULL, "Certificate revoked!");
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
}
X509_OBJECT_free_contents(&obj);
}
return 1; /* success */
}
#endif
void
_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
{
free(opts->cert_path);
free(opts->key_path);
free(opts->ca_file);
free(opts->ca_dir);
SSL_CTX_free(opts->tls_ctx);
free(opts->crl_file);
free(opts->crl_dir);
X509_STORE_free(opts->crl_store);
memset(opts, 0, sizeof *opts);
}
void
nc_client_tls_destroy_opts(void)
{
_nc_client_tls_destroy_opts(&tls_opts);
_nc_client_tls_destroy_opts(&tls_ch_opts);
}
static int
_nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts)
{
if (!client_cert) {
ERRARG("client_cert");
return -1;
}
free(opts->cert_path);
free(opts->key_path);
opts->cert_path = strdup(client_cert);
if (!opts->cert_path) {
ERRMEM;
return -1;
}
if (client_key) {
opts->key_path = strdup(client_key);
if (!opts->key_path) {
ERRMEM;
return -1;
}
} else {
opts->key_path = NULL;
}
opts->tls_ctx_change = 1;
return 0;
}
API int
nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
{
return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
}
API int
nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
{
return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
}
static void
_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
{
if (!client_cert && !client_key) {
ERRARG("client_cert and client_key");
return;
}
if (client_cert) {
*client_cert = opts->cert_path;
}
if (client_key) {
*client_key = opts->key_path;
}
}
API void
nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
{
_nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
}
API void
nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
{
_nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
}
static int
_nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts)
{
if (!ca_file && !ca_dir) {
ERRARG("ca_file and ca_dir");
return -1;
}
free(opts->ca_file);
free(opts->ca_dir);
if (ca_file) {
opts->ca_file = strdup(ca_file);
if (!opts->ca_file) {
ERRMEM;
return -1;
}
} else {
opts->ca_file = NULL;
}
if (ca_dir) {
opts->ca_dir = strdup(ca_dir);
if (!opts->ca_dir) {
ERRMEM;
return -1;
}
} else {
opts->ca_dir = NULL;
}
opts->tls_ctx_change = 1;
return 0;
}
API int
nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
{
return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
}
API int
nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
{
return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
}
static void
_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
{
if (!ca_file && !ca_dir) {
ERRARG("ca_file and ca_dir");
return;
}
if (ca_file) {
*ca_file = opts->ca_file;
}
if (ca_dir) {
*ca_dir = opts->ca_dir;
}
}
API void
nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
{
_nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
}
API void
nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
{
_nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
}
static int
_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts)
{
if (!crl_file && !crl_dir) {
ERRARG("crl_file and crl_dir");
return -1;
}
free(opts->crl_file);
free(opts->crl_dir);
if (crl_file) {
opts->crl_file = strdup(crl_file);
if (!opts->crl_file) {
ERRMEM;
return -1;
}
} else {
opts->crl_file = NULL;
}
if (crl_dir) {
opts->crl_dir = strdup(crl_dir);
if (!opts->crl_dir) {
ERRMEM;
return -1;
}
} else {
opts->crl_dir = NULL;
}
opts->crl_store_change = 1;
return 0;
}
API int
nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
{
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
}
API int
nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
{
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
}
static void
_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
{
if (!crl_file && !crl_dir) {
ERRARG("crl_file and crl_dir");
return;
}
if (crl_file) {
*crl_file = opts->crl_file;
}
if (crl_dir) {
*crl_dir = opts->crl_dir;
}
}
API void
nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
{
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
}
API void
nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
{
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
}
API int
nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
{
return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
}
API int
nc_client_tls_ch_del_bind(const char *address, uint16_t port)
{
return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
}
static int
nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
{
char *key;
X509_LOOKUP *lookup;
if (!opts->tls_ctx || opts->tls_ctx_change) {
SSL_CTX_free(opts->tls_ctx);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
/* prepare global SSL context, highest available method is negotiated autmatically */
if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
#else
/* prepare global SSL context, allow only mandatory TLS 1.2 */
if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
#endif
{
ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
return -1;
}
SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
/* get peer certificate */
if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path,
ERR_reason_error_string(ERR_get_error()));
return -1;
}
/* if the file with private key not specified, expect that the private key is stored with the certificate */
if (!opts->key_path) {
key = opts->cert_path;
} else {
key = opts->key_path;
}
if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
ERR(NULL, "Loading the client priavte key from \'%s\' failed (%s).", key,
ERR_reason_error_string(ERR_get_error()));
return -1;
}
if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).",
ERR_reason_error_string(ERR_get_error()));
return -1;
}
}
if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
/* set the revocation store with the correct paths for the callback */
X509_STORE_free(opts->crl_store);
opts->crl_store = X509_STORE_new();
if (!opts->crl_store) {
ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
return -1;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
/* whaveter this does... */
opts->crl_store->cache = 0;
#endif
if (opts->crl_file) {
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
ERR(NULL, "Failed to add lookup method to CRL checking.");
return -1;
}
if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file);
return -1;
}
}
if (opts->crl_dir) {
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
ERR(NULL, "Failed to add lookup method to CRL checking.");
return -1;
}
if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
return -1;
}
}
}
return 0;
}
API struct nc_session *
nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
{
struct nc_session *session = NULL;
int sock, verify, ret;
unsigned long tls_err;
struct timespec ts_timeout, ts_cur;
const char *peername;
char *ip_host = NULL;
if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
ERRINIT;
return NULL;
}
/* process parameters */
if (!host || strisempty(host)) {
host = "localhost";
}
if (!port) {
port = NC_PORT_TLS;
}
/* create/update TLS structures */
if (nc_client_tls_update_opts(&tls_opts)) {
return NULL;
}
/* prepare session structure */
session = nc_new_session(NC_CLIENT, 0);
if (!session) {
ERRMEM;
return NULL;
}
session->status = NC_STATUS_STARTING;
/* fill the session */
session->ti_type = NC_TI_OPENSSL;
if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
goto fail;
}
/* create and assign socket */
sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
}
SSL_set_fd(session->ti.tls, sock);
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
/* server identity (hostname) verification */
if (!SSL_set1_host(session->ti.tls, host)) {
ERR(NULL, "Failed to set expected server hostname.");
goto fail;
}
#endif
/* connect and perform the handshake */
nc_gettimespec_mono(&ts_timeout);
nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
tlsauth_ch = 0;
while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
usleep(NC_TIMEOUT_STEP);
nc_gettimespec_mono(&ts_cur);
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
ERR(NULL, "SSL_connect timeout.");
goto fail;
}
}
if (ret != 1) {
switch (SSL_get_error(session->ti.tls, ret)) {
case SSL_ERROR_SYSCALL:
ERR(NULL, "SSL_connect failed (%s).", errno ? strerror(errno) : "unexpected EOF");
break;
case SSL_ERROR_SSL:
tls_err = ERR_get_error();
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(tls_err));
break;
default:
ERR(NULL, "SSL_connect failed.");
break;
}
goto fail;
}
/* check certificate verification result */
verify = SSL_get_verify_result(session->ti.tls);
switch (verify) {
case X509_V_OK:
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
peername = SSL_get0_peername(session->ti.tls);
VRB(NULL, "Server certificate successfully verified (domain \"%s\").", peername ? peername : "<unknown>");
#else
(void)peername;
VRB(NULL, "Server certificate successfully verified.");
#endif
break;
default:
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
}
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;
/* NETCONF handshake */
if (nc_handshake_io(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
if (nc_ctx_check_and_fill(session) == -1) {
goto fail;
}
/* store information into session and the dictionary */
lydict_insert_zc(ctx, ip_host, &session->host);
session->port = port;
lydict_insert(ctx, "certificate-based", 0, &session->username);
return session;
fail:
free(ip_host);
nc_session_free(session, NULL);
return NULL;
}
API struct nc_session *
nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
{
struct nc_session *session;
if (!tls) {
ERRARG("tls");
return NULL;
} else if (!SSL_is_init_finished(tls)) {
ERR(NULL, "Supplied TLS session is not fully connected!");
return NULL;
}
/* prepare session structure */
session = nc_new_session(NC_CLIENT, 0);
if (!session) {
ERRMEM;
return NULL;
}
session->status = NC_STATUS_STARTING;
session->ti_type = NC_TI_OPENSSL;
session->ti.tls = tls;
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;
/* NETCONF handshake */
if (nc_handshake_io(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
if (nc_ctx_check_and_fill(session) == -1) {
goto fail;
}
return session;
fail:
session->ti.tls = NULL;
nc_session_free(session, NULL);
return NULL;
}
struct nc_session *
nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
{
int verify, ret;
SSL *tls = NULL;
struct nc_session *session = NULL;
struct timespec ts_timeout, ts_cur;
if (nc_client_tls_update_opts(&tls_ch_opts)) {
goto cleanup;
}
if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
goto cleanup;
}
SSL_set_fd(tls, sock);
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
/* connect and perform the handshake */
if (timeout > -1) {
nc_gettimespec_mono(&ts_timeout);
nc_addtimespec(&ts_timeout, timeout);
}
tlsauth_ch = 1;
while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
usleep(NC_TIMEOUT_STEP);
if (timeout > -1) {
nc_gettimespec_mono(&ts_cur);
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
ERR(NULL, "SSL_connect timeout.");
goto cleanup;
}
}
}
if (ret != 1) {
switch (SSL_get_error(tls, ret)) {
case SSL_ERROR_SYSCALL:
ERR(NULL, "SSL_connect failed (%s).", strerror(errno));
break;
case SSL_ERROR_SSL:
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
break;
default:
ERR(NULL, "SSL_connect failed.");
break;
}
goto cleanup;
}
/* check certificate verification result */
verify = SSL_get_verify_result(tls);
switch (verify) {
case X509_V_OK:
VRB(NULL, "Server certificate successfully verified.");
break;
default:
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
}
/* connect */
session = nc_connect_libssl(tls, ctx);
if (!session) {
goto cleanup;
}
session->flags |= NC_SESSION_CALLHOME;
/* store information into session and the dictionary */
lydict_insert(session->ctx, host, 0, &session->host);
session->port = port;
lydict_insert(session->ctx, "certificate-based", 0, &session->username);
cleanup:
if (!session) {
SSL_free(tls);
close(sock);
}
return session;
}

861
src/session_p.h Normal file
View file

@ -0,0 +1,861 @@
/**
* @file session_p.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session manipulation
*
* Copyright (c) 2017 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_PRIVATE_H_
#define NC_SESSION_PRIVATE_H_
#include <pthread.h>
#include <stdint.h>
#include <sys/stat.h>
#include <libyang/libyang.h>
#include "compat.h"
#include "libnetconf.h"
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
#ifdef NC_ENABLED_SSH
# include <libssh/callbacks.h>
# include <libssh/libssh.h>
# include <libssh/server.h>
/* seconds */
# define NC_SSH_TIMEOUT 10
/* number of all supported authentication methods */
# define NC_SSH_AUTH_COUNT 3
/* ACCESS unlocked */
struct nc_client_ssh_opts {
/* SSH authentication method preferences */
struct {
NC_SSH_AUTH_TYPE type;
int16_t value;
} auth_pref[NC_SSH_AUTH_COUNT];
/* SSH key pairs */
struct {
char *pubkey_path;
char *privkey_path;
int8_t privkey_crypt;
} *keys;
uint16_t key_count;
/* SSH authentication callbacks */
int (*auth_hostkey_check)(const char *, ssh_session, void *);
char *(*auth_password)(const char *, const char *, void *);
char *(*auth_interactive)(const char *, const char *, const char *, int, void *);
char *(*auth_privkey_passphrase)(const char *, void *);
/* private data for the callbacks */
void *auth_hostkey_check_priv;
void *auth_password_priv;
void *auth_interactive_priv;
void *auth_privkey_passphrase_priv;
char *username;
};
/* ACCESS locked, separate locks */
struct nc_server_ssh_opts {
/* SSH bind options */
const char **hostkeys;
uint8_t hostkey_count;
int auth_methods;
uint16_t auth_attempts;
uint16_t auth_timeout;
};
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
# include <openssl/bio.h>
# include <openssl/ssl.h>
/* ACCESS unlocked */
struct nc_client_tls_opts {
char *cert_path;
char *key_path;
char *ca_file;
char *ca_dir;
int8_t tls_ctx_change;
SSL_CTX *tls_ctx;
char *crl_file;
char *crl_dir;
int8_t crl_store_change;
X509_STORE *crl_store;
};
/* ACCESS locked, separate locks */
struct nc_server_tls_opts {
const char *server_cert;
const char **trusted_cert_lists;
uint16_t trusted_cert_list_count;
const char *trusted_ca_file;
const char *trusted_ca_dir;
X509_STORE *crl_store;
struct nc_ctn {
uint32_t id;
const char *fingerprint;
NC_TLS_CTN_MAPTYPE map_type;
const char *name;
struct nc_ctn *next;
} *ctn;
};
#endif /* NC_ENABLED_TLS */
/* ACCESS unlocked */
struct nc_keepalives {
int enabled;
uint16_t idle_time;
uint16_t max_probes;
uint16_t probe_interval;
};
/* ACCESS unlocked */
struct nc_server_unix_opts {
mode_t mode;
uid_t uid;
gid_t gid;
};
/* ACCESS unlocked */
struct nc_client_opts {
char *schema_searchpath;
ly_module_imp_clb schema_clb;
void *schema_clb_data;
struct nc_keepalives ka;
struct nc_bind {
const char *address;
uint16_t port;
int sock;
int pollin;
} *ch_binds;
NC_TRANSPORT_IMPL *ch_bind_ti;
uint16_t ch_bind_count;
};
/* ACCESS unlocked */
struct nc_client_context {
unsigned int refcount;
struct nc_client_opts opts;
#ifdef NC_ENABLED_SSH
struct nc_client_ssh_opts ssh_opts;
struct nc_client_ssh_opts ssh_ch_opts;
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
struct nc_client_tls_opts tls_opts;
struct nc_client_tls_opts tls_ch_opts;
#endif /* NC_ENABLED_TLS */
};
struct nc_server_opts {
/* ACCESS unlocked (dictionary locked internally in libyang) */
struct ly_ctx *ctx;
/* ACCESS unlocked */
NC_WD_MODE wd_basic_mode;
int wd_also_supported;
uint32_t capabilities_count;
const char **capabilities;
char *(*content_id_clb)(void *user_data);
void *content_id_data;
void (*content_id_data_free)(void *data);
/* ACCESS unlocked */
uint16_t hello_timeout;
uint16_t idle_timeout;
#ifdef NC_ENABLED_SSH
int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data);
void *passwd_auth_data;
void (*passwd_auth_data_free)(void *data);
int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data);
void *pubkey_auth_data;
void (*pubkey_auth_data_free)(void *data);
int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data);
void *interactive_auth_data;
void (*interactive_auth_data_free)(void *data);
#endif
#ifdef NC_ENABLED_TLS
int (*user_verify_clb)(const struct nc_session *session);
int (*server_cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data, char **privkey_path,
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *server_cert_data;
void (*server_cert_data_free)(void *data);
int (*server_cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
char ***cert_data, int *cert_data_count);
void *server_cert_chain_data;
void (*server_cert_chain_data_free)(void *data);
int (*trusted_cert_list_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
char ***cert_data, int *cert_data_count);
void *trusted_cert_list_data;
void (*trusted_cert_list_data_free)(void *data);
#endif
#ifdef NC_ENABLED_SSH
/* ACCESS locked with authkey_lock */
struct {
const char *path;
const char *base64;
NC_SSH_KEY_TYPE type;
const char *username;
} *authkeys;
uint16_t authkey_count;
pthread_mutex_t authkey_lock;
int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *hostkey_data;
void (*hostkey_data_free)(void *data);
#endif
/* ACCESS locked, add/remove endpts/binds - bind_lock + WRITE endpt_lock (strict order!)
* modify endpts - WRITE endpt_lock
* access endpts - READ endpt_lock
* modify/poll binds - bind_lock */
struct nc_bind *binds;
pthread_mutex_t bind_lock;
struct nc_endpt {
const char *name;
NC_TRANSPORT_IMPL ti;
struct nc_keepalives ka;
union {
#ifdef NC_ENABLED_SSH
struct nc_server_ssh_opts *ssh;
#endif
#ifdef NC_ENABLED_TLS
struct nc_server_tls_opts *tls;
#endif
struct nc_server_unix_opts *unixsock;
} opts;
} *endpts;
uint16_t endpt_count;
pthread_rwlock_t endpt_lock;
/* ACCESS locked, add/remove CH clients - WRITE lock ch_client_lock
* modify CH clients - READ lock ch_client_lock + ch_client_lock */
struct nc_ch_client {
const char *name;
struct nc_ch_endpt {
const char *name;
NC_TRANSPORT_IMPL ti;
const char *address;
uint16_t port;
int sock_pending;
int sock_retries;
struct nc_keepalives ka;
union {
#ifdef NC_ENABLED_SSH
struct nc_server_ssh_opts *ssh;
#endif
#ifdef NC_ENABLED_TLS
struct nc_server_tls_opts *tls;
#endif
} opts;
} *ch_endpts;
uint16_t ch_endpt_count;
NC_CH_CONN_TYPE conn_type;
union {
struct {
uint16_t period;
time_t anchor_time;
uint16_t idle_timeout;
} period;
} conn;
NC_CH_START_WITH start_with;
uint8_t max_attempts;
uint32_t id;
pthread_mutex_t lock;
} *ch_clients;
uint16_t ch_client_count;
pthread_rwlock_t ch_client_lock;
/* Atomic IDs */
ATOMIC_T new_session_id;
ATOMIC_T new_client_id;
};
/**
* Sleep time in usec to wait between nc_recv_notif() calls.
*/
#define NC_CLIENT_NOTIF_THREAD_SLEEP 10000
/**
* Timeout in msec for transport-related data to arrive (ssh_handle_key_exchange(), SSL_accept(), SSL_connect()).
* It can be quite a lot on slow machines (waiting for TLS cert-to-name resolution, ...).
*/
#define NC_TRANSPORT_TIMEOUT 10000
/**
* Timeout in msec for acquiring a lock of a session (used with a condition, so higher numbers could be required
* only in case of extreme concurrency).
*/
#define NC_SESSION_LOCK_TIMEOUT 500
/**
* Timeout in msec for acquiring a lock of a session that is supposed to be freed.
*/
#define NC_SESSION_FREE_LOCK_TIMEOUT 1000
/**
* Timeout in msec for acquiring a lock of a pollsession structure.
*/
#define NC_PS_LOCK_TIMEOUT 200
/**
* Timeout in msec for a thread to wait for its turn to work with a pollsession structure.
*/
#define NC_PS_QUEUE_TIMEOUT 5000
/**
* Time slept in msec if no endpoint was created for a running Call Home client.
*/
#define NC_CH_NO_ENDPT_WAIT 1000
/**
* Time slept in msec after a failed Call Home endpoint session creation.
*/
#define NC_CH_ENDPT_FAIL_WAIT 1000
/**
* Number of sockets kept waiting to be accepted.
*/
#define NC_REVERSE_QUEUE 5
/**
* Timeout for connecting Call Home socket to a client (s).
*/
#define NC_SOCKET_CH_TIMEOUT 5
/**
* Number of retires of connection Call Home socket to a client.
*/
#define NC_SOCKET_CH_RETRIES 5
/**
* @brief Type of the session
*/
typedef enum {
NC_CLIENT, /**< client side */
NC_SERVER /**< server side */
} NC_SIDE;
/**
* @brief Enumeration of the supported NETCONF protocol versions
*/
typedef enum {
NC_VERSION_10 = 0, /**< NETCONF 1.0 - RFC 4741, 4742 */
NC_VERSION_11 = 1 /**< NETCONF 1.1 - RFC 6241, 6242 */
} NC_VERSION;
#define NC_VERSION_10_ENDTAG "]]>]]>"
#define NC_VERSION_10_ENDTAG_LEN 6
/**
* @brief Container to serialize RPC messages
*/
struct nc_msg_cont {
struct ly_in *msg;
NC_MSG_TYPE type; /**< can be either NC_MSG_REPLY or NC_MSG_NOTIF */
struct nc_msg_cont *next;
};
/**
* @brief NETCONF session structure
*/
struct nc_session {
NC_STATUS status; /**< status of the session */
NC_SESSION_TERM_REASON term_reason; /**< reason of termination, if status is NC_STATUS_INVALID */
uint32_t killed_by; /**< session responsible for termination, if term_reason is NC_SESSION_TERM_KILLED */
NC_SIDE side; /**< side of the session: client or server */
/* NETCONF data */
uint32_t id; /**< NETCONF session ID (session-id-type) */
NC_VERSION version; /**< NETCONF protocol version */
/* Transport implementation */
NC_TRANSPORT_IMPL ti_type; /**< transport implementation type to select items from ti union */
pthread_mutex_t *io_lock; /**< input/output lock, note that in case of libssh TI, it will be shared with
other NETCONF sessions on the same SSH session (but different SSH channel) */
union {
struct {
int in; /**< input file descriptor */
int out; /**< output file descriptor */
} fd; /**< NC_TI_FD transport implementation structure */
struct {
int sock; /**< socket file descriptor */
} unixsock; /**< NC_TI_UNIX transport implementation structure */
#ifdef NC_ENABLED_SSH
struct {
ssh_channel channel;
ssh_session session;
struct nc_session *next; /**< pointer to the next NETCONF session on the same
SSH session, but different SSH channel. If no such session exists, it is NULL.
otherwise there is a ring list of the NETCONF sessions */
} libssh;
#endif
#ifdef NC_ENABLED_TLS
SSL *tls;
#endif
} ti; /**< transport implementation data */
const char *username;
const char *host;
uint16_t port;
const char *path; /**< socket path in case of unix socket */
/* other */
struct ly_ctx *ctx; /**< libyang context of the session */
void *data; /**< arbitrary user data */
uint8_t flags; /**< various flags of the session */
#define NC_SESSION_SHAREDCTX 0x01
#define NC_SESSION_CALLHOME 0x02
union {
struct {
/* client side only data */
uint64_t msgid;
char **cpblts; /**< list of server's capabilities on client side */
pthread_mutex_t msgs_lock; /**< lock for the msgs buffer */
struct nc_msg_cont *msgs; /**< queue for messages received of different type than expected */
ATOMIC_T ntf_thread; /**< flag whether notification thread for this session is running or not,
2 means it should quit */
/* client flags */
/* some server modules failed to load so the data from them will be ignored - not use strict flag for parsing */
# define NC_SESSION_CLIENT_NOT_STRICT 0x40
} client;
struct {
/* server side only data */
time_t session_start; /**< real time the session was created */
time_t last_rpc; /**< monotonic time (seconds) the last RPC was received on this session */
int ntf_status; /**< flag (count) whether the session is subscribed to notifications */
pthread_mutex_t rpc_lock; /**< lock indicating RPC processing, this lock is always locked before io_lock!! */
pthread_cond_t rpc_cond; /**< RPC condition (tied with rpc_lock and rpc_inuse) */
int rpc_inuse; /**< variable indicating whether there is RPC being processed or not (tied with
rpc_cond and rpc_lock) */
pthread_mutex_t ch_lock; /**< Call Home thread lock */
pthread_cond_t ch_cond; /**< Call Home thread condition */
/* server flags */
#ifdef NC_ENABLED_SSH
/* SSH session authenticated */
# define NC_SESSION_SSH_AUTHENTICATED 0x04
/* netconf subsystem requested */
# define NC_SESSION_SSH_SUBSYS_NETCONF 0x08
/* new SSH message arrived */
# define NC_SESSION_SSH_NEW_MSG 0x10
/* this session is passed to nc_sshcb_msg() */
# define NC_SESSION_SSH_MSG_CB 0x20
uint16_t ssh_auth_attempts; /**< number of failed SSH authentication attempts */
#endif
#ifdef NC_ENABLED_TLS
X509 *client_cert; /**< TLS client certificate if used for authentication */
#endif
} server;
} opts;
};
enum nc_ps_session_state {
NC_PS_STATE_NONE = 0, /**< session is not being worked with */
NC_PS_STATE_BUSY, /**< session is being polled or communicated on (and locked) */
NC_PS_STATE_INVALID /**< session is invalid and was already returned by another poll */
};
struct nc_ps_session {
struct nc_session *session;
enum nc_ps_session_state state;
};
/* ACCESS locked */
struct nc_pollsession {
struct nc_ps_session **sessions;
uint16_t session_count;
uint16_t last_event_session;
pthread_cond_t cond;
pthread_mutex_t lock;
uint8_t queue[NC_PS_QUEUE_SIZE]; /**< round buffer, queue is empty when queue_len == 0 */
uint8_t queue_begin; /**< queue starts on queue[queue_begin] */
uint8_t queue_len; /**< queue ends on queue[(queue_begin + queue_len - 1) % NC_PS_QUEUE_SIZE] */
};
struct nc_ntf_thread_arg {
struct nc_session *session;
void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
};
void *nc_realloc(void *ptr, size_t size);
struct passwd *nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size);
NC_MSG_TYPE nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op);
int nc_gettimespec_mono(struct timespec *ts);
int nc_gettimespec_real(struct timespec *ts);
int32_t nc_difftimespec(const struct timespec *ts1, const struct timespec *ts2);
void nc_addtimespec(struct timespec *ts, uint32_t msec);
const char *nc_keytype2str(NC_SSH_KEY_TYPE type);
int nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka);
struct nc_session *nc_new_session(NC_SIDE side, int shared_ti);
int nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func);
int nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func);
/**
* @brief Lock IO lock on a session.
*
* @param[in] session Session to lock.
* @param[in] timeout Timeout in msec to use.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return 0 on timeout;
* @return -1 on error.
*/
int nc_session_io_lock(struct nc_session *session, int timeout, const char *func);
/**
* @brief Unlock IO lock on a session.
*
* @param[in] session Session to unlock.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return -1 on error.
*/
int nc_session_io_unlock(struct nc_session *session, const char *func);
/**
* @brief Lock MSGS lock on a session.
*
* @param[in] session Session to lock.
* @param[in,out] timeout Timeout in msec to use. If positive and on successful lock, is updated based on what was elapsed.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return 0 on timeout;
* @return -1 on error.
*/
int nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func);
/**
* @brief Unlock MSGS lock on a session.
*
* @param[in] session Session to unlock.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return -1 on error.
*/
int nc_session_client_msgs_unlock(struct nc_session *session, const char *func);
int nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func);
int nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func);
/**
* @brief Fill libyang context in @p session. Context models are based on the stored session
* capabilities. If the server does not support \<get-schema\>, the models are searched
* for in the directory set using nc_client_schema_searchpath().
*
* @param[in] session Session to create the context for.
* @return 0 on success, 1 on some missing schemas, -1 on error.
*/
int nc_ctx_check_and_fill(struct nc_session *session);
/**
* @brief Perform NETCONF handshake on @p session.
*
* @param[in] session NETCONF session to use.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail
* (server-side only), NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
*/
NC_MSG_TYPE nc_handshake_io(struct nc_session *session);
/**
* @brief Create a socket connection.
*
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect on.
* @param[in] timeout for blocking the connect+select call (-1 for infinite).
* @param[in] ka Keepalives parameters.
* @param[in,out] sock_pending for exchanging the pending socket, if the blocking timeout was != -1
* @param[out] ip_host Optional parameter with string IP address of the connected host.
* @return Connected socket or -1 on error.
*/
int nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host);
/**
* @brief Accept a new socket connection.
*
* @param[in] sock Listening socket.
* @param[in] timeout Timeout in milliseconds.
* @param[out] peer_host Host the new connection was initiated from. Can be NULL.
* @param[out] peer_port Port the new connection is connected on. Can be NULL.
* @return Connected socket with the new connection, -1 on error.
*/
int nc_sock_accept(int sock, int timeout, char **peer_host, uint16_t *peer_port);
/**
* @brief Create a listening socket (AF_INET or AF_INET6).
*
* @param[in] address IP address to listen on.
* @param[in] port Port to listen on.
* @param[in] ka Keepalives parameters.
* @return Listening socket, -1 on error.
*/
int nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka);
/**
* @brief Create a listening socket (AF_UNIX).
*
* @param[in] address UNIX address to listen on.
* @param[in] opts The server options (unix permissions).
* @return Listening socket, -1 on error.
*/
int nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts);
/**
* @brief Accept a new connection on a listening socket.
*
* @param[in] binds Structure with the listening sockets.
* @param[in] bind_count Number of @p binds.
* @param[in] timeout Timeout for accepting.
* @param[out] host Host of the remote peer. Can be NULL.
* @param[out] port Port of the new connection. Can be NULL.
* @param[out] idx Index of the bind that was accepted. Can be NULL.
* @return Accepted socket of the new connection, -1 on error.
*/
int nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx);
/**
* @brief Lock endpoint structures for reading and the specific endpoint.
*
* @param[in] name Name of the endpoint.
* @param[in] ti Expected transport.
* @param[out] idx Index of the endpoint. Optional.
* @return Endpoint structure.
*/
struct nc_endpt *nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx);
/**
* @brief Lock CH client structures for reading and lock the specific client.
*
* @param[in] name Name of the CH client.
* @param[in] endpt_name Endpoint of the CH client.
* @param[in] ti Expected transport.
* @param[out] client_p Pointer to the CH client.
* @return CH endpoint structure.
*/
struct nc_ch_endpt *nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti,
struct nc_ch_client **client_p);
/**
* @brief Unlock CH client strcutures and the specific client.
*
* @param[in] endpt Locked CH client structure.
*/
void nc_server_ch_client_unlock(struct nc_ch_client *client);
/**
* @brief Add a client Call Home bind, listen on it.
*
* @param[in] address Address to bind to.
* @param[in] port Port to bind to.
* @param[in] ti Transport to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti);
/**
* @brief Remove a client Call Home bind, stop listening on it.
*
* @param[in] address Address of the bind. NULL matches any address.
* @param[in] port Port of the bind. 0 matches all ports.
* @param[in] ti Transport of the bind. 0 matches all transports.
* @return 0 on success, -1 on no matches found.
*/
int nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti);
/**
* @brief Connect to a listening NETCONF client using Call Home.
*
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect to.
* @param[in] ti Transport fo the connection.
* @param[out] session New Call Home session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
void nc_init(void);
void nc_destroy(void);
#ifdef NC_ENABLED_SSH
/**
* @brief Accept a server Call Home connection on a socket.
*
* @param[in] sock Socket with a new connection.
* @param[in] host Hostname of the server.
* @param[in] port Port of the server.
* @param[in] ctx Context for the session. Can be NULL.
* @param[in] timeout Transport operations timeout in msec.
* @return New session, NULL on error.
*/
struct nc_session *nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout);
/**
* @brief Establish SSH transport on a socket.
*
* @param[in] session Session structure of the new connection.
* @param[in] sock Socket of the new connection.
* @param[in] timeout Transport operations timeout in msec (not SSH authentication one).
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_ssh_session(struct nc_session *session, int sock, int timeout);
/**
* @brief Callback called when a new SSH message is received.
*
* @param[in] sshsession SSH session the message arrived on.
* @param[in] msg SSH message itself.
* @param[in] data NETCONF session running on @p sshsession.
* @return 0 if the message was handled, 1 if it is left up to libssh.
*/
int nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data);
void nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts);
void nc_client_ssh_destroy_opts(void);
void _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts);
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
struct nc_session *nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout);
/**
* @brief Establish TLS transport on a socket.
*
* @param[in] session Session structure of the new connection.
* @param[in] sock Socket of the new connection.
* @param[in] timeout Transport operations timeout in msec.
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_tls_session(struct nc_session *session, int sock, int timeout);
void nc_server_tls_clear_opts(struct nc_server_tls_opts *opts);
void nc_client_tls_destroy_opts(void);
void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts);
#endif /* NC_ENABLED_TLS */
/**
* Functions
* - io.c
*/
/**
* @brief Read message from the wire.
*
* Accepts hello, rpc, rpc-reply and notification. Received string is transformed into
* libyang XML tree and the message type is detected from the top level element.
*
* @param[in] session NETCONF session from which the message is being read.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[out] msg Input handled with the NETCONF message (application layer data).
* @return 1 on success.
* @return 0 on timeout.
* @return -1 on error.
* @return -2 on malformed message error.
*/
int nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg);
/**
* @brief Read a message from the wire.
*
* @param[in] session NETCONF session from which the message is being read.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[out] msg Input handled with the NETCONF message (application layer data).
* @param[in] passing_io_lock True if @p session IO lock is already held. This function always unlocks
* it before returning!
* @return 1 on success.
* @return 0 on timeout.
* @return -1 on error.
* @return -2 on malformed message error.
*/
int nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, int passing_io_lock);
/**
* @brief Write message into wire.
*
* @param[in] session NETCONF session to which the message will be written.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[in] type The type of the message to write, specified as #NC_MSG_TYPE value. According to the type, the
* specific additional parameters are required or accepted:
* - #NC_MSG_RPC
* - `struct lyd_node *op;` - operation (content of the \<rpc/\> to be sent. Required parameter.
* - `const char *attrs;` - additional attributes to be added into the \<rpc/\> element. Required parameter.
* - #NC_MSG_REPLY
* - `struct lyd_node_opaq *rpc_envp;` - parsed envelopes of the RPC to reply to. Required parameter.
* - `struct nc_server_reply *reply;` - RPC reply. Required parameter.
* - #NC_MSG_NOTIF
* - `struct nc_server_notif *notif;` - notification object. Required parameter.
* - #NC_MSG_HELLO
* - `const char **capabs;` - capabilities array ended with NULL. Required parameter.
* - `uint32_t *sid;` - session ID to be included in the hello message. Optional parameter.
*
* @return Type of the written message. #NC_MSG_WOULDBLOCK is returned if timeout is positive
* (or zero) value and IO lock could not be acquired in that time. #NC_MSG_ERROR is
* returned on error and #NC_MSG_NONE is never returned by this function.
*/
NC_MSG_TYPE nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...);
/**
* @brief Check whether a session is still connected (on transport layer).
*
* @param[in] session Session to check.
* @return 1 if connected, 0 if not.
*/
int nc_session_is_connected(struct nc_session *session);
#endif /* NC_SESSION_PRIVATE_H_ */

3665
src/session_server.c Normal file

File diff suppressed because it is too large Load diff

930
src/session_server.h Normal file
View file

@ -0,0 +1,930 @@
/**
* @file session_server.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session server manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_SERVER_H_
#define NC_SESSION_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#ifdef NC_ENABLED_TLS
# include <openssl/x509.h>
#endif
#ifdef NC_ENABLED_SSH
# include <libssh/callbacks.h>
# include <libssh/libssh.h>
# include <libssh/server.h>
#endif
#include "netconf.h"
#include "session.h"
/**
* @defgroup server_session Server Session
* @ingroup server
*
* @brief Server-side NETCONF session manipulation.
* @{
*/
/**
* @brief Prototype of callbacks that are called if some RPCs are received.
*
* If @p session termination reason is changed in the callback, one last reply
* is sent and then the session is considered invalid.
*
* The callback is set via nc_set_global_rpc_clb().
*
* @param[in] rpc Parsed client RPC request.
* @param[in] session Session the RPC arrived on.
* @return Server reply. If NULL, an operation-failed error will be sent to the client.
*/
typedef struct nc_server_reply *(*nc_rpc_clb)(struct lyd_node *rpc, struct nc_session *session);
/**
* @brief Set the termination reason for a session. Use only in #nc_rpc_clb callbacks.
*
* @param[in] session Session to modify.
* @param[in] reason Reason of termination.
*/
void nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason);
/**
* @brief Set the session-id of the session responsible for this session's termination.
*
* @param[in] session Session to modify. Must have term_reason set to #NC_SESSION_TERM_KILLED.
* @param[in] sid SID of the killing session.
*/
void nc_session_set_killed_by(struct nc_session *session, uint32_t sid);
/**
* @brief Set the status of a session.
*
* @param[in] session Session to modify.
* @param[in] status Status of the session.
*/
void nc_session_set_status(struct nc_session *session, NC_STATUS status);
/**
* @brief Set a global nc_rpc_clb that is called if the particular RPC request is
* received and the private field in the corresponding RPC schema node is NULL.
*
* @param[in] clb An user-defined nc_rpc_clb function callback, NULL to default.
*/
void nc_set_global_rpc_clb(nc_rpc_clb clb);
/** @} Server Session */
/**
* @addtogroup server
* @{
*/
/**
* @brief Initialize libssh and/or libssl/libcrypto and the server using a libyang context.
*
* The context is not modified internally, only its dictionary is used for holding
* all the strings, which is thread-safe. Reading models is considered thread-safe
* as models cannot be removed and are rarely modified (augments or deviations).
*
* If the RPC callbacks on schema nodes (mentioned in @ref howtoserver) are modified after
* server initialization with that particular context, they will be called (changes
* will take effect). However, there could be race conditions as the access to
* these callbacks is not thread-safe.
*
* Server capabilities are generated based on its content. Changing the context
* in ways that result in changed capabilities (adding models, changing features)
* is discouraged after sessions are established as it is not possible to change
* capabilities of a session.
*
* This context can safely be destroyed only after calling the last libnetconf2
* function in an application.
*
* Supported RPCs of models in the context are expected to have their callback
* in the corresponding RPC schema node set to a nc_rpc_clb function callback using nc_set_rpc_callback().
* This callback is called by nc_ps_poll() if the particular RPC request is
* received. Callbacks for ietf-netconf:get-schema (supporting YANG and YIN format
* only) and ietf-netconf:close-session are set internally if left unset.
*
* @param[in] ctx Core NETCONF server context.
* @return 0 on success, -1 on error.
*/
int nc_server_init(struct ly_ctx *ctx);
/**
* @brief Destroy any dynamically allocated libssh and/or libssl/libcrypto and
* server resources.
*/
void nc_server_destroy(void);
/**
* @brief Set the with-defaults capability extra parameters.
*
* For the capability to be actually advertised, the server context must also
* include the ietf-netconf-with-defaults model.
*
* Changing this option has the same ill effects as changing capabilities while
* sessions are already established.
*
* @param[in] basic_mode basic-mode with-defaults parameter.
* @param[in] also_supported NC_WD_MODE bit array, also-supported with-defaults
* parameter.
* @return 0 on success, -1 on error.
*/
int nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported);
/**
* @brief Get with-defaults capability extra parameters.
*
* At least one argument must be non-NULL.
*
* @param[in,out] basic_mode basic-mode parameter.
* @param[in,out] also_supported also-supported parameter.
*/
void nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported);
/**
* @brief Set capability of the server.
*
* Capability can be used when some behavior or extension of the server is not defined
* as a YANG module. The provided value will be advertised in the server's \<hello\>
* messages. Note, that libnetconf only checks that the provided value is non-empty
* string.
*
* @param[in] value Capability string to be advertised in server's \<hello\> messages.
*/
int nc_server_set_capability(const char *value);
/**
* @brief Set the callback for getting yang-library capability identifier. If none is set, libyang context change count is used.
*
* @param[in] content_id_clb Callback that should return the yang-library content identifier.
* @param[in] user_data Optional arbitrary user data that will be passed to @p content_id_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
void (*free_user_data)(void *user_data));
/**
* @brief Set server timeout for receiving a hello message.
*
* @param[in] hello_timeout Hello message timeout. 0 for infinite waiting.
*/
void nc_server_set_hello_timeout(uint16_t hello_timeout);
/**
* @brief get server timeout for receiving a hello message.
*
* @return Hello message timeout, 0 is infinite.
*/
uint16_t nc_server_get_hello_timeout(void);
/**
* @brief Set server timeout for dropping an idle session.
*
* @param[in] idle_timeout Idle session timeout. 0 to never drop a session
* because of inactivity.
*/
void nc_server_set_idle_timeout(uint16_t idle_timeout);
/**
* @brief Get server timeout for dropping an idle session.
*
* @return Idle session timeout, 0 for for never dropping
* a session because of inactivity.
*/
uint16_t nc_server_get_idle_timeout(void);
/**
* @brief Get all the server capabilities including all the schemas.
*
* A few capabilities (with-defaults, interleave) depend on the current
* server options.
*
* @param[in] ctx Context to read most capabilities from.
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
*/
const char **nc_server_get_cpblts(struct ly_ctx *ctx);
/**
* @brief Get the server capabilities including the schemas with the specified YANG version.
*
* A few capabilities (with-defaults, interleave) depend on the current
* server options.
*
* @param[in] ctx Context to read most capabilities from.
* @param[in] version YANG version of the schemas to be included in result, with
* LYS_VERSION_UNDEF the result is the same as from nc_server_get_cpblts().
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
*/
const char **nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version);
/** @} Server */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Accept a new session on a pre-established transport session.
*
* @param[in] fdin File descriptor to read (unencrypted) XML data from.
* @param[in] fdout File descriptor to write (unencrypted) XML data to.
* @param[in] username NETCONF username as provided by the transport protocol.
* @param[out] session New session on success.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
/**
* @brief Create an empty structure for polling sessions.
*
* @return Empty pollsession structure, NULL on error.
*/
struct nc_pollsession *nc_ps_new(void);
/**
* @brief Free a pollsession structure.
*
* !IMPORTANT! Make sure that @p ps is not accessible (is not used)
* by any thread before and after this call!
*
* @param[in] ps Pollsession structure to free.
*/
void nc_ps_free(struct nc_pollsession *ps);
/**
* @brief Add a session to a pollsession structure.
*
* @param[in] ps Pollsession structure to modify.
* @param[in] session Session to add to @p ps.
* @return 0 on success, -1 on error.
*/
int nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session);
/**
* @brief Remove a session from a pollsession structure.
*
* @param[in] ps Pollsession structure to modify.
* @param[in] session Session to remove from @p ps.
* @return 0 on success, -1 on not found.
*/
int nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session);
/**
* @brief Get a session from a pollsession structure matching the session ID.
*
* @param[in] ps Pollsession structure to read from.
* @param[in] idx Index of the session.
* @return Session on index, NULL if out-of-bounds.
*/
struct nc_session *nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx);
/**
* @brief Learn the number of sessions in a pollsession structure.
*
* Does not lock @p ps structure for efficiency.
*
* @param[in] ps Pollsession structure to check.
* @return Number of sessions (even invalid ones) in @p ps, -1 on error.
*/
uint16_t nc_ps_session_count(struct nc_pollsession *ps);
#define NC_PSPOLL_NOSESSIONS 0x0001 /**< No sessions to poll. */
#define NC_PSPOLL_TIMEOUT 0x0002 /**< Timeout elapsed. */
#define NC_PSPOLL_RPC 0x0004 /**< RPC was correctly parsed and processed. */
#define NC_PSPOLL_BAD_RPC 0x0008 /**< RPC was received, but failed to be parsed. */
#define NC_PSPOLL_REPLY_ERROR 0x0010 /**< Response to the RPC was a \<rpc-reply\> of type error. */
#define NC_PSPOLL_SESSION_TERM 0x0020 /**< Some session was terminated. */
#define NC_PSPOLL_SESSION_ERROR 0x0040 /**< Some session was terminated incorrectly (not by a \<close-session\> or \<kill-session\> RPC). */
#define NC_PSPOLL_ERROR 0x0080 /**< Other fatal errors (they are printed). */
#ifdef NC_ENABLED_SSH
# define NC_PSPOLL_SSH_MSG 0x00100 /**< SSH message received (and processed, if relevant, only with SSH support). */
# define NC_PSPOLL_SSH_CHANNEL 0x0200 /**< New SSH channel opened on an existing session (only with SSH support). */
#endif
/**
* @brief Poll sessions and process any received RPCs.
*
* Only one event on one session is handled in one function call. If this event
* is a session termination (#NC_PSPOLL_SESSION_TERM returned), the session
* should be removed from @p ps.
*
* @param[in] ps Pollsession structure to use.
* @param[in] timeout Poll timeout in milliseconds. 0 for non-blocking call, -1 for
* infinite waiting.
* @param[in] session Session that was processed and that specific return bits concern.
* Can be NULL.
* @return Bitfield of NC_PSPOLL_* macros.
*/
int nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session);
/**
* @brief Remove sessions from a pollsession structure and
* call nc_session_free() on them.
*
* Calling this function with @p all false makes sense if nc_ps_poll() returned #NC_PSPOLL_SESSION_TERM.
*
* @param[in] ps Pollsession structure to clear.
* @param[in] all Whether to free all sessions, or only the invalid ones.
* @param[in] data_free Session user data destructor.
*/
void nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *));
/** @} Server Session */
/**
* @addtogroup server
* @{
*/
/**
* @brief Add a new endpoint.
*
* Before the endpoint can accept any connections, its address and port must
* be set via nc_server_endpt_set_address() and nc_server_endpt_set_port().
*
* @param[in] name Arbitrary unique endpoint name.
* @param[in] ti Transport protocol to use.
* @return 0 on success, -1 on error.
*/
int nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti);
/**
* @brief Stop listening on and remove an endpoint.
*
* @param[in] name Endpoint name. NULL matches all endpoints.
* @param[in] ti Endpoint transport protocol. NULL matches any protocol.
* Redundant to set if @p name is set, endpoint names are
* unique disregarding their protocol.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti);
/**
* @brief Get the number of currently configured listening endpoints.
* Note that an ednpoint without address and/or port will be included
* even though it is not, in fact, listening.
*
* @return Number of added listening endpoints.
*/
int nc_server_endpt_count(void);
/**
* @brief Check if an endpoint exists.
*
* @param[in] name Endpoint name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_is_endpt(const char *name);
/**
* @brief Change endpoint listening address.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] address New listening address.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_address(const char *endpt_name, const char *address);
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @brief Change endpoint listening port.
*
* This is only valid on SSH/TLS transport endpoint.
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] port New listening port.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_port(const char *endpt_name, uint16_t port);
#endif
/**
* @brief Change endpoint permissions.
*
* This is only valid on UNIX transport endpoint.
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] mode New mode, -1 to use default.
* @param[in] uid New uid, -1 to use default.
* @param[in] gid New gid, -1 to use default.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid);
/**
* @brief Change endpoint keepalives state. Affects only new connections.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] enable Whether to enable or disable keepalives.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_enable_keepalives(const char *endpt_name, int enable);
/**
* @brief Change endpoint keepalives parameters. Affects only new connections.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval);
/** @} Server */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Accept new sessions on all the listening endpoints.
*
* Once a new (TCP/IP) conection is established a different (quite long) timeout
* is used for waiting for transport-related data, which means this call can block
* for much longer that @p timeout, but only with slow/faulty/malicious clients.
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
* non-blocking call, -1 for infinite waiting.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_accept(int timeout, struct nc_session **session);
#ifdef NC_ENABLED_SSH
/**
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF @p orig_session.
* Call this function only when nc_ps_poll() returns #NC_PSPOLL_SSH_CHANNEL on @p orig_session.
*
* @param[in] orig_session Session that has a new SSH channel ready.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session);
/**
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF session
* that was polled in @p ps. Call this function only when nc_ps_poll() on @p ps returns #NC_PSPOLL_SSH_CHANNEL.
* The new session is only returned in @p session, it is not added to @p ps.
*
* @param[in] ps Unmodified pollsession structure from the previous nc_ps_poll() call.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
/** @} Server Session */
/**
* @defgroup server_ssh Server SSH
* @ingroup server
*
* @brief Server-side settings for SSH connections.
* @{
*/
/**
* @brief Add an authorized client SSH public key. This public key can be used for
* publickey authentication (for any SSH connection, even Call Home) afterwards.
*
* @param[in] pubkey_base64 Authorized public key binary content encoded in base64.
* @param[in] type Authorized public key SSH type.
* @param[in] username Username that the client with the public key must use.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username);
/**
* @brief Add an authorized client SSH public key. This public key can be used for
* publickey authentication (for any SSH connection, even Call Home) afterwards.
*
* @param[in] pubkey_path Path to the public key.
* @param[in] username Username that the client with the public key must use.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username);
/**
* @brief Remove an authorized client SSH public key.
*
* @param[in] pubkey_path Path to an authorized public key. NULL matches all the keys.
* @param[in] pubkey_base64 Authorized public key content. NULL matches any key.
* @param[in] type Authorized public key type. 0 matches all types.
* @param[in] username Username for an authorized public key. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
const char *username);
/**
* @brief Set the callback for SSH password authentication. If none is set, local system users are used.
*
* @param[in] passwd_auth_clb Callback that should authenticate the user. Username can be directly obtained from @p session.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password,
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for SSH interactive authentication. If none is set, local system users are used.
*
* @param[in] interactive_auth_clb Callback that should authenticate the user.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session,
const ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for SSH public key authentication. If none is set, local system users are used.
*
* @param[in] pubkey_auth_clb Callback that should authenticate the user.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key,
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for retrieving host keys. Any RSA, DSA, and ECDSA keys can be added. However,
* a maximum of one key of each type will be used during SSH authentication, later keys replacing
* the earlier ones.
*
* @param[in] hostkey_clb Callback that should return the key itself. Zero return indicates success, non-zero
* an error. On success exactly ONE of @p privkey_path or @p privkey_data is expected
* to be set. The one set will be freed.
* - @p privkey_path expects a PEM file,
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
* - @p privkey_type type of the key in @p privkey_data. Use ::NC_SSH_KEY_UNKNOWN for
* PKCS#8 key that includes the information about the key in its data.
* @param[in] user_data Optional arbitrary user data that will be passed to @p hostkey_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add endpoint SSH host keys the server will identify itself with. Only the name is set, the key itself
* wil be retrieved using a callback.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitrary name of the host key.
* @param[in] idx Optional index where to add the key. -1 adds at the end.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Delete endpoint SSH host key. Their order is preserved.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Move endpoint SSH host key.
*
* @param[in] endpt_name Exisitng endpoint name.
* @param[in] key_mov Name of the host key that will be moved.
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after);
/**
* @brief Modify endpoint SSH host key.
*
* @param[in] endpt_name Exisitng endpoint name.
* @param[in] name Name of an existing host key.
* @param[in] new_name New name of the host key @p name.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name);
/**
* @brief Set endpoint accepted SSH authentication methods. All (publickey, password, interactive)
* are supported by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods);
/**
* @brief Get endpoint accepted SSH authentication methods.
*
* @param[in] endpt_name Existing endpoint name.
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
*/
int nc_server_ssh_endpt_get_auth_methods(const char *endpt_name);
/**
* @brief Set endpoint SSH authentication attempts of every client. 3 by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts);
/**
* @brief Set endpoint SSH authentication timeout. 30 seconds by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout);
/** @} Server SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup server_tls Server TLS
* @ingroup server
*
* @brief Server-side settings for TLS connections.
* @{
*/
/**
* @brief Set the server TLS certificate. Only the name is set, the certificate itself
* wil be retrieved using a callback.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitrary certificate name.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name);
/**
* @brief Set the callback for retrieving server certificate and matching private key.
*
* @param[in] cert_clb Callback that should return the certificate and the key itself. Zero return indicates success,
* non-zero an error. On success exactly ONE of @p cert_path or @p cert_data and ONE of
* @p privkey_path and @p privkey_data is expected to be set. Those set will be freed.
* - @p cert_path expects a PEM file,
* - @p cert_data expects a base-64 encoded ASN.1 DER data,
* - @p privkey_path expects a PEM file,
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
* - @p privkey_type type of the key in @p privkey_data.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for retrieving server certificate chain
*
* @param[in] cert_chain_clb Callback that should return all the certificates of the chain. Zero return indicates success,
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
* NULL. Both will be (deeply) freed.
* - @p cert_paths expect an array of PEM files,
* - @p cert_path_count number of @p cert_paths array members,
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
* - @p cert_data_count number of @p cert_data array members.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add a trusted certificate list. Can be both a CA or a client one. Can be
* safely used together with nc_server_tls_endpt_set_trusted_ca_paths().
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitary name identifying this certificate list.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name);
/**
* @brief Set the callback for retrieving trusted certificates.
*
* @param[in] cert_list_clb Callback that should return all the certificates of a list. Zero return indicates success,
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
* NULL. Both will be (deeply) freed.
* - @p cert_paths expect an array of PEM files,
* - @p cert_path_count number of @p cert_paths array members,
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
* - @p cert_data_count number of @p cert_data array members.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Remove a trusted certificate.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
* @return 0 on success, -1 on not found.
*/
int nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name);
/**
* @brief Set trusted Certificate Authority certificate locations. There can only be
* one file and one directory, they are replaced if already set. Can be safely
* used with nc_server_tls_endpt_add_trusted_cert() or its _path variant.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] ca_file Path to a trusted CA cert store file in PEM format. Can be NULL.
* @param[in] ca_dir Path to a trusted CA cert store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir);
/**
* @brief Set Certificate Revocation List locations. There can only be one file
* and one directory, they are replaced if already set.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir);
/**
* @brief Destroy and clean CRLs. Certificates, private keys, and CTN entries are
* not affected.
*
* @param[in] endpt_name Existing endpoint name.
*/
void nc_server_tls_endpt_clear_crls(const char *endpt_name);
/**
* @brief Add a cert-to-name entry.
*
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
* @p fingerprint, @p map_type, and optionally @p name spearately.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
* is modified.
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint,
NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Remove a cert-to-name entry.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] id Priority of the entry. -1 matches all the priorities.
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
* @param[in] name Specific username for the entry. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint,
NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Get a cert-to-name entry.
*
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
* it is filled and returned. If the value is set, it is used as a filter.
* Returns first matching entry.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in,out] id Priority of the entry.
* @param[in,out] fingerprint Fingerprint fo the entry.
* @param[in,out] map_type Mapping type of the entry.
* @param[in,out] name Specific username for the entry.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type,
char **name);
/**
* @brief Get client certificate.
*
* @param[in] session Session to get the information from.
* @return Const session client certificate.
*/
const X509 *nc_session_get_client_cert(const struct nc_session *session);
/**
* @brief Set TLS authentication additional verify callback.
*
* Server will always perform cert-to-name based on its configuration. Only after it passes
* and this callback is set, it is also called. It should return exactly what OpenSSL
* verify callback meaning 1 for success, 0 to deny the user.
*
* @param[in] verify_clb Additional user verify callback.
*/
void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session));
/** @} Server TLS */
#endif /* NC_ENABLED_TLS */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Get session start time.
*
* @param[in] session Session to get the information from.
* @return Session start time.
*/
time_t nc_session_get_start_time(const struct nc_session *session);
/**
* @brief Increase session notification subscription flag count.
* Supports multiple subscriptions on one session.
*
* It is used only to ignore timeouts, because they are
* ignored for sessions with active subscriptions.
*
* @param[in] session Session to modify.
*/
void nc_session_inc_notif_status(struct nc_session *session);
/**
* @brief Decrease session notification subscription flag count.
* Supports multiple subscriptions on one session.
*
* @param[in] session Session to modify.
*/
void nc_session_dec_notif_status(struct nc_session *session);
/**
* @brief Get session notification subscription flag.
*
* @param[in] session Session to get the information from.
* @return 0 for no active subscription, non-zero for an active subscription.
*/
int nc_session_get_notif_status(const struct nc_session *session);
/**
* @brief Learn whether a session was created using Call Home or not.
* Works only for server sessions.
*
* @param[in] session Session to get the information from.
* @return 0 if a standard session, non-zero if a Call Home session.
*/
int nc_session_is_callhome(const struct nc_session *session);
/** @} Server Session */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_SERVER_H_ */

436
src/session_server_ch.h Normal file
View file

@ -0,0 +1,436 @@
/**
* @file session_server_ch.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 Call Home session server manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_SERVER_CH_H_
#define NC_SESSION_SERVER_CH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#include "netconf.h"
#include "session.h"
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @defgroup server_ch Server-side Call Home
* @ingroup server
*
* @brief Call Home functionality for server-side applications.
* @{
*/
/**
* @brief Add a new Call Home client.
*
* @param[in] name Arbitrary unique client name.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_add_client(const char *name);
/**
* @brief Drop any connections, stop connecting and remove a client.
*
* @param[in] name Client name. NULL matches all the clients.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_ch_del_client(const char *name);
/**
* @brief Check if a Call Home client exists.
*
* @param[in] name Client name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_ch_is_client(const char *name);
/**
* @brief Add a new Call Home client endpoint.
*
* @param[in] client_name Existing client name.
* @param[in] endpt_name Arbitrary unique (within the client) endpoint name.
* @param[in] ti Transport protocol to use.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
/**
* @brief Remove a Call Home client endpoint.
*
* @param[in] client_name Existing client name.
* @param[in] endpt_name Existing endpoint of @p client_name. NULL matches all endpoints.
* @param[in] ti Client transport protocol. NULL matches any protocol.
* Redundant to set if @p endpt_name is set, client names are
* unique disregarding their protocol.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
/**
* @brief Check if an endpoint of a Call Home client exists.
*
* @param[in] client_name Client name.
* @param[in] endpt_name Endpoint name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name);
/**
* @brief Change Call Home client endpoint listening address.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] address New listening address.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address);
/**
* @brief Change Call Home client endpoint listening port.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] port New listening port.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port);
/**
* @brief Change Call Home client endpoint keepalives state. Affects only new connections.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] enable Whether to enable or disable keepalives.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable);
/**
* @brief Change Call Home client endpoint keepalives parameters. Affects only new connections.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time,
int max_probes, int probe_interval);
/**
* @brief Set Call Home client connection type.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] conn_type Call Home connection type.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type);
/**
* @brief Set Call Home client periodic connection period for reconnecting.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] period Call Home periodic connection period in minutes.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period);
/**
* @brief Set Call Home client periodic connection period anchor time.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] anchor_time Call Home periodic connection anchor time for the period.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time);
/**
* @brief Set Call Home client periodic connection idle timeout.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] idle_timeout Call Home periodic idle timeout.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
/**
* @brief Set Call Home client start-with policy.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] start_with Call Home client start-with.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with);
/**
* @brief Set Call Home client overall max attempts.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] max_attempts Call Home overall max reconnect attempts.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts);
/**
* @brief Establish a Call Home connection with a listening NETCONF client.
*
* @param[in] client_name Existing client name.
* @param[out] session_clb Function that is called for every established session on the client. @p new_session
* pointer is internally discarded afterwards. If the callback returns non-zero, the @p new_session is freed.
* @return 0 if the thread was successfully created, -1 on error.
*/
int nc_connect_ch_client_dispatch(const char *client_name,
int (*session_clb)(const char *client_name, struct nc_session *new_session));
/** @} Server-side Call Home */
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
/**
* @defgroup server_ch_ssh Server-side Call Home on SSH
* @ingroup server_ch
*
* @brief SSH settings for the Call Home functionality
* @{
*/
/**
* @brief Add Call Home SSH host keys the server will identify itself with. Only the name is set, the key itself
* wil be retrieved using a callback.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitrary name of the host key.
* @param[in] idx Optional index where to add the key. -1 adds at the end.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Delete Call Home SSH host keys. Their order is preserved.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Move Call Home SSH host key.
*
* @param[in] client_name Exisitng Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] key_mov Name of the host key that will be moved.
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
const char *key_after);
/**
* @brief Set accepted Call Home SSH authentication methods. All (publickey, password, interactive)
* are supported by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods);
/**
* @brief Get accepted Call Home SSH authentication methods.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
*/
int nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name);
/**
* @brief Set Call Home SSH authentication attempts of every client. 3 by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts);
/**
* @brief Set Call Home SSH authentication timeout. 30 seconds by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout);
/** @} Server-side Call Home on SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup server_ch_tls Server-side Call Home on TLS
* @ingroup server_ch
*
* @brief TLS settings for the Call Home functionality
* @{
*/
/**
* @brief Set the server Call Home TLS certificate. Only the name is set, the certificate itself
* wil be retrieved using a callback.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitrary certificate name.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Add a Call Home trusted certificate list. Can be both a CA or a client one.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitary name identifying this certificate list.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Remove a set Call Home trusted certificate list. CRLs and CTN entries are not affected.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
* @return 0 on success, -1 on not found.
*/
int nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Set trusted Call Home Certificate Authority certificate locations. There
* can only be one file and one directory, they are replaced if already set.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] ca_file Path to a trusted CA cert store file in PEM format.
* Can be NULL.
* @param[in] ca_dir Path to a trusted CA cert store hashed directory
* (c_rehash utility can be used to create hashes)
* with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file,
const char *ca_dir);
/**
* @brief Set Call Home Certificate Revocation List locations. There can only be
* one file and one directory, they are replaced if already set.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file,
const char *crl_dir);
/**
* @brief Destroy and clean Call Home CRLs. Call Home certificates, private keys,
* and CTN entries are not affected.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
*/
void nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name);
/**
* @brief Add a cert-to-name entry.
*
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
* @p fingerprint, @p map_type, and optionally @p name spearately.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
* is modified.
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id,
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Remove a Call Home cert-to-name entry.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] id Priority of the entry. -1 matches all the priorities.
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
* @param[in] name Specific username for the entry. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id,
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Get a Call Home cert-to-name entry.
*
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
* it is filled and returned. If the value is set, it is used as a filter.
* Returns first matching entry.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in,out] id Priority of the entry.
* @param[in,out] fingerprint Fingerprint fo the entry.
* @param[in,out] map_type Mapping type of the entry.
* @param[in,out] name Specific username for the entry.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_ch_client_endpt_get_ctn(const char *client_name, const char *endpt_name, uint32_t *id, char **fingerprint,
NC_TLS_CTN_MAPTYPE *map_type, char **name);
/** @} Server-side Call Home on TLS */
#endif /* NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_SERVER_CH_H_ */

1709
src/session_server_ssh.c Normal file

File diff suppressed because it is too large Load diff

2005
src/session_server_tls.c Normal file

File diff suppressed because it is too large Load diff

78
tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,78 @@
# headers test for including compat.h
add_test(NAME headers
COMMAND ${CMAKE_SOURCE_DIR}/compat/check_includes.sh ${CMAKE_SOURCE_DIR}/src/)
# format
if (${SOURCE_FORMAT_ENABLED})
add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND make format-check)
endif()
# list of all the tests in each directory
set(tests test_io test_fd_comm test_init_destroy_client test_init_destroy_server test_client_thread test_thread_messages)
set(client_tests test_client test_client_messages)
# add -Wl,--wrap flags
set(test test_client_ssh)
set(${test}_mock_funcs connect ssh_connect ssh_userauth_none ssh_userauth_kbdint ssh_is_connected
ssh_channel_open_session ssh_channel_request_subsystem ssh_channel_is_close ssh_channel_write
ssh_channel_poll_timeout ssh_userauth_password nc_handshake_io nc_ctx_check_and_fill
ssh_userauth_try_publickey ssh_userauth_publickey nc_sock_listen_inet nc_sock_accept_binds nc_accept_callhome_ssh_sock)
set(${test}_wrap_link_flags "-Wl")
foreach(mock_func IN LISTS ${test}_mock_funcs)
set(${test}_wrap_link_flags "${${test}_wrap_link_flags},--wrap=${mock_func}")
endforeach()
set(test test_client_tls)
set(${test}_mock_funcs connect SSL_connect nc_send_hello_io nc_handshake_io nc_ctx_check_and_fill)
set(${test}_wrap_link_flags "-Wl")
foreach(mock_func IN LISTS ${test}_mock_funcs)
set(${test}_wrap_link_flags "${${test}_wrap_link_flags},--wrap=${mock_func}")
endforeach()
#append tests depending on SSH/TLS
if(ENABLE_SSH OR ENABLE_TLS)
list(APPEND tests test_server_thread)
if(ENABLE_SSH)
list(APPEND client_tests test_client_ssh)
endif()
if(ENABLE_TLS)
list(APPEND client_tests test_client_tls)
endif()
endif()
foreach(src IN LISTS libsrc)
list(APPEND test_srcs "../${src}")
endforeach()
add_library(testobj OBJECT ${test_srcs})
foreach(test_name IN LISTS tests)
add_executable(${test_name} $<TARGET_OBJECTS:testobj> ${test_name}.c)
target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2)
target_include_directories(${test_name} PRIVATE ${CMOCKA_INCLUDE_DIR})
set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${${test_name}_wrap_link_flags}")
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
endforeach()
foreach(test_name IN LISTS client_tests)
add_executable(${test_name} $<TARGET_OBJECTS:testobj> ./client/${test_name}.c)
target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2)
target_include_directories(${test_name} PRIVATE ${CMOCKA_INCLUDE_DIR})
set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${${test_name}_wrap_link_flags}")
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
endforeach()
if(ENABLE_VALGRIND_TESTS)
foreach(test_name IN LISTS tests)
add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1
--suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp ${CMAKE_BINARY_DIR}/tests/${test_name})
endforeach()
foreach(test_name IN LISTS client_tests)
add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1
--suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp ${CMAKE_BINARY_DIR}/tests/${test_name})
endforeach()
endif()
include_directories(${CMAKE_SOURCE_DIR}/src ${PROJECT_BINARY_DIR})
configure_file("${PROJECT_SOURCE_DIR}/tests/config.h.in" "${PROJECT_BINARY_DIR}/tests/config.h" ESCAPE_QUOTES @ONLY)

125
tests/client/test_client.c Normal file
View file

@ -0,0 +1,125 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_f(void **state)
{
(void)state;
nc_verbosity(NC_VERB_VERBOSE);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
static void
test_nc_client_setting_schema_searchpath(void **state)
{
(void)state;
const char *path;
int ret;
/* initiate client */
nc_client_init();
path = nc_client_get_schema_searchpath();
assert_null(path);
ret = nc_client_set_schema_searchpath("path");
assert_int_equal(ret, 0);
path = nc_client_get_schema_searchpath();
assert_string_equal(path, "path");
ret = nc_client_set_schema_searchpath("path1");
assert_int_equal(ret, 0);
path = nc_client_get_schema_searchpath();
assert_string_equal(path, "path1");
}
LY_ERR
test_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data))
{
(void)mod_name;
(void)mod_rev;
(void)submod_name;
(void)sub_rev;
(void)user_data;
(void)format;
(void)model_data;
(void)free_module_data;
return LY_SUCCESS;
}
LY_ERR
test_clb1(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data))
{
(void)mod_name;
(void)mod_rev;
(void)submod_name;
(void)sub_rev;
(void)user_data;
(void)format;
(void)model_data;
(void)free_module_data;
return LY_SUCCESS;
}
static void
test_nc_client_setting_schema_callback(void **state)
{
(void)state;
ly_module_imp_clb ret_f;
char *data_ret;
int ret;
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_null(ret_f);
assert_null(data_ret);
ret = nc_client_set_schema_callback(test_clb, "DATA");
assert_int_equal(ret, 0);
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_ptr_equal(test_clb, ret_f);
assert_string_equal("DATA", data_ret);
ret = nc_client_set_schema_callback(test_clb1, "DATA1");
assert_int_equal(ret, 0);
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_ptr_equal(test_clb1, ret_f);
assert_string_equal("DATA1", data_ret);
/* destroy client */
nc_client_destroy();
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_searchpath, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_callback, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -0,0 +1,709 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <messages_p.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_f(void **state)
{
(void)state;
nc_verbosity(NC_VERB_VERBOSE);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
static void
test_nc_rpc_act_generic_xml(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
struct nc_rpc_act_generic *generic_rpc = NULL;
/* create generic rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_act_generic_xml("xml", NC_PARAMTYPE_CONST);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_ACT_GENERIC);
generic_rpc = (struct nc_rpc_act_generic *)rpc;
assert_int_equal(generic_rpc->type, NC_RPC_ACT_GENERIC);
assert_int_equal(generic_rpc->has_data, 0);
assert_string_equal(generic_rpc->content.xml_str, "xml");
nc_rpc_free(rpc);
/* create generic rpc with NC_PARAMTYPE_FREE */
char *str = strdup("str");
rpc = nc_rpc_act_generic_xml(str, NC_PARAMTYPE_FREE);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_ACT_GENERIC);
generic_rpc = (struct nc_rpc_act_generic *)rpc;
assert_int_equal(generic_rpc->type, NC_RPC_ACT_GENERIC);
assert_int_equal(generic_rpc->has_data, 0);
assert_string_equal(generic_rpc->content.xml_str, str);
nc_rpc_free(rpc);
/* create generic rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_act_generic_xml("xml", NC_PARAMTYPE_DUP_AND_FREE);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_ACT_GENERIC);
generic_rpc = (struct nc_rpc_act_generic *)rpc;
assert_int_equal(generic_rpc->type, NC_RPC_ACT_GENERIC);
assert_int_equal(generic_rpc->has_data, 0);
assert_string_equal(generic_rpc->content.xml_str, "xml");
nc_rpc_free(rpc);
}
static void
test_nc_rpc_act_generic(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
struct nc_rpc_act_generic *generic_rpc = NULL;
struct lyd_node node;
node.next = NULL;
node.prev = &node;
rpc = nc_rpc_act_generic(&node, NC_PARAMTYPE_CONST);
assert_non_null(rpc);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_ACT_GENERIC);
generic_rpc = (struct nc_rpc_act_generic *)rpc;
assert_int_equal(generic_rpc->type, NC_RPC_ACT_GENERIC);
assert_int_equal(generic_rpc->has_data, 1);
assert_ptr_equal(generic_rpc->content.data, &node);
nc_rpc_free(rpc);
}
/* function to check if values of getconfig rpc are set correctly */
void
check_getconfig(struct nc_rpc *rpc, enum NC_DATASTORE_TYPE source, char *filter, NC_WD_MODE wd_mode)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_GETCONFIG);
struct nc_rpc_getconfig *getconfig_rpc = (struct nc_rpc_getconfig *)rpc;
assert_int_equal(getconfig_rpc->type, NC_RPC_GETCONFIG);
assert_int_equal(getconfig_rpc->source, source);
assert_string_equal(getconfig_rpc->filter, filter);
assert_int_equal(getconfig_rpc->wd_mode, wd_mode);
}
static void
test_nc_rpc_getconfig(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create getconfig rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_getconfig(NC_DATASTORE_CANDIDATE, "filter-string", NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_getconfig(rpc, NC_DATASTORE_CANDIDATE, "filter-string", NC_WD_UNKNOWN);
nc_rpc_free(rpc);
/* create getconfig rpc with NC_PARAMTYPE_FREE */
char *filter = strdup("string");
rpc = nc_rpc_getconfig(NC_DATASTORE_CONFIG, filter, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_getconfig(rpc, NC_DATASTORE_CONFIG, filter, NC_WD_EXPLICIT);
nc_rpc_free(rpc);
/* create getconfig rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_getconfig(NC_DATASTORE_RUNNING, "filter", NC_WD_ALL, NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_getconfig(rpc, NC_DATASTORE_RUNNING, "filter", NC_WD_ALL);
nc_rpc_free(rpc);
}
/* function to check if values of edit rpc are set correctly */
void
check_edit(struct nc_rpc *rpc, NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
NC_RPC_EDIT_ERROPT error_opt, const char *edit_content)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_EDIT);
struct nc_rpc_edit *edit_rpc = (struct nc_rpc_edit *)rpc;
assert_int_equal(edit_rpc->type, NC_RPC_EDIT);
assert_int_equal(edit_rpc->target, target);
assert_int_equal(edit_rpc->default_op, default_op);
assert_int_equal(edit_rpc->test_opt, test_opt);
assert_int_equal(edit_rpc->error_opt, error_opt);
assert_string_equal(edit_rpc->edit_cont, edit_content);
}
static void
test_nc_rpc_edit(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create edit rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_edit(NC_DATASTORE_RUNNING, NC_RPC_EDIT_DFLTOP_REPLACE, NC_RPC_EDIT_TESTOPT_TESTSET,
NC_RPC_EDIT_ERROPT_STOP, "url", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_edit(rpc, NC_DATASTORE_RUNNING, NC_RPC_EDIT_DFLTOP_REPLACE,
NC_RPC_EDIT_TESTOPT_TESTSET, NC_RPC_EDIT_ERROPT_STOP, "url");
nc_rpc_free(rpc);
/* create edit rpc with NC_PARAMTYPE_FREE */
char *str = strdup("string");
rpc = nc_rpc_edit(NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE, NC_RPC_EDIT_TESTOPT_SET,
NC_RPC_EDIT_ERROPT_ROLLBACK, str, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_edit(rpc, NC_DATASTORE_CANDIDATE, NC_RPC_EDIT_DFLTOP_MERGE,
NC_RPC_EDIT_TESTOPT_SET, NC_RPC_EDIT_ERROPT_ROLLBACK, str);
nc_rpc_free(rpc);
/* create edit rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_edit(NC_DATASTORE_CONFIG, NC_RPC_EDIT_DFLTOP_NONE, NC_RPC_EDIT_TESTOPT_TEST,
NC_RPC_EDIT_ERROPT_CONTINUE, "url1", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_edit(rpc, NC_DATASTORE_CONFIG, NC_RPC_EDIT_DFLTOP_NONE,
NC_RPC_EDIT_TESTOPT_TEST, NC_RPC_EDIT_ERROPT_CONTINUE, "url1");
nc_rpc_free(rpc);
}
/* function to check if values of copy rpc are set correctly */
void
check_copy(struct nc_rpc *rpc, NC_DATASTORE target, const char *url_trg, NC_DATASTORE source,
const char *url_or_config_src, NC_WD_MODE wd_mode)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_COPY);
struct nc_rpc_copy *copy_rpc = (struct nc_rpc_copy *)rpc;
assert_int_equal(copy_rpc->type, NC_RPC_COPY);
assert_int_equal(copy_rpc->target, target);
assert_string_equal(copy_rpc->url_trg, url_trg);
assert_int_equal(copy_rpc->source, source);
assert_string_equal(copy_rpc->url_config_src, url_or_config_src);
assert_int_equal(copy_rpc->wd_mode, wd_mode);
}
static void
test_nc_rpc_copy(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create copy rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_copy(NC_DATASTORE_RUNNING, "target-url", NC_DATASTORE_RUNNING, "src-url",
NC_WD_ALL, NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_copy(rpc, NC_DATASTORE_RUNNING, "target-url", NC_DATASTORE_RUNNING, "src-url", NC_WD_ALL);
nc_rpc_free(rpc);
/* create copy rpc with NC_PARAMTYPE_FREE */
char *target = strdup("target");
char *src = strdup("src");
rpc = nc_rpc_copy(NC_DATASTORE_STARTUP, target, NC_DATASTORE_RUNNING, src,
NC_WD_ALL_TAG, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_copy(rpc, NC_DATASTORE_STARTUP, target, NC_DATASTORE_RUNNING, src, NC_WD_ALL_TAG);
nc_rpc_free(rpc);
/* create copy rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_copy(NC_DATASTORE_STARTUP, "url", NC_DATASTORE_CANDIDATE, "url",
NC_WD_TRIM, NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_copy(rpc, NC_DATASTORE_STARTUP, "url", NC_DATASTORE_CANDIDATE, "url", NC_WD_TRIM);
nc_rpc_free(rpc);
}
/* function to check if values of delete rpc are set correctly */
void
check_delete(struct nc_rpc *rpc, NC_DATASTORE target, const char *url)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_DELETE);
struct nc_rpc_delete *delete_rpc = (struct nc_rpc_delete *)rpc;
assert_int_equal(delete_rpc->type, NC_RPC_DELETE);
assert_int_equal(delete_rpc->target, target);
assert_string_equal(delete_rpc->url, url);
}
static void
test_nc_rpc_delete(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create delete rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_delete(NC_DATASTORE_RUNNING, "target-url", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_delete(rpc, NC_DATASTORE_RUNNING, "target-url");
nc_rpc_free(rpc);
/* create delete rpc with NC_PARAMTYPE_FREE */
char *url = strdup("url");
rpc = nc_rpc_delete(NC_DATASTORE_CANDIDATE, url, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_delete(rpc, NC_DATASTORE_CANDIDATE, url);
nc_rpc_free(rpc);
/* create delete rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_delete(NC_DATASTORE_CONFIG, "target", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_delete(rpc, NC_DATASTORE_CONFIG, "target");
nc_rpc_free(rpc);
}
static void
test_nc_rpc_lock(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
struct nc_rpc_lock *lock_rpc = NULL;
rpc = nc_rpc_lock(NC_DATASTORE_RUNNING);
assert_non_null(rpc);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_LOCK);
lock_rpc = (struct nc_rpc_lock *)rpc;
assert_int_equal(lock_rpc->type, NC_RPC_LOCK);
assert_int_equal(lock_rpc->target, NC_DATASTORE_RUNNING);
nc_rpc_free(rpc);
}
static void
test_nc_rpc_unlock(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
struct nc_rpc_lock *unlock_rpc = NULL;
rpc = nc_rpc_unlock(NC_DATASTORE_RUNNING);
assert_non_null(rpc);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_UNLOCK);
unlock_rpc = (struct nc_rpc_lock *)rpc;
assert_int_equal(unlock_rpc->type, NC_RPC_UNLOCK);
assert_int_equal(unlock_rpc->target, NC_DATASTORE_RUNNING);
nc_rpc_free(rpc);
}
/* function to check if values of get rpc are set correctly */
void
check_get_rpc(struct nc_rpc *rpc, const char *filter, NC_WD_MODE wd_mode)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_GET);
struct nc_rpc_get *get_rpc = (struct nc_rpc_get *)rpc;
assert_int_equal(get_rpc->type, NC_RPC_GET);
assert_string_equal(get_rpc->filter, filter);
assert_int_equal(get_rpc->wd_mode, wd_mode);
}
static void
test_nc_rpc_get(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create get rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_get("filter", NC_WD_ALL, NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_get_rpc(rpc, "filter", NC_WD_ALL);
nc_rpc_free(rpc);
/* create get rpc with NC_PARAMTYPE_FREE */
char *str = strdup("string");
rpc = nc_rpc_get(str, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_get_rpc(rpc, str, NC_WD_EXPLICIT);
nc_rpc_free(rpc);
/* create get rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_get("filter-string", NC_WD_UNKNOWN, NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_get_rpc(rpc, "filter-string", NC_WD_UNKNOWN);
nc_rpc_free(rpc);
}
static void
test_nc_rpc_kill(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
struct nc_rpc_kill *kill_rpc = NULL;
rpc = nc_rpc_kill(10);
assert_non_null(rpc);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_KILL);
kill_rpc = (struct nc_rpc_kill *)rpc;
assert_int_equal(kill_rpc->type, NC_RPC_KILL);
assert_int_equal(kill_rpc->sid, 10);
nc_rpc_free(rpc);
}
/* function to check if values of commit rpc are set correctly */
void
check_commit_rpc(struct nc_rpc *rpc, int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_COMMIT);
struct nc_rpc_commit *commit_rpc = (struct nc_rpc_commit *)rpc;
assert_int_equal(commit_rpc->type, NC_RPC_COMMIT);
assert_int_equal(commit_rpc->confirmed, confirmed);
assert_int_equal(commit_rpc->confirm_timeout, confirm_timeout);
assert_string_equal(commit_rpc->persist, persist);
assert_string_equal(commit_rpc->persist_id, persist_id);
}
static void
test_nc_rpc_commit(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create commit rpc with NC_PARAMTYPE_CONST*/
rpc = nc_rpc_commit(1, 100, "persist", "persist-id", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_commit_rpc(rpc, 1, 100, "persist", "persist-id");
nc_rpc_free(rpc);
/* create commit rpc with NC_PARAMTYPE_FREE*/
char *str1 = strdup("str1");
char *str2 = strdup("str2");
rpc = nc_rpc_commit(2, 5, str1, str2, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_commit_rpc(rpc, 2, 5, str1, str2);
nc_rpc_free(rpc);
/* create commit rpc with NC_PARAMTYPE_DUP_AND_FREE*/
rpc = nc_rpc_commit(10, 200, "persistent", "persistent-id", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_commit_rpc(rpc, 10, 200, "persistent", "persistent-id");
nc_rpc_free(rpc);
}
static void
test_nc_rpc_discard(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
rpc = nc_rpc_discard();
assert_non_null(rpc);
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_DISCARD);
nc_rpc_free(rpc);
}
/* function to check if values of cancel rpc are set correctly */
void
check_cancel_rpc(struct nc_rpc *rpc, const char *persist_id)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_CANCEL);
struct nc_rpc_cancel *cancel_rpc = (struct nc_rpc_cancel *)rpc;
assert_int_equal(cancel_rpc->type, NC_RPC_CANCEL);
assert_string_equal(cancel_rpc->persist_id, persist_id);
}
static void
test_nc_rpc_cancel(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create cancel rpc with NC_PARAMTYPE_CONST*/
rpc = nc_rpc_cancel("persist-id", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_cancel_rpc(rpc, "persist-id");
nc_rpc_free(rpc);
/* create cancel rpc with NC_PARAMTYPE_FREE*/
char *str = strdup("string");
rpc = nc_rpc_cancel(str, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_cancel_rpc(rpc, str);
nc_rpc_free(rpc);
/* create cancel rpc with NC_PARAMTYPE_DUP_AND_FREE*/
rpc = nc_rpc_cancel("id", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_cancel_rpc(rpc, "id");
nc_rpc_free(rpc);
}
/* function to check if values of validate rpc are set correctly */
void
check_validate_rpc(struct nc_rpc *rpc, NC_DATASTORE source, const char *url_or_config)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_VALIDATE);
struct nc_rpc_validate *validate_rpc = (struct nc_rpc_validate *)rpc;
assert_int_equal(validate_rpc->type, NC_RPC_VALIDATE);
assert_int_equal(validate_rpc->source, source);
assert_string_equal(validate_rpc->url_config_src, url_or_config);
}
static void
test_nc_rpc_validate(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create validate rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_validate(NC_DATASTORE_RUNNING, "url", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_validate_rpc(rpc, NC_DATASTORE_RUNNING, "url");
nc_rpc_free(rpc);
/* create validate rpc with NC_PARAMTYPE_FREE */
char *str = strdup("string");
rpc = nc_rpc_validate(NC_DATASTORE_CANDIDATE, str, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_validate_rpc(rpc, NC_DATASTORE_CANDIDATE, str);
nc_rpc_free(rpc);
/* create validate rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_validate(NC_DATASTORE_CONFIG, "url1", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_validate_rpc(rpc, NC_DATASTORE_CONFIG, "url1");
nc_rpc_free(rpc);
}
/* function to check if values of getschema rpc are set correctly */
void
check_getschema_rpc(struct nc_rpc *rpc, const char *identifier, const char *version, const char *format)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_GETSCHEMA);
struct nc_rpc_getschema *getchema_rpc = (struct nc_rpc_getschema *)rpc;
assert_int_equal(getchema_rpc->type, NC_RPC_GETSCHEMA);
assert_string_equal(getchema_rpc->identifier, identifier);
assert_string_equal(getchema_rpc->version, version);
assert_string_equal(getchema_rpc->format, format);
}
static void
test_nc_rpc_getschema(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create getchema with NC_PARAMTYPE_CONST*/
rpc = nc_rpc_getschema("id", "version", "format", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_getschema_rpc(rpc, "id", "version", "format");
nc_rpc_free(rpc);
/* create getchema with NC_PARAMTYPE_FREE*/
char *str1 = strdup("str1");
char *str2 = strdup("str2");
char *str3 = strdup("str3");
rpc = nc_rpc_getschema(str1, str2, str3, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_getschema_rpc(rpc, str1, str2, str3);
nc_rpc_free(rpc);
/* create getchema with NC_PARAMTYPE_DUP_AND_FREE*/
rpc = nc_rpc_getschema("id1", "version1", "format1", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_getschema_rpc(rpc, "id1", "version1", "format1");
nc_rpc_free(rpc);
}
/* function to check if values of subscribe rpc are set correctly */
void
check_subscribe_rpc(struct nc_rpc *rpc, const char *stream_name, const char *filter,
const char *start_time, const char *stop_time)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_SUBSCRIBE);
struct nc_rpc_subscribe *subscribe_rpc = (struct nc_rpc_subscribe *)rpc;
assert_int_equal(subscribe_rpc->type, NC_RPC_SUBSCRIBE);
assert_string_equal(subscribe_rpc->stream, stream_name);
assert_string_equal(subscribe_rpc->filter, filter);
assert_string_equal(subscribe_rpc->start, start_time);
assert_string_equal(subscribe_rpc->stop, stop_time);
}
static void
test_nc_rpc_subscribe(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create subscribe rpc with NC_PARAMTYPE_CONST*/
rpc = nc_rpc_subscribe("stream-name", "filter", "start-time", "stop-time", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_subscribe_rpc(rpc, "stream-name", "filter", "start-time", "stop-time");
nc_rpc_free(rpc);
/* create subscribe rpc with NC_PARAMTYPE_FREE*/
char *str1 = strdup("str1");
char *str2 = strdup("str2");
char *str3 = strdup("str3");
char *str4 = strdup("str4");
rpc = nc_rpc_subscribe(str1, str2, str3, str4, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_subscribe_rpc(rpc, str1, str2, str3, str4);
nc_rpc_free(rpc);
/* create subscribe rpc with NC_PARAMTYPE_DUP_AND_FREE*/
rpc = nc_rpc_subscribe("name", "filter-str", "start", "stop", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_subscribe_rpc(rpc, "name", "filter-str", "start", "stop");
nc_rpc_free(rpc);
}
/* function to check if values of getdata rpc are set correctly */
void
check_getdata(struct nc_rpc *rpc, char *datastore, const char *filter, const char *config_filter,
char **origin_filter, int origin_filter_count, int negated_origin_filter, uint16_t max_depth,
int with_origin, NC_WD_MODE wd_mode)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_GETDATA);
struct nc_rpc_getdata *rpc_getdata = (struct nc_rpc_getdata *)rpc;
assert_int_equal(rpc_getdata->type, NC_RPC_GETDATA);
assert_string_equal(rpc_getdata->datastore, datastore);
assert_string_equal(rpc_getdata->filter, filter);
assert_string_equal(rpc_getdata->config_filter, config_filter);
assert_string_equal(*rpc_getdata->origin_filter, *origin_filter);
assert_int_equal(rpc_getdata->origin_filter_count, origin_filter_count);
assert_int_equal(rpc_getdata->negated_origin_filter, negated_origin_filter);
assert_int_equal(rpc_getdata->max_depth, max_depth);
assert_int_equal(rpc_getdata->with_origin, with_origin);
assert_int_equal(rpc_getdata->wd_mode, wd_mode);
}
static void
test_nc_rpc_getdata(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create getdata rpc with NC_PARAMTYPE_CONST */
char *origin_filters = "origin_filter";
rpc = nc_rpc_getdata("candidate", "filter", "true", &origin_filters, 1, 1, 3, 1, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_getdata(rpc, "candidate", "filter", "true", &origin_filters, 1, 1, 3, 1, NC_WD_UNKNOWN);
nc_rpc_free(rpc);
/* create getdata rpc with NC_PARAMTYPE_FREE */
char *datastore = strdup("running");
char *filter = strdup("filter");
char *config_filter = strdup("true");
char buf[20] = {0};
char **origin_filter;
int origin_filter_count = 2;
origin_filter = calloc(origin_filter_count, sizeof *origin_filter);
assert_non_null(origin_filter);
for (int i = 0; i < origin_filter_count; i++) {
snprintf(buf, sizeof(buf) - 1, "origin_filter%d", i + 1);
origin_filter[i] = strdup(buf);
}
rpc = nc_rpc_getdata(datastore, filter, config_filter, origin_filter, origin_filter_count, 2, 3, 1, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_getdata(rpc, datastore, filter, config_filter, origin_filter, origin_filter_count, 2, 3, 1, NC_WD_EXPLICIT);
nc_rpc_free(rpc);
/* create getdata rpc with NC_PARAMTYPE_DUP_AND_FREE */
char *origin_filter1 = "origin_filter1";
rpc = nc_rpc_getdata("startup", "filter1", "false", &origin_filter1, 1, 0, 3, 1, NC_WD_ALL, NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_getdata(rpc, "startup", "filter1", "false", &origin_filter1, 1, 0, 3, 1, NC_WD_ALL);
nc_rpc_free(rpc);
}
/* function to check if values of editdata rpc are set correctly */
void
check_editdata(struct nc_rpc *rpc, char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char *edit_content)
{
assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_EDITDATA);
struct nc_rpc_editdata *rpc_editdata = (struct nc_rpc_editdata *)rpc;
assert_int_equal(rpc_editdata->type, NC_RPC_EDITDATA);
assert_string_equal(rpc_editdata->datastore, datastore);
assert_int_equal(rpc_editdata->default_op, default_op);
assert_string_equal(rpc_editdata->edit_cont, edit_content);
}
static void
test_nc_rpc_editdata(void **state)
{
(void)state;
struct nc_rpc *rpc = NULL;
/* create editdata rpc with NC_PARAMTYPE_CONST */
rpc = nc_rpc_editdata("candidate", NC_RPC_EDIT_DFLTOP_UNKNOWN, "edit", NC_PARAMTYPE_CONST);
assert_non_null(rpc);
check_editdata(rpc, "candidate", NC_RPC_EDIT_DFLTOP_UNKNOWN, "edit");
nc_rpc_free(rpc);
/* create editdata rpc with NC_PARAMTYPE_FREE */
char *datastore = strdup("running");
char *edit_cont = strdup("edit_data");
rpc = nc_rpc_editdata(datastore, NC_RPC_EDIT_DFLTOP_MERGE, edit_cont, NC_PARAMTYPE_FREE);
assert_non_null(rpc);
check_editdata(rpc, datastore, NC_RPC_EDIT_DFLTOP_MERGE, edit_cont);
nc_rpc_free(rpc);
/* create editdata rpc with NC_PARAMTYPE_DUP_AND_FREE */
rpc = nc_rpc_editdata("startup", NC_RPC_EDIT_DFLTOP_REPLACE, "edit_cont", NC_PARAMTYPE_DUP_AND_FREE);
assert_non_null(rpc);
check_editdata(rpc, "startup", NC_RPC_EDIT_DFLTOP_REPLACE, "edit_cont");
nc_rpc_free(rpc);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_rpc_act_generic_xml, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_act_generic, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_getconfig, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_edit, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_copy, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_delete, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_lock, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_unlock, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_get, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_kill, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_commit, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_discard, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_cancel, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_validate, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_getschema, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_subscribe, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_getdata, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_rpc_editdata, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -0,0 +1,813 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include <session_client_ch.h>
#include <session_p.h>
#include "tests/config.h"
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
static int
ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
static int
setup_f(void **state)
{
(void)state;
int ret;
nc_verbosity(NC_VERB_VERBOSE);
ret = nc_client_ssh_set_username("username");
assert_int_equal(ret, 0);
ret = nc_client_ssh_ch_set_username("ch_username");
assert_int_equal(ret, 0);
nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
MOCK int
__wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
(void)sockfd;
(void)addr;
(void)addrlen;
return (int)mock();
}
MOCK int
__wrap_ssh_connect(ssh_session session)
{
(void)session;
/* set support of all authentication methods by fake server */
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_none(ssh_session session, const char *username)
{
(void)session;
(void)username;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_kbdint(ssh_session session, const char *user, const char *submethods)
{
(void)session;
(void)user;
(void)submethods;
return (int)mock();
}
MOCK int
__wrap_ssh_is_connected(ssh_session session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_open_session(ssh_channel channel)
{
(void)channel;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_request_subsystem(ssh_channel channel, const char *subsystem)
{
(void)channel;
(void)subsystem;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_is_closed(ssh_channel channel)
{
(void)channel;
return 0;
}
MOCK int
__wrap_ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
{
(void)channel;
(void)data;
return len;
}
MOCK int
__wrap_ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
{
(void)channel;
(void)timeout;
(void)is_stderr;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_password(ssh_session session, const char *username, const char *password)
{
(void)session;
check_expected(password);
check_expected(username);
return (int)mock();
}
MOCK int
__wrap_nc_handshake_io(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_nc_ctx_check_and_fill(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_try_publickey(ssh_session session, const char *username, const ssh_key pubkey)
{
(void)session;
(void)username;
(void)pubkey;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_publickey(ssh_session session, const char *username, const ssh_key privkey)
{
(void)session;
(void)username;
(void)privkey;
return (int)mock();
}
MOCK int
__wrap_nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
{
(void)address;
(void)port;
(void)ka;
return (int)mock();
}
MOCK int
__wrap_nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
{
(void)binds;
(void)bind_count;
(void)timeout;
(void)host;
(void)port;
*idx = 0;
return (int)mock();
}
MOCK struct nc_session *
__wrap_nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
{
(void)sock;
(void)host;
(void)port;
(void)ctx;
(void)timeout;
return mock_ptr_type(struct nc_session *);
}
static int
test_hostkey_clb(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_hostkey_check_clb(void **state)
{
(void)state;
int (*ret_f)(const char *hostname, ssh_session session, void *priv);
char *priv_data_ret;
/* ssh_hostkey_check_clb is set in setup_f */
nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, ssh_hostkey_check_clb);
assert_null(priv_data_ret);
/* set different callback and private data */
nc_client_ssh_set_auth_hostkey_check_clb(test_hostkey_clb, "DATA");
nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_hostkey_clb);
assert_string_equal(priv_data_ret, "DATA");
}
char *
test_pwd_clb1(const char *username, const char *hostname, void *priv)
{
char *pass, *pass_to_return;
check_expected(username);
check_expected(hostname);
check_expected(priv);
pass = (char *)mock();
pass_to_return = malloc(sizeof *pass * (strlen(pass) + 1));
strcpy(pass_to_return, pass);
return pass_to_return;
}
char *
test_pwd_clb2(const char *username, const char *hostname, void *priv)
{
(void)username;
(void)hostname;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_password_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *username, const char *hostname, void *priv);
char *priv_data_ret;
/* set callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb1, "DATA");
nc_client_ssh_get_auth_password_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_pwd_clb1, ret_f);
assert_string_equal("DATA", priv_data_ret);
/* set different callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb2, "NEW DATA");
nc_client_ssh_get_auth_password_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_pwd_clb2, ret_f);
assert_string_equal("NEW DATA", priv_data_ret);
}
char *
test_inter_clb1(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
{
(void)auth_name;
(void)instruction;
(void)prompt;
(void)echo;
(void)priv;
return 0;
}
char *
test_inter_clb2(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
{
(void)auth_name;
(void)instruction;
(void)prompt;
(void)echo;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_interactive_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
char *priv_data_ret;
/* set callback */
nc_client_ssh_set_auth_interactive_clb(test_inter_clb1, "DATA");
nc_client_ssh_get_auth_interactive_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_inter_clb1, ret_f);
assert_string_equal("DATA", priv_data_ret);
/* set diferent callback */
nc_client_ssh_set_auth_interactive_clb(test_inter_clb2, "NEW DATA");
nc_client_ssh_get_auth_interactive_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_inter_clb2, ret_f);
assert_string_equal("NEW DATA", priv_data_ret);
}
char *
test_passphrase_clb1(const char *privkey_path, void *priv)
{
(void)privkey_path;
(void)priv;
return 0;
}
char *
test_passphrase_clb2(const char *privkey_path, void *priv)
{
(void)privkey_path;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_privkey_passphrase_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *privkey_path, void *priv);
char *priv_data_ret;
/* set first callback */
nc_client_ssh_set_auth_privkey_passphrase_clb(test_passphrase_clb1, "DATA");
nc_client_ssh_get_auth_privkey_passphrase_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_passphrase_clb1);
assert_string_equal("DATA", priv_data_ret);
/* set different callback */
nc_client_ssh_set_auth_privkey_passphrase_clb(test_passphrase_clb2, "NEW DATA");
nc_client_ssh_get_auth_privkey_passphrase_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_passphrase_clb2);
assert_string_equal("NEW DATA", priv_data_ret);
}
static void
test_nc_client_ssh_adding_keypair(void **state)
{
(void)state;
int ret;
const char *pubkey1, *pubkey2;
/* at the beginning keypair count should be 0 */
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 0);
/* add first key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_dsa.pub", TESTS_DIR "/data/key_dsa");
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* add second keypair */
ret = nc_client_ssh_add_keypair("key_pub", "key_priv");
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 2);
ret = nc_client_ssh_get_keypair(1, &pubkey1, &pubkey2);
assert_int_equal(ret, 0);
assert_string_equal(pubkey1, "key_pub");
assert_string_equal(pubkey2, "key_priv");
/* delete first keypair */
ret = nc_client_ssh_del_keypair(0);
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* try to get deleted keypair */
ret = nc_client_ssh_get_keypair(5, &pubkey1, &pubkey2);
assert_int_equal(ret, -1);
/* try to add keypair that is already set */
ret = nc_client_ssh_add_keypair("key_pub", "key_priv");
assert_int_equal(ret, -1);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* try to delete keypair with id that is not used */
ret = nc_client_ssh_del_keypair(42);
assert_int_equal(ret, -1);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* remove remaining keypairs */
ret = nc_client_ssh_del_keypair(0);
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 0);
}
static void
test_nc_client_ssh_setting_auth_pref(void **state)
{
(void)state;
int ret;
/* initiate client, must be called in first test */
nc_client_init();
/* check default prefference settings according to documentation */
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE);
assert_int_equal(ret, 3);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD);
assert_int_equal(ret, 2);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY);
assert_int_equal(ret, 1);
/* try to set prefetence of non existing method */
nc_client_ssh_set_auth_pref(42, 22);
/* try to get preference of non existing method */
ret = nc_client_ssh_get_auth_pref(42);
assert_int_equal(ret, 0);
/* change values of all methods and check if they actually changed */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 9);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE);
assert_int_equal(ret, 9);
/* negative value should be set as -1 */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -5);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD);
assert_int_equal(ret, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 11);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY);
assert_int_equal(ret, 11);
}
static void
test_nc_client_ssh_setting_username(void **state)
{
(void)state;
int ret;
const char *username_ret;
username_ret = nc_client_ssh_get_username();
/* username is set to "username" in setup_f */
assert_string_equal(username_ret, "username");
/* set new username and check if it changes */
ret = nc_client_ssh_set_username("new_username");
assert_int_equal(ret, 0);
username_ret = nc_client_ssh_get_username();
assert_string_equal(username_ret, "new_username");
}
static void
test_nc_connect_ssh_interactive_succesfull(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use interactive authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 20);
/* prepare return values for functions used by nc_connect_ssh */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_kbdint, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_ssh_password_succesfull(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use password authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
/* set authentication callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb1, "private_data");
will_return(test_pwd_clb1, "secret password");
/* set values that are expected as parameters for authentication callback */
expect_string(test_pwd_clb1, username, "username");
expect_string(test_pwd_clb1, hostname, "127.0.0.1");
expect_string(test_pwd_clb1, priv, "private_data");
/* fake succesfull connection */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
/* do not authenticate using no authentication method */
will_return(__wrap_ssh_userauth_none, 1);
/* succesfully authenticate via password authentication */
expect_string(__wrap_ssh_userauth_password, password, "secret password");
expect_string(__wrap_ssh_userauth_password, username, "username");
will_return(__wrap_ssh_userauth_password, 0);
/* fake ssh functions that are used to open netconf channel */
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
/* fake that connection is still alive*/
will_return(__wrap_ssh_is_connected, 1);
/* fake ssh function for recieving hello message */
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
/* disconnect */
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_ssh_pubkey_succesfull(void **state)
{
(void)state;
struct nc_session *session;
int ret = 0;
/* set authentication method to use password authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
/* add keypair for authentication */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_dsa.pub", TESTS_DIR "/data/key_dsa");
assert_int_equal(ret, 0);
/* fake succesfull connection */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
/* do not authenticate using no authentication method */
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_try_publickey, 0);
will_return(__wrap_ssh_userauth_publickey, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
/* fake ssh function for recieving hello message */
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
/* disconnect */
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_connection_failed(void **state)
{
(void)state;
struct nc_session *session;
errno = ECONNREFUSED;
will_return(__wrap_connect, -1);
will_return(__wrap_ssh_is_connected, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_null(session);
}
static void
test_nc_connect_ssh_bad_hello(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use interactive authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1);
nc_client_ssh_set_auth_password_clb(test_pwd_clb2, NULL);
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_kbdint, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
will_return(__wrap_nc_handshake_io, 4);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_null(session);
/* destroy client, must be called in last test */
nc_client_destroy();
}
static void
test_nc_client_ssh_ch_setting_username(void **state)
{
(void)state;
const char *username_ret;
int ret;
/* username is set to "ch_username" in setup_f */
username_ret = nc_client_ssh_ch_get_username();
assert_string_equal(username_ret, "ch_username");
/* set new username and check if it changes */
ret = nc_client_ssh_ch_set_username("new_ch_username");
assert_int_equal(ret, 0);
username_ret = nc_client_ssh_ch_get_username();
assert_string_equal(username_ret, "new_ch_username");
}
static void
test_nc_client_ssh_ch_add_bind_listen(void **state)
{
(void)state;
int ret;
/* invalid parameters, address NULL or port 0 */
ret = nc_client_ssh_ch_add_bind_listen(NULL, 4334);
assert_int_equal(ret, -1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 0);
assert_int_equal(ret, -1);
/* failed to create an ssh listening socket */
will_return(__wrap_nc_sock_listen_inet, -1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, -1);
/* fake a successful CH ssh listening socket */
will_return(__wrap_nc_sock_listen_inet, 1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
}
static void
test_nc_accept_callhome(void **state)
{
(void)state;
struct nc_session *session = NULL;
int timeout = 10;
int ret;
/* invalid parameter session */
ret = nc_accept_callhome(timeout, NULL, NULL);
assert_int_equal(ret, -1);
/* no client bind */
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* successfully add a client Call Home bind */
will_return(__wrap_nc_sock_listen_inet, 1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* failed to accept a client bind */
will_return(__wrap_nc_sock_accept_binds, -1);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* failed to accept a server Call Home connection */
will_return(__wrap_nc_accept_callhome_ssh_sock, NULL);
will_return(__wrap_nc_sock_accept_binds, 2);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* create session structure to fake a successful server call home connection */
session = nc_new_session(NC_CLIENT, 0);
assert_non_null(session);
will_return(__wrap_nc_sock_accept_binds, 2);
will_return(__wrap_nc_accept_callhome_ssh_sock, session);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, 1);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* free session */
nc_session_free(session, NULL);
}
static void
test_nc_client_ssh_callhome_successful(void **state)
{
(void)state;
struct nc_session *session = NULL;
int timeout = 10;
int ret;
/* create session structure */
session = nc_new_session(NC_CLIENT, 0);
assert_non_null(session);
/* prepare to fake return values for functions used by nc_accept_callhome */
will_return(__wrap_nc_sock_listen_inet, 1);
will_return(__wrap_nc_sock_accept_binds, 2);
will_return(__wrap_nc_accept_callhome_ssh_sock, session);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, 1);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* free session */
nc_session_free(session, NULL);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_pref, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_hostkey_check_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_password_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_interactive_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_privkey_passphrase_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_adding_keypair, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_username, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_interactive_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_password_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_pubkey_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_connection_failed, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_bad_hello, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_ch_setting_username, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_ch_add_bind_listen, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_accept_callhome, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_callhome_successful, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -0,0 +1,200 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_f(void **state)
{
(void)state;
nc_verbosity(NC_VERB_VERBOSE);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
MOCK int
__wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
(void)sockfd;
(void)addr;
(void)addrlen;
return (int)mock();
}
MOCK int
__wrap_SSL_connect(SSL *ssl)
{
(void)ssl;
return (int)mock();
}
MOCK int
__wrap_nc_handshake_io(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_nc_ctx_check_and_fill(struct nc_session *session)
{
(void)session;
return (int)mock();
}
static void
test_nc_client_tls_setting_cert_key_paths(void **state)
{
(void)state;
const char *cert, *key;
int ret;
nc_client_init();
/* no certificats are set, nc_client_tls_get_cert_key_paths should output NULL */
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_null(cert);
assert_null(key);
/* set certificate path */
ret = nc_client_tls_set_cert_key_paths("cert_path", "key_path");
assert_int_equal(ret, 0);
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_string_equal(cert, "cert_path");
assert_string_equal(key, "key_path");
/* override certificate path */
ret = nc_client_tls_set_cert_key_paths("cert_path1", "key_path1");
assert_int_equal(ret, 0);
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_string_equal(cert, "cert_path1");
assert_string_equal(key, "key_path1");
}
static void
test_nc_client_tls_setting_trusted_ca_paths(void **state)
{
(void)state;
const char *file, *dir;
int ret;
ret = nc_client_tls_set_trusted_ca_paths("ca_file", "ca_dir");
assert_int_equal(ret, 0);
nc_client_tls_get_trusted_ca_paths(&file, &dir);
assert_string_equal("ca_file", file);
assert_string_equal("ca_dir", dir);
ret = nc_client_tls_set_trusted_ca_paths("ca_file1", "ca_dir1");
assert_int_equal(ret, 0);
nc_client_tls_get_trusted_ca_paths(&file, &dir);
assert_string_equal("ca_file1", file);
assert_string_equal("ca_dir1", dir);
}
static void
test_nc_connect_tls_succesfull(void **state)
{
(void)state;
int ret;
struct nc_session *session;
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
will_return(__wrap_connect, 0);
will_return(__wrap_SSL_connect, 1);
/* fake succesfull handshake */
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_tls("0.0.0.0", 6001, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
}
static void
test_nc_client_tls_setting_crl_paths(void **state)
{
(void)state;
const char *file, *dir;
int ret;
nc_client_tls_get_crl_paths(&file, &dir);
assert_null(file);
assert_null(dir);
ret = nc_client_tls_set_crl_paths("file", "dir");
assert_int_equal(ret, 0);
nc_client_tls_get_crl_paths(&file, &dir);
assert_string_equal(file, "file");
assert_string_equal(dir, "dir");
ret = nc_client_tls_set_crl_paths("file1", "dir1");
assert_int_equal(ret, 0);
nc_client_tls_get_crl_paths(&file, &dir);
assert_string_equal(file, "file1");
assert_string_equal(dir, "dir1");
/* destroy client */
nc_client_destroy();
}
static void
test_nc_connect_tls_handshake_failed(void **state)
{
(void)state;
int ret;
struct nc_session *session;
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
will_return(__wrap_connect, 0);
will_return(__wrap_SSL_connect, 1);
/* fake failed handshake */
will_return(__wrap_nc_handshake_io, 0);
session = nc_connect_tls("0.0.0.0", 6001, NULL);
assert_null(session);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_cert_key_paths, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_tls_handshake_failed, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_tls_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_trusted_ca_paths, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_crl_paths, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

24
tests/config.h.in Normal file
View file

@ -0,0 +1,24 @@
/**
* @file config.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief cmocka tests configuration header.
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifdef __GNUC__
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#else
# define UNUSED(x) UNUSED_ ## x
#endif
#define TESTS_DIR "@CMAKE_SOURCE_DIR@/tests"
@SSH_MACRO@
@TLS_MACRO@

1
tests/data/042686bb.0 Symbolic link
View file

@ -0,0 +1 @@
serverca.pem

1
tests/data/5412ca73.0 Symbolic link
View file

@ -0,0 +1 @@
server.crt

1
tests/data/62436b04.0 Symbolic link
View file

@ -0,0 +1 @@
client.crt

25
tests/data/client.crt Normal file
View file

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEQDCCAygCCQCV65JgDvfWkDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD
WjETMBEGA1UECAwKU29tZS1TdGF0ZTENMAsGA1UEBwwEQnJubzEPMA0GA1UECgwG
Q0VTTkVUMQwwCgYDVQQLDANUTUMxETAPBgNVBAMMCHNlcnZlcmNhMB4XDTE4MTEw
NTA3MzAzOVoXDTI4MTEwMjA3MzAzOVowYTELMAkGA1UEBhMCQ1oxEzARBgNVBAgM
ClNvbWUtU3RhdGUxDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEMMAoG
A1UECwwDVE1DMQ8wDQYDVQQDDAZjbGllbnQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQC+kqPqDL9GbWmqVQhp4qla4vYo4kFuh2HG48b7RLp/4l/guik4
Hvq2aDVFD9sBcs3FeQbjoLH1Q4doUr8jG6VwJsfovE5SD3T8dVLs2dtpW0OyXTcc
b0I9lOVDMz6f6IUBe/m5vk8XNbdUII0NJ8y1dQ51VH3e784Bzu7PSdaMaFac4fkw
8kJA9LxkWkv2FDFC7IBcVjRgtb/15EwODH849O6+VPEgX5gdozNj5bL45rKDBvvx
0KjD7dFBGAIHbSjmjp7HHadfYKqvtQnMb83fRcK6wohxNP3vy13wBTtSOlvOg16G
b+2ZB0Or7wgOw19ZvEIcNgswPwhHfZQNcYMNVLCu02BSzwdY00IEqNM0J5B/W//K
JF3uFF/ZqP7D2wO7w8j5UL2lpuxF7YrGecNT1Kr7ggHdkzcLlekkNu7wNWKAZlJ6
+Kbun8PqZbamGXLG2Ur1aZDkyKyD9nyMzsRneAlxO+HbQhLojimUbsMm+wgL89zc
hLYkL/DSmsJlryA4qhvzHaaONBw4DV5UhSbLDpZtXVfrn+MAm8hLpqf+gUCThsFN
8kDp9h9Tg9v01ai9jGb941HkFtGYUWHS5drSz3ZLnSI6i1/wKdbs9ns9YqDMqq2c
305v5h7taMN0b40KuGSIME4K4cOsdfCprFYGZQgKjaadQwrsm4k1Jl7kMwIDAQAB
MA0GCSqGSIb3DQEBCwUAA4IBAQAtwH2u1o16jkABlqEGDIvAJSMwBmZzNTNHgOXt
WDlkzWGsj+L1wc/DNjDRBkE31h4h7ASthhqi1nzzHYQ1hhPVhUUqI532AS/V7EPs
Bg3f+BI8gxqQ31TUBEQ4Ll6FtW36nqpJOe6Uui2rH2FonuUg2Av5BvDRil42Tu7f
YW4WpSU3e00HiGJ0J0t+QjoKRnnoLJJqlmzk8Y4aIlQim7Azvrlo1WEtOhI3L9UE
1GEqxLjRB45P36FSe1wfkgt7xmD0Xjy33Wh6Ae2Fvx7OfJ0K1zy0LHr4rDDJ3tLT
qjPqHIFhaa73jGXwXk8sZnbAk542Oa6C6AjzNFyqV7T5Q5lg
-----END CERTIFICATE-----

51
tests/data/client.key Normal file
View file

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAvpKj6gy/Rm1pqlUIaeKpWuL2KOJBbodhxuPG+0S6f+Jf4Lop
OB76tmg1RQ/bAXLNxXkG46Cx9UOHaFK/IxulcCbH6LxOUg90/HVS7NnbaVtDsl03
HG9CPZTlQzM+n+iFAXv5ub5PFzW3VCCNDSfMtXUOdVR93u/OAc7uz0nWjGhWnOH5
MPJCQPS8ZFpL9hQxQuyAXFY0YLW/9eRMDgx/OPTuvlTxIF+YHaMzY+Wy+Oaygwb7
8dCow+3RQRgCB20o5o6exx2nX2Cqr7UJzG/N30XCusKIcTT978td8AU7UjpbzoNe
hm/tmQdDq+8IDsNfWbxCHDYLMD8IR32UDXGDDVSwrtNgUs8HWNNCBKjTNCeQf1v/
yiRd7hRf2aj+w9sDu8PI+VC9pabsRe2KxnnDU9Sq+4IB3ZM3C5XpJDbu8DVigGZS
evim7p/D6mW2phlyxtlK9WmQ5Misg/Z8jM7EZ3gJcTvh20IS6I4plG7DJvsIC/Pc
3IS2JC/w0prCZa8gOKob8x2mjjQcOA1eVIUmyw6WbV1X65/jAJvIS6an/oFAk4bB
TfJA6fYfU4Pb9NWovYxm/eNR5BbRmFFh0uXa0s92S50iOotf8CnW7PZ7PWKgzKqt
nN9Ob+Ye7WjDdG+NCrhkiDBOCuHDrHXwqaxWBmUICo2mnUMK7JuJNSZe5DMCAwEA
AQKCAgA3X963LHsL2NECSHEIa28wVJCYcp32oun7Y8Y2ztKuRDX907oUb5QEGqWX
6rKFajl2buNckx4CmVuoKZsWdXsN6obeDpFncMxaazDsV6VUqMsz8bgI0B9cS36O
lz5UMrkrJD39BdpvcRFTJZ42u2DVPS01VJa6h83BYsKrgtYPuGWqclL5MPulajev
pTk7SMTDoHrv2bCghU9BANREpMb24tzYe1ARSxWlTv2owl7NyiMGxanBqxLO07Sh
CHvWcpaW38wtKWWv5iPSqHUvbTFR9jBOGiaRVoeO/PXPv4VsMD7q8+ssfyt38s9s
Dym1OHnlVjmTfvSjUT1zoH67pUchv/RUsUT8SR2zvK2Cm9xwEG+Nn2izuUZwCjxp
Pouan6ZZrCnJut0PKWbEpeEzwKWgEx3r+tYeoV+svkePKKU3oQEUgzACAA5PHRZT
GiaFBHznb57HZaw5xXCmm1g3k31wP0MEnLebEeFtFvqcc0LWQ6uN5UZtpZNM393n
w87CVIZqX0miuB7GsDsZwg8ODWy7nEsAppeoudFg81Gy0jrVluI72le2sYGU4SRe
TBjAf/9H6GF+rYWYgWFfkDeQp31Vx3gkJC6V+lSdMh6OMa8A+vk47alwQbX5kbLJ
AqtSFjOCWLDhUB650huOnk9PyhoK85D+hd9Tx924TByeegXO2QKCAQEA/QptsVKV
fUlfKN2HVntPP6+y/fnR2nSGRWP91KP1I1/PyG+FSXGxR1ngXFpn5TByD5CFBEhv
0PX8pUjbC1cgNC7WJuy2B0g4Cn4m25yiMPz7g1Ngt+JkWH3E57AeSYCqUUm41Qb0
PUA1zZDE4izvEzweTp04ZFk/1+oPQDeol0oIFhrlrxDSYRjpgDcwPWrQvRnmqfQL
C1aB9+ulHqLIzvhpsWBg4+mYsMfFGX6P+f0NxWFH2hVZoax2STcupAXiRcUbzTO9
z6Yan6DydHv0dS3XcReOFhgB1fRC4hbnJPRZxH1rJ6H0PzmN6vfKeWzZ60Uflyti
r100P+Mh2rUobwKCAQEAwM0w282zqmn71pqmCUrC153uRY1eXRtGmDkuLvpPO0N9
LPLo1AblboIdxXw4pUBzmxtO+J8L+rYzi+sorNhL0GuOHulgVSe27WMfEXeGOhay
m0qGiEEs1s2Ctgj7MdzHzW2moiMOJUgnBIQraJ9JF7KGwMoblUsFE3EeIF2sy8qk
tHugxfx/HUouCx5m8Fsh0HMkvsJMZvmJPMihsVYL57UxGTuKMPsNGpNfdyKgHo3G
0t+0yR81EPsfnhxoLbGlvgA9Bcn5I2tU9WyYI+e5LdbsXg2EAltXY4YHyg+GRFnL
2ZrPNfm5Wrh2P5w0xepGflwW0QfL1CVPJz9+0c86fQKCAQB4FgDkzGqBYNa2UBuw
YSjE8p8hhPOglvg56jBGP+FQfmHfn31D057sW6zsZ0MzM4CN/moCFFZsdrEFx8Oc
aCayXR/orSHd5tohsKjERFt8oDLEqkcWPWydymIuChj1jQhHN5NuFbTHdLeT7QZi
yCxVloxThq2CghCYaU3/jeqGke2wf+dM49DTn70AyjAslYqmk6oyMc0j/lQD0mM0
XNCr2JxgP3r77po2Gzhg1v2BCCUG7RnqV4OIBI7GRFfwI5K0xcxh4BJOf2fXJcyq
l0D2c2DxHNqjhZUpcphjL7dWhFgttc+qqWN+tdOyFRKT+aKZ0t4hIcfdrX/kaehh
IOQlAoIBAQCCEmyh/db2Y2Yp1E+r+SoWOVAk0EkXW213CSylOO33N4Ldrktxr+1d
bp8TOskkg6T6waO3i+WTERUZkl7wrUQIqmdJZ308Nfztjm/JYu/FhMaeidrVVdMg
X6mNkeWWMDMD3rQKsse6U0EvhNOcU8oGGMVcj32obOJRyYDfqRMIsgAIW1eN+tjv
M7p4edxMz86ySNxDbeYJmtQBlAGyGDET82PaeBa1EMo4YbCIOW347wFyBsZ64Xj1
qdYc32FRYoZE9vg0TZytTp4UrVy+7Hg7+sGgelHTHTiJxkS/B1Y4CrTCa/Tbn0xz
bfso0wOvemxwl0Q1ZaMXzsvl2KqAdeQpAoIBAQC9tvBTKaBxcaijwLqa+lKOQZnO
4MxQsCX5hcXyBJPPhEjP8J3MoyEOGQZK1gu6fRnDuOhgEFqBHJAGIEvLYId2qP3f
4/wZp4as4BYrEPBplwehrh9ufG8NrY4v9vSFzWkNCvuiOgGxmmzo9CPDRvMHSjnx
R1FqAc4PfMqU7LYEX9MKgu5KwCzp8Ot6Y5ifm1hXr2x7ARxe/S3zdvIpaB/aY9TC
D/gWkXVplQDGrLwcguleMy4ZUKuD6L9QLbBBjN18ua5yHfCfw3flSgC9hBxazqce
WChZob9ttZIXX/W0sefNjQjo0etQFakkGYAZihK34fUnCSuMVQDWWxdl1wJA
-----END RSA PRIVATE KEY-----

12
tests/data/key_dsa Normal file
View file

@ -0,0 +1,12 @@
-----BEGIN DSA PRIVATE KEY-----
MIIBugIBAAKBgQC5UysgLVJXTBaY4hPuLBBmxsOkC9VzFKbH581ufLqKxBZlQhcn
OZ4DaQWXUvMXOmDTnG+ROpjbXBMxaYRmCwrUL9Gz0JeTYuqE413D1XYpQYZH5oJH
7SyIe4R+mlCdORekeSKdY2oJ9dREVVe0WXiAzrBV+Eg5Ve3uWQkYw3HAMwIVAMOo
rc88si96YqcYSPf//761FJtVAoGANwUJNODSvguHU6x55+UTGHLyWnbIqd/nbUn1
cnCRa4xeHSHq0rBayoHh3hJiqqpcdRpb0lVoYNe51HmFJHJUGLHtYcxgnqdvIF5K
QiP5h1ESxYqb4v4AXHl5+CVyC0Yp/hkowqaNfVqyOgol/IhNlJknZDCMxWUD9NUJ
iiV5oc0CgYACU3qrETmgEHbwx5kCKN1/Ly4pWzS5rNg764aYsU0wE714TfYs5nOf
yEvQYkfBDb+rEpGyKot6ZvDsHvL0WVVVx7mIDSKnzHAYwYGl1wKNHlLenOMZIDRT
43AKfTz03wRkCrzBl2fAmLLq7wFaXcDDxaBg4zN2CDbuHjmJgRuwzAIUBRb7QH4c
p2gVuWcmRuuI9Qexmzc=
-----END DSA PRIVATE KEY-----

1
tests/data/key_dsa.pub Normal file
View file

@ -0,0 +1 @@
ssh-dss AAAAB3NzaC1kc3MAAACBALlTKyAtUldMFpjiE+4sEGbGw6QL1XMUpsfnzW58uorEFmVCFyc5ngNpBZdS8xc6YNOcb5E6mNtcEzFphGYLCtQv0bPQl5Ni6oTjXcPVdilBhkfmgkftLIh7hH6aUJ05F6R5Ip1jagn11ERVV7RZeIDOsFX4SDlV7e5ZCRjDccAzAAAAFQDDqK3PPLIvemKnGEj3//++tRSbVQAAAIA3BQk04NK+C4dTrHnn5RMYcvJadsip3+dtSfVycJFrjF4dIerSsFrKgeHeEmKqqlx1GlvSVWhg17nUeYUkclQYse1hzGCep28gXkpCI/mHURLFipvi/gBceXn4JXILRin+GSjCpo19WrI6CiX8iE2UmSdkMIzFZQP01QmKJXmhzQAAAIACU3qrETmgEHbwx5kCKN1/Ly4pWzS5rNg764aYsU0wE714TfYs5nOfyEvQYkfBDb+rEpGyKot6ZvDsHvL0WVVVx7mIDSKnzHAYwYGl1wKNHlLenOMZIDRT43AKfTz03wRkCrzBl2fAmLLq7wFaXcDDxaBg4zN2CDbuHjmJgRuwzA== vasko@pcvasko

5
tests/data/key_ecdsa Normal file
View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICQ2fr9Jt2xluom0YQQ7HseE8YTo5reZRVcQENKUWOrooAoGCCqGSM49
AwEHoUQDQgAENEgHZ41JFZT4c1ZFQYqvvqCEIuDxFoyVQKmz2UGOdzqCBomXZD5M
4ufyx7bAB0reVgEzPd1ypH5KVTt4HzAkBw==
-----END EC PRIVATE KEY-----

1
tests/data/key_ecdsa.pub Normal file
View file

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDRIB2eNSRWU+HNWRUGKr76ghCLg8RaMlUCps9lBjnc6ggaJl2Q+TOLn8se2wAdK3lYBMz3dcqR+SlU7eB8wJAc= vasko@pcvasko

27
tests/data/key_rsa Normal file
View file

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAz2r1QC4jO0MEyFAJ3TvBPRjpEg0Fo7hMJM2yd5DgT2u5JKu9
VDmRXnnA4xW/XoqZJY3xcrPyWBxkGmFRfP7L09CleYex5brUXResr6khG46W0zkW
9u6XR194hRMgwsAiKKvbzHl/sMl57Xsp9NRqSC9IoNFgJ75Kl/0j47qZV+WSLVEq
9DIVzO3zUl+dAQ+hvCDtP3Y1HHTFHI04xr+90sQfWcy78Z4V7lCW8xCSSS4+qmo8
hCFWiUmu97wN9yQg/Ks2q67LiE6I1SzwxZnh8EgKAeXT/OsqKenusqbl8yscXn+s
jV3BPKE4/FBWASF7ICD9pwb1pONRGitFZN2BZQIDAQABAoIBAQC1jeTQYdI67EXC
ZLTNrqFNroFMaJOYJBiaWmat2+VL/3nWzHDzyVQiQyaAXyfcRCsbQSyn/zTQxUEm
Cis+4vRdGpPNVeZ0tN1wAuoH9F3jdiM1DhK44E0Qj1O5/+08Ktt7iDrjtzH699A+
/ADUqh3Bw4mqIrss7pbyhQSmME5LLTbaWikZ8LgtUiF9f5JWzsqjPb6Yd8JEg0O+
5lDngLfgEYevKCJxxBMtQQQ6gZCjQQWmir+/0NBezSHsoltPlw1m8Vs8Y5zz684y
v33J/qxDM7+rbGbte2fSQ06OuK7abCZMyfXyWdp4cQpG1JZRxGp4Y8vQKvsU5ZOQ
UT/v7ur9AoGBAO+Li/vUzU3GlL7mxBlPTg5LavItWq6C7Rnwftjql7yPxrQ/+m5R
Za0YujnqvZq5SpdpljCZbF9KYrFr92wgFqlt5uYptI4eD0/6xALEUcJJIlllTjiK
tJmuyFkkD45WEn1IlDGAURQiDn6aqd40odlPsv4L5EdnQEQQz6Kfv6JLAoGBAN2q
chHTKv1PBXfqRm0ABYSPyFhki2RqI4DWsbwykFXn3qP7tDDnmR/VMsAbApgTVW77
LGffJ7DZXsqgzujwcqvLBKf8Wl5MRJg2jTe0GkKEBYqhGWNzBhuIwnIcKu/6HsEd
FfCD93hwUPaVTBE+2ckXQVb9RSUCpGarXKk9cZ0PAoGBAJ/Hku29OdwA80KKpo7D
SStbvtAe1HfGuOQueE2z3NZXiJC+hAqFnK5i6gSrwSCtK0XnldiA3bqJ4V66x2SF
2tfUiMlJVDffcRNGDuxRir9vDMxYOF6alnBUFyruVLn6S4bpnH+QOYSWWtizzU58
CODsulWeFPxTsJg2Jmkw6SAVAoGANWBGqX4k2uw9T9vM65BWw83vm0FSw3I/bFXG
ZJ/0W4tC9E+22xPZrm2jE9ktLbtyFhBLaBO3NgGRrs88I6FKq41uaJj+lbhdyB1S
sfgfXqb1wqT6PRVEgjrTP7ECsdiTsUK0tr7AR3McO9RFhd2Ribec1zqTfM7/EW3w
GRyfkAcCgYAtw6KO+5fXHE79v9pUdZAJ4PAc/KdHjv0zE9s5snwUrh7TO5fIB62d
i6nPBWLwD5InDZ9sNgxzTBt+0o2N6PsvKQFtfEBemKimmZShMytFkx9/KTRNR9se
2qcBMiJsdAaz6hHUliYVWV3Ui+Uy+vYh5reuEhcvEjEzT6ySaCrZfg==
-----END RSA PRIVATE KEY-----

1
tests/data/key_rsa.pub Normal file
View file

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPavVALiM7QwTIUAndO8E9GOkSDQWjuEwkzbJ3kOBPa7kkq71UOZFeecDjFb9eipkljfFys/JYHGQaYVF8/svT0KV5h7HlutRdF6yvqSEbjpbTORb27pdHX3iFEyDCwCIoq9vMeX+wyXnteyn01GpIL0ig0WAnvkqX/SPjuplX5ZItUSr0MhXM7fNSX50BD6G8IO0/djUcdMUcjTjGv73SxB9ZzLvxnhXuUJbzEJJJLj6qajyEIVaJSa73vA33JCD8qzarrsuITojVLPDFmeHwSAoB5dP86yop6e6ypuXzKxxef6yNXcE8oTj8UFYBIXsgIP2nBvWk41EaK0Vk3YFl vasko@pcvasko

View file

@ -0,0 +1,464 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="ietf-netconf-acm"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:nacm="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"
xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"/>
<prefix value="nacm"/>
<import module="ietf-yang-types">
<prefix value="yang"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;https://datatracker.ietf.org/wg/netconf/&gt;
WG List: &lt;mailto:netconf@ietf.org&gt;
Author: Andy Bierman
&lt;mailto:andy@yumaworks.com&gt;
Author: Martin Bjorklund
&lt;mailto:mbj@tail-f.com&gt;</text>
</contact>
<description>
<text>Network Configuration Access Control Model.
Copyright (c) 2012 - 2018 IETF Trust and the persons
identified as authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8341; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2018-02-14">
<description>
<text>Added support for YANG 1.1 actions and notifications tied to
data nodes. Clarified how NACM extensions can be used by
other data models.</text>
</description>
<reference>
<text>RFC 8341: Network Configuration Access Control Model</text>
</reference>
</revision>
<revision date="2012-02-22">
<description>
<text>Initial version.</text>
</description>
<reference>
<text>RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model</text>
</reference>
</revision>
<extension name="default-deny-write">
<description>
<text>Used to indicate that the data model node
represents a sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have write access to the node. An
explicit access control rule is required for all other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.</text>
</description>
</extension>
<extension name="default-deny-all">
<description>
<text>Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have read, write, or execute access to
the node. An explicit access control rule is required for all
other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.</text>
</description>
</extension>
<typedef name="user-name-type">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>General-purpose username string.</text>
</description>
</typedef>
<typedef name="matchall-string-type">
<type name="string">
<pattern value="\*"/>
</type>
<description>
<text>The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.</text>
</description>
</typedef>
<typedef name="access-operations-type">
<type name="bits">
<bit name="create">
<description>
<text>Any protocol operation that creates a
new data node.</text>
</description>
</bit>
<bit name="read">
<description>
<text>Any protocol operation or notification that
returns the value of a data node.</text>
</description>
</bit>
<bit name="update">
<description>
<text>Any protocol operation that alters an existing
data node.</text>
</description>
</bit>
<bit name="delete">
<description>
<text>Any protocol operation that removes a data node.</text>
</description>
</bit>
<bit name="exec">
<description>
<text>Execution access to the specified protocol operation.</text>
</description>
</bit>
</type>
<description>
<text>Access operation.</text>
</description>
</typedef>
<typedef name="group-name-type">
<type name="string">
<length value="1..max"/>
<pattern value="[^\*].*"/>
</type>
<description>
<text>Name of administrative group to which
users can be assigned.</text>
</description>
</typedef>
<typedef name="action-type">
<type name="enumeration">
<enum name="permit">
<description>
<text>Requested action is permitted.</text>
</description>
</enum>
<enum name="deny">
<description>
<text>Requested action is denied.</text>
</description>
</enum>
</type>
<description>
<text>Action taken by the server when a particular
rule matches.</text>
</description>
</typedef>
<typedef name="node-instance-identifier">
<type name="yang:xpath1.0"/>
<description>
<text>Path expression used to represent a special
data node, action, or notification instance-identifier
string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply,
except that predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XML Path Language (XPath) expression is evaluated in the
following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the
current session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.
The accessible tree includes actions and notifications tied
to data nodes.</text>
</description>
</typedef>
<container name="nacm">
<nacm:default-deny-all/>
<description>
<text>Parameters for NETCONF access control model.</text>
</description>
<leaf name="enable-nacm">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.</text>
</description>
</leaf>
<leaf name="read-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether read access is granted if
no appropriate rule is found for a
particular read request.</text>
</description>
</leaf>
<leaf name="write-default">
<type name="action-type"/>
<default value="deny"/>
<description>
<text>Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.</text>
</description>
</leaf>
<leaf name="exec-default">
<type name="action-type"/>
<default value="permit"/>
<description>
<text>Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.</text>
</description>
</leaf>
<leaf name="enable-external-groups">
<type name="boolean"/>
<default value="true"/>
<description>
<text>Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.</text>
</description>
</leaf>
<leaf name="denied-operations">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request was denied.</text>
</description>
</leaf>
<leaf name="denied-data-writes">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.</text>
</description>
</leaf>
<leaf name="denied-notifications">
<type name="yang:zero-based-counter32"/>
<config value="false"/>
<mandatory value="true"/>
<description>
<text>Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.</text>
</description>
</leaf>
<container name="groups">
<description>
<text>NETCONF access control groups.</text>
</description>
<list name="group">
<key value="name"/>
<description>
<text>One NACM group entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.</text>
</description>
<leaf name="name">
<type name="group-name-type"/>
<description>
<text>Group name associated with this entry.</text>
</description>
</leaf>
<leaf-list name="user-name">
<type name="user-name-type"/>
<description>
<text>Each entry identifies the username of
a member of the group associated with
this entry.</text>
</description>
</leaf-list>
</list>
</container>
<list name="rule-list">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>An ordered collection of access control rules.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule-list.</text>
</description>
</leaf>
<leaf-list name="group">
<type name="union">
<type name="matchall-string-type"/>
<type name="group-name-type"/>
</type>
<description>
<text>List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.</text>
</description>
</leaf-list>
<list name="rule">
<key value="name"/>
<ordered-by value="user"/>
<description>
<text>One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines whether or not
access is granted.</text>
</description>
<leaf name="name">
<type name="string">
<length value="1..max"/>
</type>
<description>
<text>Arbitrary name assigned to the rule.</text>
</description>
</leaf>
<leaf name="module-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<default value="*"/>
<description>
<text>Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.</text>
</description>
</leaf>
<choice name="rule-type">
<description>
<text>This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.</text>
</description>
<case name="protocol-operation">
<leaf name="rpc-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.</text>
</description>
</leaf>
</case>
<case name="notification">
<leaf name="notification-name">
<type name="union">
<type name="matchall-string-type"/>
<type name="string"/>
</type>
<description>
<text>This leaf matches if it has the value '*' or if its
value equals the requested notification name.</text>
</description>
</leaf>
</case>
<case name="data-node">
<leaf name="path">
<type name="node-instance-identifier"/>
<mandatory value="true"/>
<description>
<text>Data node instance-identifier associated with the
data node, action, or notification controlled by
this rule.
Configuration data or state data
instance-identifiers start with a top-level
data node. A complete instance-identifier is
required for this type of path value.
The special value '/' refers to all possible
datastore contents.</text>
</description>
</leaf>
</case>
</choice>
<leaf name="access-operations">
<type name="union">
<type name="matchall-string-type"/>
<type name="access-operations-type"/>
</type>
<default value="*"/>
<description>
<text>Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.</text>
</description>
</leaf>
<leaf name="action">
<type name="action-type"/>
<mandatory value="true"/>
<description>
<text>The access control action associated with the
rule. If a rule has been determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.</text>
</description>
</leaf>
<leaf name="comment">
<type name="string"/>
<description>
<text>A textual description of the access rule.</text>
</description>
</leaf>
</list>
</list>
</container>
</module>

View file

@ -0,0 +1,600 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="ietf-netconf-monitoring"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:ncm="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types"
xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"/>
<prefix value="ncm"/>
<import module="ietf-yang-types">
<prefix value="yang"/>
</import>
<import module="ietf-inet-types">
<prefix value="inet"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
WG List: &lt;mailto:netconf@ietf.org&gt;
WG Chair: Mehmet Ersue
&lt;mailto:mehmet.ersue@nsn.com&gt;
WG Chair: Bert Wijnen
&lt;mailto:bertietf@bwijnen.net&gt;
Editor: Mark Scott
&lt;mailto:mark.scott@ericsson.com&gt;
Editor: Martin Bjorklund
&lt;mailto:mbj@tail-f.com&gt;</text>
</contact>
<description>
<text>NETCONF Monitoring Module.
All elements in this module are read-only.
Copyright (c) 2010 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD
License set forth in Section 4.c of the IETF Trust's
Legal Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6022; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2010-10-04">
<description>
<text>Initial revision.</text>
</description>
<reference>
<text>RFC 6022: YANG Module for NETCONF Monitoring</text>
</reference>
</revision>
<typedef name="netconf-datastore-type">
<type name="enumeration">
<enum name="running"/>
<enum name="candidate"/>
<enum name="startup"/>
</type>
<description>
<text>Enumeration of possible NETCONF datastore types.</text>
</description>
<reference>
<text>RFC 4741: NETCONF Configuration Protocol</text>
</reference>
</typedef>
<identity name="transport">
<description>
<text>Base identity for NETCONF transport types.</text>
</description>
</identity>
<identity name="netconf-ssh">
<base name="transport"/>
<description>
<text>NETCONF over Secure Shell (SSH).</text>
</description>
<reference>
<text>RFC 4742: Using the NETCONF Configuration Protocol
over Secure SHell (SSH)</text>
</reference>
</identity>
<identity name="netconf-soap-over-beep">
<base name="transport"/>
<description>
<text>NETCONF over Simple Object Access Protocol (SOAP) over
Blocks Extensible Exchange Protocol (BEEP).</text>
</description>
<reference>
<text>RFC 4743: Using NETCONF over the Simple Object
Access Protocol (SOAP)</text>
</reference>
</identity>
<identity name="netconf-soap-over-https">
<base name="transport"/>
<description>
<text>NETCONF over Simple Object Access Protocol (SOAP)
over Hypertext Transfer Protocol Secure (HTTPS).</text>
</description>
<reference>
<text>RFC 4743: Using NETCONF over the Simple Object
Access Protocol (SOAP)</text>
</reference>
</identity>
<identity name="netconf-beep">
<base name="transport"/>
<description>
<text>NETCONF over Blocks Extensible Exchange Protocol (BEEP).</text>
</description>
<reference>
<text>RFC 4744: Using the NETCONF Protocol over the
Blocks Extensible Exchange Protocol (BEEP)</text>
</reference>
</identity>
<identity name="netconf-tls">
<base name="transport"/>
<description>
<text>NETCONF over Transport Layer Security (TLS).</text>
</description>
<reference>
<text>RFC 5539: NETCONF over Transport Layer Security (TLS)</text>
</reference>
</identity>
<identity name="schema-format">
<description>
<text>Base identity for data model schema languages.</text>
</description>
</identity>
<identity name="xsd">
<base name="schema-format"/>
<description>
<text>W3C XML Schema Definition.</text>
</description>
<reference>
<text>W3C REC REC-xmlschema-1-20041028:
XML Schema Part 1: Structures</text>
</reference>
</identity>
<identity name="yang">
<base name="schema-format"/>
<description>
<text>The YANG data modeling language for NETCONF.</text>
</description>
<reference>
<text>RFC 6020: YANG - A Data Modeling Language for the
Network Configuration Protocol (NETCONF)</text>
</reference>
</identity>
<identity name="yin">
<base name="schema-format"/>
<description>
<text>The YIN syntax for YANG.</text>
</description>
<reference>
<text>RFC 6020: YANG - A Data Modeling Language for the
Network Configuration Protocol (NETCONF)</text>
</reference>
</identity>
<identity name="rng">
<base name="schema-format"/>
<description>
<text>Regular Language for XML Next Generation (RELAX NG).</text>
</description>
<reference>
<text>ISO/IEC 19757-2:2008: RELAX NG</text>
</reference>
</identity>
<identity name="rnc">
<base name="schema-format"/>
<description>
<text>Relax NG Compact Syntax</text>
</description>
<reference>
<text>ISO/IEC 19757-2:2008: RELAX NG</text>
</reference>
</identity>
<grouping name="common-counters">
<description>
<text>Counters that exist both per session, and also globally,
accumulated from all sessions.</text>
</description>
<leaf name="in-rpcs">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of correct &lt;rpc&gt; messages received.</text>
</description>
</leaf>
<leaf name="in-bad-rpcs">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of messages received when an &lt;rpc&gt; message was expected,
that were not correct &lt;rpc&gt; messages. This includes XML parse
errors and errors on the rpc layer.</text>
</description>
</leaf>
<leaf name="out-rpc-errors">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of &lt;rpc-reply&gt; messages sent that contained an
&lt;rpc-error&gt; element.</text>
</description>
</leaf>
<leaf name="out-notifications">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of &lt;notification&gt; messages sent.</text>
</description>
</leaf>
</grouping>
<container name="netconf-state">
<config value="false"/>
<description>
<text>The netconf-state container is the root of the monitoring
data model.</text>
</description>
<container name="capabilities">
<description>
<text>Contains the list of NETCONF capabilities supported by the
server.</text>
</description>
<leaf-list name="capability">
<type name="inet:uri"/>
<description>
<text>List of NETCONF capabilities supported by the server.</text>
</description>
</leaf-list>
</container>
<container name="datastores">
<description>
<text>Contains the list of NETCONF configuration datastores.</text>
</description>
<list name="datastore">
<key value="name"/>
<description>
<text>List of NETCONF configuration datastores supported by
the NETCONF server and related information.</text>
</description>
<leaf name="name">
<type name="netconf-datastore-type"/>
<description>
<text>Name of the datastore associated with this list entry.</text>
</description>
</leaf>
<container name="locks">
<presence value="This container is present only if the datastore&#10;is locked."/>
<description>
<text>The NETCONF &lt;lock&gt; and &lt;partial-lock&gt; operations allow
a client to lock specific resources in a datastore. The
NETCONF server will prevent changes to the locked
resources by all sessions except the one that acquired
the lock(s).
Monitoring information is provided for each datastore
entry including details such as the session that acquired
the lock, the type of lock (global or partial) and the
list of locked resources. Multiple locks per datastore
are supported.</text>
</description>
<grouping name="lock-info">
<description>
<text>Lock related parameters, common to both global and
partial locks.</text>
</description>
<leaf name="locked-by-session">
<type name="uint32"/>
<mandatory value="true"/>
<description>
<text>The session ID of the session that has locked
this resource. Both a global lock and a partial
lock MUST contain the NETCONF session-id.
If the lock is held by a session that is not managed
by the NETCONF server (e.g., a CLI session), a session
id of 0 (zero) is reported.</text>
</description>
<reference>
<text>RFC 4741: NETCONF Configuration Protocol</text>
</reference>
</leaf>
<leaf name="locked-time">
<type name="yang:date-and-time"/>
<mandatory value="true"/>
<description>
<text>The date and time of when the resource was
locked.</text>
</description>
</leaf>
</grouping>
<choice name="lock-type">
<description>
<text>Indicates if a global lock or a set of partial locks
are set.</text>
</description>
<container name="global-lock">
<description>
<text>Present if the global lock is set.</text>
</description>
<uses name="lock-info"/>
</container>
<list name="partial-lock">
<key value="lock-id"/>
<description>
<text>List of partial locks.</text>
</description>
<reference>
<text>RFC 5717: Partial Lock Remote Procedure Call (RPC) for
NETCONF</text>
</reference>
<leaf name="lock-id">
<type name="uint32"/>
<description>
<text>This is the lock id returned in the &lt;partial-lock&gt;
response.</text>
</description>
</leaf>
<uses name="lock-info"/>
<leaf-list name="select">
<type name="yang:xpath1.0"/>
<min-elements value="1"/>
<description>
<text>The xpath expression that was used to request
the lock. The select expression indicates the
original intended scope of the lock.</text>
</description>
</leaf-list>
<leaf-list name="locked-node">
<type name="instance-identifier"/>
<description>
<text>The list of instance-identifiers (i.e., the
locked nodes).
The scope of the partial lock is defined by the list
of locked nodes.</text>
</description>
</leaf-list>
</list>
</choice>
</container>
</list>
</container>
<container name="schemas">
<description>
<text>Contains the list of data model schemas supported by the
server.</text>
</description>
<list name="schema">
<key value="identifier version format"/>
<description>
<text>List of data model schemas supported by the server.</text>
</description>
<leaf name="identifier">
<type name="string"/>
<description>
<text>Identifier to uniquely reference the schema. The
identifier is used in the &lt;get-schema&gt; operation and may
be used for other purposes such as file retrieval.
For modeling languages that support or require a data
model name (e.g., YANG module name) the identifier MUST
match that name. For YANG data models, the identifier is
the name of the module or submodule. In other cases, an
identifier such as a filename MAY be used instead.</text>
</description>
</leaf>
<leaf name="version">
<type name="string"/>
<description>
<text>Version of the schema supported. Multiple versions MAY be
supported simultaneously by a NETCONF server. Each
version MUST be reported individually in the schema list,
i.e., with same identifier, possibly different location,
but different version.
For YANG data models, version is the value of the most
recent YANG 'revision' statement in the module or
submodule, or the empty string if no 'revision' statement
is present.</text>
</description>
</leaf>
<leaf name="format">
<type name="identityref">
<base name="schema-format"/>
</type>
<description>
<text>The data modeling language the schema is written
in (currently xsd, yang, yin, rng, or rnc).
For YANG data models, 'yang' format MUST be supported and
'yin' format MAY also be provided.</text>
</description>
</leaf>
<leaf name="namespace">
<type name="inet:uri"/>
<mandatory value="true"/>
<description>
<text>The XML namespace defined by the data model.
For YANG data models, this is the module's namespace.
If the list entry describes a submodule, this field
contains the namespace of the module to which the
submodule belongs.</text>
</description>
</leaf>
<leaf-list name="location">
<type name="union">
<type name="enumeration">
<enum name="NETCONF"/>
</type>
<type name="inet:uri"/>
</type>
<description>
<text>One or more locations from which the schema can be
retrieved. This list SHOULD contain at least one
entry per schema.
A schema entry may be located on a remote file system
(e.g., reference to file system for ftp retrieval) or
retrieved directly from a server supporting the
&lt;get-schema&gt; operation (denoted by the value 'NETCONF').</text>
</description>
</leaf-list>
</list>
</container>
<container name="sessions">
<description>
<text>The sessions container includes session-specific data for
NETCONF management sessions. The session list MUST include
all currently active NETCONF sessions.</text>
</description>
<list name="session">
<key value="session-id"/>
<description>
<text>All NETCONF sessions managed by the NETCONF server
MUST be reported in this list.</text>
</description>
<leaf name="session-id">
<type name="uint32">
<range value="1..max"/>
</type>
<description>
<text>Unique identifier for the session. This value is the
NETCONF session identifier, as defined in RFC 4741.</text>
</description>
<reference>
<text>RFC 4741: NETCONF Configuration Protocol</text>
</reference>
</leaf>
<leaf name="transport">
<type name="identityref">
<base name="transport"/>
</type>
<mandatory value="true"/>
<description>
<text>Identifies the transport for each session, e.g.,
'netconf-ssh', 'netconf-soap', etc.</text>
</description>
</leaf>
<leaf name="username">
<type name="string"/>
<mandatory value="true"/>
<description>
<text>The username is the client identity that was authenticated
by the NETCONF transport protocol. The algorithm used to
derive the username is NETCONF transport protocol specific
and in addition specific to the authentication mechanism
used by the NETCONF transport protocol.</text>
</description>
</leaf>
<leaf name="source-host">
<type name="inet:host"/>
<description>
<text>Host identifier of the NETCONF client. The value
returned is implementation specific (e.g., hostname,
IPv4 address, IPv6 address)</text>
</description>
</leaf>
<leaf name="login-time">
<type name="yang:date-and-time"/>
<mandatory value="true"/>
<description>
<text>Time at the server at which the session was established.</text>
</description>
</leaf>
<uses name="common-counters">
<description>
<text>Per-session counters. Zero based with following reset
behaviour:
- at start of a session
- when max value is reached</text>
</description>
</uses>
</list>
</container>
<container name="statistics">
<description>
<text>Statistical data pertaining to the NETCONF server.</text>
</description>
<leaf name="netconf-start-time">
<type name="yang:date-and-time"/>
<description>
<text>Date and time at which the management subsystem was
started.</text>
</description>
</leaf>
<leaf name="in-bad-hellos">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of sessions silently dropped because an
invalid &lt;hello&gt; message was received. This includes &lt;hello&gt;
messages with a 'session-id' attribute, bad namespace, and
bad capability declarations.</text>
</description>
</leaf>
<leaf name="in-sessions">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of sessions started. This counter is incremented
when a &lt;hello&gt; message with a &lt;session-id&gt; is sent.
'in-sessions' - 'in-bad-hellos' =
'number of correctly started netconf sessions'</text>
</description>
</leaf>
<leaf name="dropped-sessions">
<type name="yang:zero-based-counter32"/>
<description>
<text>Number of sessions that were abnormally terminated, e.g.,
due to idle timeout or transport close. This counter is not
incremented when a session is properly closed by a
&lt;close-session&gt; operation, or killed by a &lt;kill-session&gt;
operation.</text>
</description>
</leaf>
<uses name="common-counters">
<description>
<text>Global counters, accumulated from all sessions.
Zero based with following reset behaviour:
- re-initialization of NETCONF server
- when max value is reached</text>
</description>
</uses>
</container>
</container>
<rpc name="get-schema">
<description>
<text>This operation is used to retrieve a schema from the
NETCONF server.
Positive Response:
The NETCONF server returns the requested schema.
Negative Response:
If requested schema does not exist, the &lt;error-tag&gt; is
'invalid-value'.
If more than one schema matches the requested parameters, the
&lt;error-tag&gt; is 'operation-failed', and &lt;error-app-tag&gt; is
'data-not-unique'.</text>
</description>
<input>
<leaf name="identifier">
<type name="string"/>
<mandatory value="true"/>
<description>
<text>Identifier for the schema list entry.</text>
</description>
</leaf>
<leaf name="version">
<type name="string"/>
<description>
<text>Version of the schema requested. If this parameter is not
present, and more than one version of the schema exists on
the server, a 'data-not-unique' error is returned, as
described above.</text>
</description>
</leaf>
<leaf name="format">
<type name="identityref">
<base name="schema-format"/>
</type>
<description>
<text>The data modeling language of the schema. If this
parameter is not present, and more than one formats of
the schema exists on the server, a 'data-not-unique' error
is returned, as described above.</text>
</description>
</leaf>
</input>
<output>
<anyxml name="data">
<description>
<text>Contains the schema content.</text>
</description>
</anyxml>
</output>
</rpc>
</module>

View file

@ -0,0 +1,353 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="ietf-netconf-notifications"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:ncn="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"
xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"/>
<prefix value="ncn"/>
<import module="ietf-inet-types">
<prefix value="inet"/>
</import>
<import module="ietf-netconf">
<prefix value="nc"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration Protocol) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
WG List: &lt;mailto:netconf@ietf.org&gt;
WG Chair: Bert Wijnen
&lt;mailto:bertietf@bwijnen.net&gt;
WG Chair: Mehmet Ersue
&lt;mailto:mehmet.ersue@nsn.com&gt;
Editor: Andy Bierman
&lt;mailto:andy@netconfcentral.org&gt;</text>
</contact>
<description>
<text>This module defines a YANG data model for use with the
NETCONF protocol that allows the NETCONF client to
receive common NETCONF base event notifications.
Copyright (c) 2012 IETF Trust and the persons identified as
the document authors. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6470; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2012-02-06">
<description>
<text>Initial version.</text>
</description>
<reference>
<text>RFC 6470: NETCONF Base Notifications</text>
</reference>
</revision>
<grouping name="common-session-parms">
<description>
<text>Common session parameters to identify a
management session.</text>
</description>
<leaf name="username">
<type name="string"/>
<mandatory value="true"/>
<description>
<text>Name of the user for the session.</text>
</description>
</leaf>
<leaf name="session-id">
<type name="nc:session-id-or-zero-type"/>
<mandatory value="true"/>
<description>
<text>Identifier of the session.
A NETCONF session MUST be identified by a non-zero value.
A non-NETCONF session MAY be identified by the value zero.</text>
</description>
</leaf>
<leaf name="source-host">
<type name="inet:ip-address"/>
<description>
<text>Address of the remote host for the session.</text>
</description>
</leaf>
</grouping>
<grouping name="changed-by-parms">
<description>
<text>Common parameters to identify the source
of a change event, such as a configuration
or capability change.</text>
</description>
<container name="changed-by">
<description>
<text>Indicates the source of the change.
If caused by internal action, then the
empty leaf 'server' will be present.
If caused by a management session, then
the name, remote host address, and session ID
of the session that made the change will be reported.</text>
</description>
<choice name="server-or-user">
<mandatory value="true"/>
<leaf name="server">
<type name="empty"/>
<description>
<text>If present, the change was caused
by the server.</text>
</description>
</leaf>
<case name="by-user">
<uses name="common-session-parms"/>
</case>
</choice>
</container>
</grouping>
<notification name="netconf-config-change">
<description>
<text>Generated when the NETCONF server detects that the
&lt;running&gt; or &lt;startup&gt; configuration datastore
has been changed by a management session.
The notification summarizes the edits that
have been detected.
The server MAY choose to also generate this
notification while loading a datastore during the
boot process for the device.</text>
</description>
<uses name="changed-by-parms"/>
<leaf name="datastore">
<type name="enumeration">
<enum name="running">
<description>
<text>The &lt;running&gt; datastore has changed.</text>
</description>
</enum>
<enum name="startup">
<description>
<text>The &lt;startup&gt; datastore has changed</text>
</description>
</enum>
</type>
<default value="running"/>
<description>
<text>Indicates which configuration datastore has changed.</text>
</description>
</leaf>
<list name="edit">
<description>
<text>An edit record SHOULD be present for each distinct
edit operation that the server has detected on
the target datastore. This list MAY be omitted
if the detailed edit operations are not known.
The server MAY report entries in this list for
changes not made by a NETCONF session (e.g., CLI).</text>
</description>
<leaf name="target">
<type name="instance-identifier"/>
<description>
<text>Topmost node associated with the configuration change.
A server SHOULD set this object to the node within
the datastore that is being altered. A server MAY
set this object to one of the ancestors of the actual
node that was changed, or omit this object, if the
exact node is not known.</text>
</description>
</leaf>
<leaf name="operation">
<type name="nc:edit-operation-type"/>
<description>
<text>Type of edit operation performed.
A server MUST set this object to the NETCONF edit
operation performed on the target datastore.</text>
</description>
</leaf>
</list>
</notification>
<notification name="netconf-capability-change">
<description>
<text>Generated when the NETCONF server detects that
the server capabilities have changed.
Indicates which capabilities have been added, deleted,
and/or modified. The manner in which a server
capability is changed is outside the scope of this
document.</text>
</description>
<uses name="changed-by-parms"/>
<leaf-list name="added-capability">
<type name="inet:uri"/>
<description>
<text>List of capabilities that have just been added.</text>
</description>
</leaf-list>
<leaf-list name="deleted-capability">
<type name="inet:uri"/>
<description>
<text>List of capabilities that have just been deleted.</text>
</description>
</leaf-list>
<leaf-list name="modified-capability">
<type name="inet:uri"/>
<description>
<text>List of capabilities that have just been modified.
A capability is considered to be modified if the
base URI for the capability has not changed, but
one or more of the parameters encoded at the end of
the capability URI have changed.
The new modified value of the complete URI is returned.</text>
</description>
</leaf-list>
</notification>
<notification name="netconf-session-start">
<description>
<text>Generated when a NETCONF server detects that a
NETCONF session has started. A server MAY generate
this event for non-NETCONF management sessions.
Indicates the identity of the user that started
the session.</text>
</description>
<uses name="common-session-parms"/>
</notification>
<notification name="netconf-session-end">
<description>
<text>Generated when a NETCONF server detects that a
NETCONF session has terminated.
A server MAY optionally generate this event for
non-NETCONF management sessions. Indicates the
identity of the user that owned the session,
and why the session was terminated.</text>
</description>
<uses name="common-session-parms"/>
<leaf name="killed-by">
<when condition="../termination-reason = 'killed'"/>
<type name="nc:session-id-type"/>
<description>
<text>The ID of the session that directly caused this session
to be abnormally terminated. If this session was abnormally
terminated by a non-NETCONF session unknown to the server,
then this leaf will not be present.</text>
</description>
</leaf>
<leaf name="termination-reason">
<type name="enumeration">
<enum name="closed">
<description>
<text>The session was terminated by the client in normal
fashion, e.g., by the NETCONF &lt;close-session&gt;
protocol operation.</text>
</description>
</enum>
<enum name="killed">
<description>
<text>The session was terminated in abnormal
fashion, e.g., by the NETCONF &lt;kill-session&gt;
protocol operation.</text>
</description>
</enum>
<enum name="dropped">
<description>
<text>The session was terminated because the transport layer
connection was unexpectedly closed.</text>
</description>
</enum>
<enum name="timeout">
<description>
<text>The session was terminated because of inactivity,
e.g., waiting for the &lt;hello&gt; message or &lt;rpc&gt;
messages.</text>
</description>
</enum>
<enum name="bad-hello">
<description>
<text>The client's &lt;hello&gt; message was invalid.</text>
</description>
</enum>
<enum name="other">
<description>
<text>The session was terminated for some other reason.</text>
</description>
</enum>
</type>
<mandatory value="true"/>
<description>
<text>Reason the session was terminated.</text>
</description>
</leaf>
</notification>
<notification name="netconf-confirmed-commit">
<description>
<text>Generated when a NETCONF server detects that a
confirmed-commit event has occurred. Indicates the event
and the current state of the confirmed-commit procedure
in progress.</text>
</description>
<reference>
<text>RFC 6241, Section 8.4</text>
</reference>
<uses name="common-session-parms">
<when condition="confirm-event != 'timeout'"/>
</uses>
<leaf name="confirm-event">
<type name="enumeration">
<enum name="start">
<description>
<text>The confirmed-commit procedure has started.</text>
</description>
</enum>
<enum name="cancel">
<description>
<text>The confirmed-commit procedure has been canceled,
e.g., due to the session being terminated, or an
explicit &lt;cancel-commit&gt; operation.</text>
</description>
</enum>
<enum name="timeout">
<description>
<text>The confirmed-commit procedure has been canceled
due to the confirm-timeout interval expiring.
The common session parameters will not be present
in this sub-mode.</text>
</description>
</enum>
<enum name="extend">
<description>
<text>The confirmed-commit timeout has been extended,
e.g., by a new &lt;confirmed-commit&gt; operation.</text>
</description>
</enum>
<enum name="complete">
<description>
<text>The confirmed-commit procedure has been completed.</text>
</description>
</enum>
</type>
<mandatory value="true"/>
<description>
<text>Indicates the event that caused the notification.</text>
</description>
</leaf>
<leaf name="timeout">
<when condition="../confirm-event = 'start' or ../confirm-event = 'extend'"/>
<type name="uint32"/>
<units name="seconds"/>
<description>
<text>The configured timeout value if the event type
is 'start' or 'extend'. This value represents
the approximate number of seconds from the event
time when the 'timeout' event might occur.</text>
</description>
</leaf>
</notification>
</module>

View file

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="ietf-netconf-with-defaults"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:ncwd="urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"
xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
<namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults"/>
<prefix value="ncwd"/>
<import module="ietf-netconf">
<prefix value="nc"/>
</import>
<organization>
<text>IETF NETCONF (Network Configuration Protocol) Working Group</text>
</organization>
<contact>
<text>WG Web: &lt;http://tools.ietf.org/wg/netconf/&gt;
WG List: &lt;netconf@ietf.org&gt;
WG Chair: Bert Wijnen
&lt;bertietf@bwijnen.net&gt;
WG Chair: Mehmet Ersue
&lt;mehmet.ersue@nsn.com&gt;
Editor: Andy Bierman
&lt;andy.bierman@brocade.com&gt;
Editor: Balazs Lengyel
&lt;balazs.lengyel@ericsson.com&gt;</text>
</contact>
<description>
<text>This module defines an extension to the NETCONF protocol
that allows the NETCONF client to control how default
values are handled by the server in particular NETCONF
operations.
Copyright (c) 2011 IETF Trust and the persons identified as
the document authors. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 6243; see
the RFC itself for full legal notices.</text>
</description>
<revision date="2011-06-01">
<description>
<text>Initial version.</text>
</description>
<reference>
<text>RFC 6243: With-defaults Capability for NETCONF</text>
</reference>
</revision>
<typedef name="with-defaults-mode">
<description>
<text>Possible modes to report default data.</text>
</description>
<reference>
<text>RFC 6243; Section 3.</text>
</reference>
<type name="enumeration">
<enum name="report-all">
<description>
<text>All default data is reported.</text>
</description>
<reference>
<text>RFC 6243; Section 3.1</text>
</reference>
</enum>
<enum name="report-all-tagged">
<description>
<text>All default data is reported.
Any nodes considered to be default data
will contain a 'default' XML attribute,
set to 'true' or '1'.</text>
</description>
<reference>
<text>RFC 6243; Section 3.4</text>
</reference>
</enum>
<enum name="trim">
<description>
<text>Values are not reported if they contain the default.</text>
</description>
<reference>
<text>RFC 6243; Section 3.2</text>
</reference>
</enum>
<enum name="explicit">
<description>
<text>Report values that contain the definition of
explicitly set data.</text>
</description>
<reference>
<text>RFC 6243; Section 3.3</text>
</reference>
</enum>
</type>
</typedef>
<grouping name="with-defaults-parameters">
<description>
<text>Contains the &lt;with-defaults&gt; parameter for control
of defaults in NETCONF retrieval operations.</text>
</description>
<leaf name="with-defaults">
<description>
<text>The explicit defaults processing mode requested.</text>
</description>
<reference>
<text>RFC 6243; Section 4.5.1</text>
</reference>
<type name="with-defaults-mode"/>
</leaf>
</grouping>
<augment target-node="/nc:get-config/nc:input">
<description>
<text>Adds the &lt;with-defaults&gt; parameter to the
input of the NETCONF &lt;get-config&gt; operation.</text>
</description>
<reference>
<text>RFC 6243; Section 4.5.1</text>
</reference>
<uses name="with-defaults-parameters"/>
</augment>
<augment target-node="/nc:get/nc:input">
<description>
<text>Adds the &lt;with-defaults&gt; parameter to
the input of the NETCONF &lt;get&gt; operation.</text>
</description>
<reference>
<text>RFC 6243; Section 4.5.1</text>
</reference>
<uses name="with-defaults-parameters"/>
</augment>
<augment target-node="/nc:copy-config/nc:input">
<description>
<text>Adds the &lt;with-defaults&gt; parameter to
the input of the NETCONF &lt;copy-config&gt; operation.</text>
</description>
<reference>
<text>RFC 6243; Section 4.5.1</text>
</reference>
<uses name="with-defaults-parameters"/>
</augment>
</module>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
module module-a-dv {
namespace "urn:jmu:params:xml:ns:yang:module-a-dv";
prefix dv;
import module-a {
prefix a;
}
description
"Contains some deviations to module-a";
deviation "/a:top/a:hidden" {
deviate not-supported;
}
}

View file

@ -0,0 +1,21 @@
module module-a-dv2 {
namespace "urn:jmu:params:xml:ns:yang:module-a-dv2";
prefix dv2;
import module-a {
prefix a;
}
description
"Contains some deviations to module-a";
deviation "/a:top/a:type" {
deviate add {
default "admin";
must "count(.) = 1";
}
}
}

View file

@ -0,0 +1,23 @@
module module-a {
namespace "urn:jmu:params:xml:ns:yang:module-a";
prefix a;
description "This is a simple user module";
container top {
leaf name {
type string;
}
leaf type {
type string;
}
leaf hidden {
type boolean;
}
}
}

View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<module name="nc-notifications"
xmlns="urn:ietf:params:xml:ns:yang:yin:1"
xmlns:manageEvent="urn:ietf:params:xml:ns:netmod:notification"
xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types"
xmlns:ncEvent="urn:ietf:params:xml:ns:netconf:notification:1.0">
<namespace uri="urn:ietf:params:xml:ns:netmod:notification"/>
<prefix value="manageEvent"/>
<import module="ietf-yang-types">
<prefix value="yang"/>
</import>
<import module="notifications">
<prefix value="ncEvent"/>
</import>
<organization>
<text>IETF NETCONF WG</text>
</organization>
<contact>
<text>netconf@ietf.org</text>
</contact>
<description>
<text>Conversion of the 'manageEvent' XSD in the NETCONF
Notifications RFC.</text>
</description>
<reference>
<text>RFC 5277</text>
</reference>
<revision date="2008-07-14">
<description>
<text>RFC 5277 version.</text>
</description>
</revision>
<container name="netconf">
<description>
<text>Top-level element in the notification namespace</text>
</description>
<config value="false"/>
<container name="streams">
<description>
<text>The list of event streams supported by the system. When
a query is issued, the returned set of streams is
determined based on user privileges.</text>
</description>
<list name="stream">
<description>
<text>Stream name, description and other information.</text>
</description>
<key value="name"/>
<min-elements value="1"/>
<leaf name="name">
<description>
<text>The name of the event stream. If this is the default
NETCONF stream, this must have the value 'NETCONF'.</text>
</description>
<type name="ncEvent:streamNameType"/>
</leaf>
<leaf name="description">
<description>
<text>A description of the event stream, including such
information as the type of events that are sent over
this stream.</text>
</description>
<type name="string"/>
<mandatory value="true"/>
</leaf>
<leaf name="replaySupport">
<description>
<text>A description of the event stream, including such
information as the type of events that are sent over
this stream.</text>
</description>
<type name="boolean"/>
<mandatory value="true"/>
</leaf>
<leaf name="replayLogCreationTime">
<description>
<text>The timestamp of the creation of the log used to support
the replay function on this stream. Note that this might
be earlier then the earliest available notification in
the log. This object is updated if the log resets for
some reason. This object MUST be present if replay is
supported.</text>
</description>
<type name="yang:date-and-time"/>
</leaf>
</list>
</container>
</container>
<notification name="replayComplete">
<description>
<text>This notification is sent to signal the end of a replay
portion of a subscription.</text>
</description>
</notification>
<notification name="notificationComplete">
<description>
<text>This notification is sent to signal the end of a notification
subscription. It is sent in the case that stopTime was
specified during the creation of the subscription..</text>
</description>
</notification>
</module>

Some files were not shown because too many files have changed in this diff Show more