Update C API: Add functions that simplify wrapper generation

This commit is contained in:
Joseph Henry
2021-03-24 12:20:39 -07:00
parent 4b190ee384
commit 9cd5fdee7b
8 changed files with 1895 additions and 554 deletions

View File

@@ -720,11 +720,11 @@ endif()
# -----------------------------------------------------------------------------
if(BUILD_HOST_SELFTEST)
add_executable(selftest ${PROJ_DIR}/test/selftest.c)
target_link_libraries(selftest ${STATIC_LIB_NAME})
add_executable(selftest-c-api ${PROJ_DIR}/test/selftest-c-api.c)
target_link_libraries(selftest-c-api ${STATIC_LIB_NAME})
project(TEST)
enable_testing()
add_test(NAME selftest COMMAND selftest)
add_test(NAME selftest-c-api COMMAND selftest-c-api)
endif()
# -----------------------------------------------------------------------------

View File

@@ -93,7 +93,7 @@ Important directories:
# Self-hosting (Optional)
We provide ways for your app or enterprise to function indepenently from any of our services if desired.
We provide ways for your app or enterprise to function independently from any of our services if desired.
While we do operate a global network of redundant root servers, network controllers and an admin API/UI called [Central](https://my.zerotier.com), some use-cases require full control over the infrastructure and we try to make it as easy as possible to set up your own controllers and root servers: See [here](https://github.com/zerotier/ZeroTierOne/tree/master/controller) to learn more about how to set up your own network controller, and [here](https://www.zerotier.com/manual/#4_4) to learn more about setting up your own roots.

View File

@@ -71,7 +71,7 @@ DEFAULT_HOST_LIB_OUTPUT_DIR=$BUILD_OUTPUT_DIR/$HOST_PLATFORM-$HOST_MACHINE_TYPE
DEFAULT_HOST_BIN_OUTPUT_DIR=$BUILD_OUTPUT_DIR/$HOST_PLATFORM-$HOST_MACHINE_TYPE
# Default location for (host) packages
DEFAULT_HOST_PKG_OUTPUT_DIR=$BUILD_OUTPUT_DIR/$HOST_PLATFORM-$HOST_MACHINE_TYPE
# Defaultlocation for CMake's caches (when building for host)
# Default location for CMake's caches (when building for host)
DEFAULT_HOST_BUILD_CACHE_DIR=$BUILD_CACHE_DIR/$HOST_PLATFORM-$HOST_MACHINE_TYPE
gethosttype()
@@ -89,8 +89,8 @@ gethosttype()
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/apple-xcframework-debug
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/apple-xcframework-debug
# - Build output : libzt/dist
#
# apple-xcframework-debug
# └── pkg
@@ -146,10 +146,10 @@ xcframework()
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/iphonesimulator-x64-framework-debug
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/iphonesimulator-x64-framework-debug
# - Build output : libzt/dist
#
# /Volumes/$USER/zt/libzt/libzt-dev/dist/iphonesimulator-x64-framework-debug
# libzt/dist/iphonesimulator-x64-framework-debug
# └── pkg
# └── zt.framework
# ├── Headers
@@ -195,10 +195,10 @@ iphonesimulator-framework()
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/macos-x64-framework-debug
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/macos-x64-framework-debug
# - Build output : libzt/dist
#
# /Volumes/$USER/zt/libzt/libzt-dev/dist/macos-x64-framework-debug
# libzt/dist/macos-x64-framework-debug
# └── pkg
# └── zt.framework
# ├── Headers
@@ -241,10 +241,10 @@ macos-framework()
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/iphoneos-arm64-framework-debug
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/iphoneos-arm64-framework-debug
# - Build output : libzt/dist
#
# /Volumes/$USER/zt/libzt/libzt-dev/dist/iphoneos-arm64-framework-debug
# libzt/dist/iphoneos-arm64-framework-debug
# └── pkg
# └── zt.framework
# ├── Headers
@@ -291,8 +291,8 @@ iphoneos-framework()
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/linux-x64-host-release
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/linux-x64-host-release
# - Build output : libzt/dist
#
# linux-x64-host-release
# ├── bin
@@ -307,6 +307,11 @@ host()
ARTIFACT="host"
# Default to release
BUILD_TYPE=${1:-release}
if [[ $1 = *"docs"* ]]; then
# Generate documentation
cd docs/c && doxygen
exit 0
fi
# -DZTS_ENABLE_CENTRAL_API=0
VARIANT="-DBUILD_HOST=True"
CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE
@@ -336,6 +341,29 @@ host-uninstall()
cd -
}
# Build C extension module (*.so), python module, package both into wheel
#
# ./build.sh host-python-wheel "release"
#
# Example output:
#
# libzt/dist/macos-x64-python-debug
# └── pkg
# └── libzt-1.3.4b1-cp39-cp39-macosx_11_0_x86_64.whl
#
host-python-wheel()
{
ARTIFACT="python"
# Default to release
BUILD_TYPE=${1:-release}
CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE
TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE
PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg
mkdir -p $PKG_OUTPUT_DIR
# Requires setuptools, etc
cd pkg/pypi && ./build.sh wheel && cp -f dist/*.whl $PKG_OUTPUT_DIR
}
# Build shared library with python wrapper symbols exported
host-python()
{
@@ -345,9 +373,8 @@ host-python()
VARIANT="-DZTS_ENABLE_PYTHON=True"
CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE
TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE
rm -rf $TARGET_BUILD_DIR
LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib
BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin
rm -rf $LIB_OUTPUT_DIR
mkdir -p $LIB_OUTPUT_DIR
# Optional step to generate new SWIG wrapper
swig -c++ -python -o src/bindings/python/zt_wrap.cpp -Iinclude src/bindings/python/zt.i
@@ -384,6 +411,11 @@ host-jar()
ARTIFACT="jar"
# Default to release
BUILD_TYPE=${1:-release}
if [[ $1 = *"docs"* ]]; then
# Generate documentation
javadoc src/bindings/java/*.java -d docs/java
exit 0
fi
VARIANT="-DZTS_ENABLE_JAVA=True"
CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE
TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE
@@ -402,7 +434,7 @@ host-jar()
cp -f $CACHE_DIR/lib/libzt.* $JAVA_JAR_DIR
cd $JAVA_JAR_DIR
export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
javac com/zerotier/libzt/*.java
javac -Xlint:deprecation com/zerotier/libzt/*.java
jar cf libzt-"$(git describe --abbrev=0)".jar $SHARED_LIB_NAME com/zerotier/libzt/*.class
rm -rf com $SHARED_LIB_NAME
cd -
@@ -441,8 +473,8 @@ fi
#
# Example output:
#
# - Cache : /Volumes/$USER/zt/libzt/libzt-dev/cache/android-any-android-release
# - Build output : /Volumes/$USER/zt/libzt/libzt-dev/dist
# - Cache : libzt/cache/android-any-android-release
# - Build output : libzt/dist
#
# android-any-android-release
# └── libzt-release.aar
@@ -496,10 +528,34 @@ test()
$TREE $TARGET_BUILD_DIR
# Test
cd $CACHE_DIR
ctest -C release
#ctest -C release
cd -
}
# Test C API
test-c()
{
if [[ -z "${alice_path}" ]]; then
echo "Please set necessary environment variables for test"
exit 0
fi
ARTIFACT="test"
# Default to debug so asserts aren't optimized out
BUILD_TYPE=${1:-debug}
TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE
BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin
rm -rf $TARGET_BUILD_DIR
# Build selftest
test $1
# Ports can be anything we want since they aren't system ports
port4=8080
port6=8081
# Start Alice as server
"$BIN_OUTPUT_DIR/selftest-c-api" $alice_path $testnet $port4 $port6 &
# Start Bob as client
"$BIN_OUTPUT_DIR/selftest-c-api" $bob_path $testnet $port4 $alice_ip4 $port6 $alice_ip6 &
}
# Recursive deep clean
clean()
{

File diff suppressed because it is too large Load Diff

View File

@@ -70,6 +70,7 @@ namespace ZeroTier
jobject objRef = NULL;
jmethodID _userCallbackMethodRef = NULL;
#endif
extern uint8_t _serviceStateFlags;
}
#ifdef __cplusplus
@@ -421,6 +422,9 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart(
int zts_free()
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (_getState(ZTS_STATE_FREE_CALLED)) {
return ZTS_ERR_SERVICE;
}

View File

@@ -59,12 +59,33 @@ int zts_connect(int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen)
if (!addr) {
return ZTS_ERR_ARG;
}
if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) {
if (addrlen > (int)sizeof(struct zts_sockaddr_storage)
|| addrlen < (int)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG;
}
return lwip_connect(fd, (sockaddr*)addr, addrlen);
}
int zts_connect_easy(int fd, int family, char *ipstr, int port) {
if (family == ZTS_AF_INET) {
struct zts_sockaddr_in in4;
zts_socklen_t addrlen = sizeof(in4);
ipstr2sockaddr(
family, ipstr, port, (struct zts_sockaddr *)&in4, &addrlen);
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in4;
return zts_connect(fd, sa, addrlen);
}
if (family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 in6;
zts_socklen_t addrlen = sizeof(in6);
ipstr2sockaddr(
family, ipstr, port, (struct zts_sockaddr *)&in6, &addrlen);
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in6;
return zts_connect(fd, sa, addrlen);
}
return ZTS_ERR_ARG;
}
int zts_bind(int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
@@ -73,12 +94,33 @@ int zts_bind(int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen)
if (!addr) {
return ZTS_ERR_ARG;
}
if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) {
if (addrlen > (int)sizeof(struct zts_sockaddr_storage)
|| addrlen < (int)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG;
}
return lwip_bind(fd, (sockaddr*)addr, addrlen);
}
int zts_bind_easy(int fd, int family, char *ipstr, int port) {
if (family == ZTS_AF_INET) {
struct zts_sockaddr_in in4;
zts_socklen_t addrlen = sizeof(in4);
ipstr2sockaddr(
family, ipstr, port, (struct zts_sockaddr *)&in4, &addrlen);
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in4;
return zts_bind(fd, sa, addrlen);
}
if (family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 in6;
zts_socklen_t addrlen = sizeof(in6);
ipstr2sockaddr(
family, ipstr, port, (struct zts_sockaddr *)&in6, &addrlen);
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in6;
return zts_bind(fd, sa, addrlen);
}
return ZTS_ERR_ARG;
}
int zts_listen(int fd, int backlog)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
@@ -95,7 +137,8 @@ int zts_accept(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen)
return lwip_accept(fd, (sockaddr*)addr, (socklen_t*)addrlen);
}
int zts_setsockopt(int fd, int level, int optname, const void *optval,zts_socklen_t optlen)
int zts_setsockopt(
int fd, int level, int optname, const void *optval,zts_socklen_t optlen)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
@@ -103,7 +146,8 @@ int zts_setsockopt(int fd, int level, int optname, const void *optval,zts_sockle
return lwip_setsockopt(fd, level, optname, optval, optlen);
}
int zts_getsockopt(int fd, int level, int optname, void *optval, zts_socklen_t *optlen)
int zts_getsockopt(
int fd, int level, int optname, void *optval, zts_socklen_t *optlen)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
@@ -119,7 +163,8 @@ int zts_getsockname(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen)
if (!addr) {
return ZTS_ERR_ARG;
}
if (*addrlen > (int)sizeof(struct zts_sockaddr_storage) || *addrlen < (int)sizeof(struct zts_sockaddr_in)) {
if (*addrlen > (int)sizeof(struct zts_sockaddr_storage)
|| *addrlen < (int)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG;
}
return lwip_getsockname(fd, (sockaddr*)addr, (socklen_t*)addrlen);
@@ -133,7 +178,8 @@ int zts_getpeername(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen)
if (!addr) {
return ZTS_ERR_ARG;
}
if (*addrlen > (int)sizeof(struct zts_sockaddr_storage) || *addrlen < (int)sizeof(struct zts_sockaddr_in)) {
if (*addrlen > (int)sizeof(struct zts_sockaddr_storage)
|| *addrlen < (int)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG;
}
return lwip_getpeername(fd, (sockaddr*)addr, (socklen_t*)addrlen);
@@ -147,13 +193,15 @@ int zts_close(int fd)
return lwip_close(fd);
}
int zts_select(int nfds, zts_fd_set *readfds, zts_fd_set *writefds, zts_fd_set *exceptfds,
int zts_select(
int nfds, zts_fd_set *readfds, zts_fd_set *writefds, zts_fd_set *exceptfds,
struct zts_timeval *timeout)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
return lwip_select(nfds, (fd_set*)readfds, (fd_set*)writefds, (fd_set*)exceptfds, (timeval*)timeout);
return lwip_select(
nfds, (fd_set*)readfds, (fd_set*)writefds, (fd_set*)exceptfds, (timeval*)timeout);
}
int zts_fcntl(int fd, int cmd, int flags)
@@ -203,7 +251,8 @@ ssize_t zts_sendto(int fd, const void *buf, size_t len, int flags,
if (!addr || !buf) {
return ZTS_ERR_ARG;
}
if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) {
if (addrlen > (int)sizeof(struct zts_sockaddr_storage)
|| addrlen < (int)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG;
}
return lwip_sendto(fd, buf, len, flags, (sockaddr*)addr, addrlen);
@@ -214,6 +263,7 @@ ssize_t zts_sendmsg(int fd, const struct zts_msghdr *msg, int flags)
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
return lwip_sendmsg(fd, (const struct msghdr *)msg, flags);
}
@@ -237,7 +287,8 @@ ssize_t zts_recvfrom(int fd, void *buf, size_t len, int flags,
if (!buf) {
return ZTS_ERR_ARG;
}
return lwip_recvfrom(fd, buf, len, flags, (sockaddr*)addr, (socklen_t*)addrlen);
return lwip_recvfrom(
fd, buf, len, flags, (sockaddr*)addr, (socklen_t*)addrlen);
}
ssize_t zts_recvmsg(int fd, struct zts_msghdr *msg, int flags)
@@ -262,12 +313,13 @@ ssize_t zts_read(int fd, void *buf, size_t len)
return lwip_read(fd, buf, len);
}
ssize_t zts_readv(int s, const struct zts_iovec *iov, int iovcnt)
ssize_t zts_readv(int fd, const struct zts_iovec *iov, int iovcnt)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
return lwip_readv(s, (iovec*)iov, iovcnt);
return lwip_readv(fd, (iovec*)iov, iovcnt);
}
ssize_t zts_write(int fd, const void *buf, size_t len)
@@ -275,6 +327,7 @@ ssize_t zts_write(int fd, const void *buf, size_t len)
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (!buf) {
return ZTS_ERR_ARG;
}
@@ -286,6 +339,7 @@ ssize_t zts_writev(int fd, const struct zts_iovec *iov, int iovcnt)
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
return lwip_writev(fd, (iovec*)iov, iovcnt);
}
@@ -344,19 +398,356 @@ int zts_ipaddr_aton(const char *cp, zts_ip_addr *addr)
return ipaddr_aton(cp, (ip_addr_t *)addr);
}
const char *zts_inet_ntop(int af, const void *src, char *dst, zts_socklen_t size)
const char *zts_inet_ntop(
int family, const void *src, char *dst, zts_socklen_t size)
{
return lwip_inet_ntop(af,src,dst,size);
return lwip_inet_ntop(family,src,dst,size);
}
int zts_inet_pton(int af, const char *src, void *dst)
int zts_inet_pton(int family, const char *src, void *dst)
{
return lwip_inet_pton(af,src,dst);
return lwip_inet_pton(family,src,dst);
}
//////////////////////////////////////////////////////////////////////////////
// Statistics //
//////////////////////////////////////////////////////////////////////////////
int ipstr2sockaddr(
int family, char *src_ipstr, int port, struct zts_sockaddr *dest_addr, zts_socklen_t *addrlen) {
if (family == ZTS_AF_INET) {
struct zts_sockaddr_in in4;
in4.sin_port = htons(port);
in4.sin_family = family;
#if defined(_WIN32)
zts_inet_pton(family, src_ipstr, &(in4.sin_addr.S_addr));
#else
zts_inet_pton(family, src_ipstr, &(in4.sin_addr.s_addr));
#endif
dest_addr = (struct zts_sockaddr *)&in4;
*addrlen = sizeof(in4);
return ZTS_ERR_OK;
}
if (family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 in6;
in6.sin6_port = htons(port);
in6.sin6_family = family;
#if defined(_WIN32)
zts_inet_pton(family, src_ipstr, &(in6.sin6_addr));
#else
zts_inet_pton(family, src_ipstr, &(in6.sin6_addr));
#endif
dest_addr = (struct zts_sockaddr *)&in6;
*addrlen = sizeof(in6);
return ZTS_ERR_OK;
}
return ZTS_ERR_ARG;
}
//----------------------------------------------------------------------------//
// Convenience functions //
//----------------------------------------------------------------------------//
/**
* Helper functions that simplify API wrapper generation and usage in other
* non-C-like languages. Use simple integer types instead of bit flags, limit
* the number of operations each function performs, prevent the user from
* needing to manipulate the content of structures in a non-native language.
*/
int zts_set_no_delay(int fd, int enabled) {
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (enabled != 0 && enabled != 1) {
return ZTS_ERR_ARG;
}
return zts_setsockopt(
fd, ZTS_IPPROTO_TCP, ZTS_TCP_NODELAY, (void *)&enabled, sizeof(int));
}
int zts_get_no_delay(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err, optval = 0;
zts_socklen_t len = sizeof(optval);
if ((err = zts_getsockopt(
fd, ZTS_IPPROTO_TCP, ZTS_TCP_NODELAY, (void *)&optval, &len)) < 0) {
return err;
}
return optval != 0;
}
int zts_set_linger(int fd, int enabled, int value) {
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (enabled != 0 && enabled != 1) {
return ZTS_ERR_ARG;
}
if (value < 0) {
return ZTS_ERR_ARG;
}
struct zts_linger linger;
linger.l_onoff = enabled;
linger.l_linger = value;
return zts_setsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void *)&linger, sizeof(linger));
}
int zts_get_linger_enabled(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err;
struct zts_linger linger;
zts_socklen_t len = sizeof(linger);
if ((err = zts_getsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void *)&linger, &len)) < 0) {
return err;
}
return linger.l_onoff;
}
int zts_get_linger_value(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err;
struct zts_linger linger;
zts_socklen_t len = sizeof(linger);
if ((err = zts_getsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void *)&linger, &len)) < 0) {
return err;
}
return linger.l_linger;
}
int zts_set_reuse_addr(int fd, int enabled)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (enabled != 0 && enabled != 1) {
return ZTS_ERR_ARG;
}
return zts_setsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void *)&enabled, sizeof(enabled));
}
int zts_get_reuse_addr(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err;
int optval = 0;
zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void *)&optval, &optlen)) < 0) {
return err;
}
return optval != 0;
}
int zts_set_recv_timeout(int fd, int seconds, int microseconds)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (seconds < 0 || microseconds < 0) {
return ZTS_ERR_ARG;
}
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = microseconds;
return zts_setsockopt(
fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
}
int zts_get_recv_timeout(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
struct timeval tv;
zts_socklen_t optlen = sizeof(tv);
int err;
if ((err = zts_getsockopt(
fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, &optlen)) < 0) {
return err;
}
return tv.tv_sec; // TODO microseconds
}
int zts_set_send_timeout(int fd, int seconds, int microseconds)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (seconds < 0 || microseconds < 0) {
return ZTS_ERR_ARG;
}
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = microseconds;
return zts_setsockopt(
fd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv));
}
int zts_get_send_timeout(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
struct zts_timeval tv;
zts_socklen_t optlen = sizeof(tv);
int err;
if ((err = zts_getsockopt(
fd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, &optlen)) < 0) {
return err;
}
return tv.tv_sec; // TODO microseconds
}
int zts_set_send_buf_size(int fd, int size)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (size < 0) {
return ZTS_ERR_ARG;
}
return zts_setsockopt(
fd, SOL_SOCKET, SO_SNDBUF, (void *)&size, sizeof(int));
}
int zts_get_send_buf_size(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err, optval = 0;
zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(
fd, SOL_SOCKET, SO_SNDBUF, (char *)&optval, &optlen)) < 0) {
return err;
}
return optval;
}
int zts_set_recv_buf_size(int fd, int size)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (size < 0) {
return ZTS_ERR_ARG;
}
return zts_setsockopt(
fd, SOL_SOCKET, SO_RCVBUF, (void *)&size, sizeof(int));
}
int zts_get_recv_buf_size(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err, optval = 0;
zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(
fd, SOL_SOCKET, SO_RCVBUF, (char *)&optval, &optlen)) < 0) {
return err;
}
return optval;
}
int zts_set_ttl(int fd, int ttl)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (ttl < 0 || ttl > 255) {
return ZTS_ERR_ARG;
}
return zts_setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
}
int zts_get_ttl(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err, ttl = 0;
zts_socklen_t optlen = sizeof(ttl);
if ((err = zts_getsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, &optlen)) < 0) {
return err;
}
return ttl;
}
int zts_set_blocking(int fd, int enabled)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (enabled != 0 && enabled != 1) {
return ZTS_ERR_ARG;
}
int flags = zts_fcntl(fd, ZTS_F_GETFL, 0);
if (!enabled) {
return zts_fcntl(fd, ZTS_F_SETFL, flags | ZTS_O_NONBLOCK);
}
else {
// Default
return zts_fcntl(fd, ZTS_F_SETFL, flags & (~ZTS_O_NONBLOCK));
}
}
int zts_get_blocking(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int flags = zts_fcntl(fd, ZTS_F_GETFL, 0);
if (flags < 0) {
return flags;
}
return !(flags & ZTS_O_NONBLOCK);
}
int zts_set_keepalive(int fd, int enabled)
{
//
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
if (enabled != 0 && enabled != 1) {
return ZTS_ERR_ARG;
}
int keepalive = enabled;
return zts_setsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, &keepalive , sizeof(keepalive));
}
int zts_get_keepalive(int fd)
{
if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
int err;
int optval = 0;
zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(
fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, (void *)&optval, &optlen)) < 0) {
return err;
}
return optval != 0;
}
//----------------------------------------------------------------------------//
// Statistics //
//----------------------------------------------------------------------------//
#ifdef ZTS_ENABLE_STATS

816
test/selftest-c-api.c Normal file
View File

@@ -0,0 +1,816 @@
/**
* Selftest. To be run for every commit.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#include <ZeroTierSockets.h>
#pragma GCC diagnostic ignored "-Wunused-value"
int random32() {
const int BITS_PER_RAND = (int)(log2(RAND_MAX/2 + 1) + 1.0);
int ret = 0;
for (int i = 0; i < sizeof(int) * CHAR_BIT; i += BITS_PER_RAND) {
ret <<= BITS_PER_RAND;
ret |= rand();
}
return ret;
}
uint64_t random64() {
return ((uint64_t)random32() << 32) | random32();
}
int is_online = 0;
int has_ip4 = 0;
int has_ip6 = 0;
//----------------------------------------------------------------------------//
// Event Handler //
//----------------------------------------------------------------------------//
void on_zts_event(void *msgPtr)
{
struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr;
fprintf(stderr, "event=%d\n", msg->eventCode);
if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
fprintf(stderr, "ZTS_EVENT_NODE_ONLINE\n");
is_online = 1;
}
if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) {
fprintf(stderr, "ZTS_EVENT_NETWORK_READY_IP4\n");
has_ip4 = 1;
}
if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) {
fprintf(stderr, "ZTS_EVENT_NETWORK_READY_IP6\n");
has_ip6 = 1;
}
}
void api_value_arg_test(
int8_t i8, int16_t i16, int32_t i32, int64_t i64, void* nullable)
{
//fprintf(stderr, "%d, %d, %d, %lld, %p\n", i8, i16, i32, i64, nullable);
int res = ZTS_ERR_OK;
//----------------------------------------------------------------------------//
// Test uninitialized Network Stack API usage //
//----------------------------------------------------------------------------//
/*
res = zts_get_all_stats((struct zts_stats *)nullable);
assert(("pre-init call to zts_get_all_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_get_protocol_stats(i32, nullable);
assert(("pre-init call to zts_get_protocol_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
*/
res = zts_dns_set_server(i8, (const zts_ip_addr *)nullable);
assert(("pre-init call to zts_add_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
const zts_ip_addr *res_ptr = zts_dns_get_server(i8);
assert(("pre-init call to zts_del_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//----------------------------------------------------------------------------//
// Test uninitialized Node API usage //
//----------------------------------------------------------------------------//
res = zts_stop();
assert(("pre-init call to zts_stop(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_restart();
assert(("pre-init call to zts_restart(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_free();
assert(("pre-init call to zts_free(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_join(i64);
assert(("pre-init call to zts_join(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_leave(i64);
assert(("pre-init call to zts_leave(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_orbit(i64,i64);
assert(("pre-init call to zts_orbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_deorbit(i64);
assert(("pre-init call to zts_deorbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//----------------------------------------------------------------------------//
// Test uninitialized Socket API usage //
//----------------------------------------------------------------------------//
res = zts_socket(i32,i32,i32);
assert(("pre-init call to zts_socket(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_connect(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_connect(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_bind(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_bind(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_listen(i32, i32);
assert(("pre-init call to zts_listen(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_accept(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_accept(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_setsockopt(i32, i32, i32, nullable, i32);
assert(("pre-init call to zts_setsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockopt(i32, i32, i32, nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockname(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockname(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getpeername(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getpeername(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_close(i32);
assert(("pre-init call to zts_close(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_select(i32, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (struct zts_timeval *)nullable);
assert(("pre-init call to zts_select(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_fcntl(i32, i32, i32);
assert(("pre-init call to zts_fcntl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_poll((struct zts_pollfd *)nullable, i32, i32);
assert(("pre-init call to zts_poll(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_ioctl(i32, i64, nullable);
assert(("pre-init call to zts_ioctl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_send(i32, nullable, i32, i32);
assert(("pre-init call to zts_send(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendto(i32, nullable, i32, i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_sendto(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendmsg(i32, (const struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_sendmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recv(i32, nullable, i32, i32);
assert(("pre-init call to zts_recv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvfrom(i32, nullable, i32, i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_recvfrom(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvmsg(i32, (struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_recvmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_read(i32, nullable, i32);
assert(("pre-init call to zts_read(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_readv(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_readv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_write(i32, nullable, i32);
assert(("pre-init call to zts_write(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_writev(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_writev(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_shutdown(i32, i32);
assert(("pre-init call to zts_shutdown(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
}
void test_pre_service()
{
//----------------------------------------------------------------------------//
// Test service-related API functions before initializing service //
//----------------------------------------------------------------------------//
// Test null values
api_value_arg_test(0,0,0,0,NULL);
// Test wild values
for (int i=0; i<4096; i++) {
int8_t i8 = (uint8_t)random64();
int16_t i16 = (uint16_t)random64();
int32_t i32 = (uint32_t)random64();
int64_t i64 = (uint64_t)random64();
int x;
void* nullable = &x;
api_value_arg_test(i8,i16,i32,i64,nullable);
}
//----------------------------------------------------------------------------//
// Test non-service helper functions //
//----------------------------------------------------------------------------//
// (B) Test zts_inet_ntop
char ipstr[ZTS_INET6_ADDRSTRLEN];
int16_t port = 0;
struct zts_sockaddr_in in4;
in4.sin_port = htons(8080);
#if defined(_WIN32)
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.S_addr));
#else
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.s_addr));
#endif
in4.sin_family = ZTS_AF_INET;
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in4;
if (sa->sa_family == ZTS_AF_INET) {
struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa;
zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr),
ipstr, ZTS_INET_ADDRSTRLEN);
port = ntohs(in4->sin_port);
}
if (sa->sa_family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa;
zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr),
ipstr, ZTS_INET6_ADDRSTRLEN);
}
assert(("zts_inet_ntop(): port != 8080", port == 8080));
assert(("zts_inet_ntop(): strcmp(ipstr, \"192.168.22.1\") != 0",
!strcmp(ipstr, "192.168.22.1")));
// (C) Test zts_inet_pton
uint8_t buf[sizeof(struct zts_in6_addr)];
char str[ZTS_INET6_ADDRSTRLEN];
zts_inet_pton(ZTS_AF_INET, "192.168.22.2", buf);
zts_inet_ntop(ZTS_AF_INET, buf, str, ZTS_INET6_ADDRSTRLEN);
assert(("zts_inet_pton(): strcmp(ipstr, \"192.168.22.2\") != 0",
!strcmp(str, "192.168.22.2")));
}
void test_service()
{
int res = ZTS_ERR_OK;
//----------------------------------------------------------------------------//
// Test simplified API, proxy for setsockopt/getsockopt/ioctl etc //
//----------------------------------------------------------------------------//
int s4 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(s4 >= 0);
// TCP_NODELAY
// Check value before doing anything
res = zts_get_no_delay(s4);
assert(res == 0);
// Turn on
res = zts_set_no_delay(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_no_delay(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_no_delay(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_no_delay(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
// SO_LINGER
// Check value before doing anything
res = zts_get_linger_enabled(s4);
assert(res == 0);
res = zts_get_linger_value(s4);
assert(res == 0);
// Turn on, set to 7 seconds
res = zts_set_linger(s4, 1, 7);
res = zts_get_linger_enabled(s4);
assert(res == 1);
res = zts_get_linger_value(s4);
assert(res == 7);
res = zts_set_linger(s4, 0, 0);
// Turn off
res = zts_get_linger_enabled(s4);
assert(res == 0);
res = zts_get_linger_value(s4);
assert(res == 0);
// SO_REUSEADDR
// Check value before doing anything
res = zts_get_reuse_addr(s4);
assert(res == 0);
// Turn on
res = zts_set_reuse_addr(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_reuse_addr(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_reuse_addr(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_reuse_addr(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
// SO_RCVTIMEO
// Check value before doing anything
res = zts_get_recv_timeout(s4);
assert(res == 0);
// Set to value
res = zts_set_recv_timeout(s4, 3, 0);
res = zts_get_recv_timeout(s4);
assert(res == 3);
res = zts_set_recv_timeout(s4, 0, 0);
// Set to zero
res = zts_get_recv_timeout(s4);
assert(res == 0);
// SO_SNDTIMEO
// Check value before doing anything
res = zts_get_send_timeout(s4);
assert(res == 0);
// Set to value
res = zts_set_send_timeout(s4, 4, 0);
res = zts_get_send_timeout(s4);
assert(res == 4);
res = zts_set_send_timeout(s4, 0, 0);
// Set to zero
res = zts_get_send_timeout(s4);
assert(res == 0);
// SO_SNDBUF
// Check value before doing anything
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
// Set to 7 seconds
res = zts_set_send_buf_size(s4, 1024);
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
res = zts_set_send_buf_size(s4, 0);
// Set to zero
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
// SO_RCVBUF
// Check value before doing anything
res = zts_get_recv_buf_size(s4);
assert(res > 0);
// Set to value
res = zts_set_recv_buf_size(s4, 1024);
res = zts_get_recv_buf_size(s4);
assert(res == 1024);
res = zts_set_recv_buf_size(s4, 0);
// Set to zero
res = zts_get_recv_buf_size(s4);
assert(res == 0);
// IP_TTL
// Check value before doing anything
res = zts_get_ttl(s4);
assert(res == 255); // Defaults to max
// Set to value
res = zts_set_ttl(s4, 128);
res = zts_get_ttl(s4);
assert(res == 128);
res = zts_set_ttl(s4, 0);
// Set to zero
res = zts_get_ttl(s4);
assert(res == 0);
// O_NONBLOCK
// Check value before doing anything
res = zts_get_blocking(s4);
assert(res == 1);
// Turn off (non-blocking)
res = zts_set_blocking(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_blocking(s4);
// Should return value instead of error code
assert(res == 0);
// Turn off
res = zts_set_blocking(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_blocking(s4);
assert(res == 1);
// SO_KEEPALIVE
// Check value before doing anything
res = zts_get_keepalive(s4);
assert(res == 0);
// Turn on
res = zts_set_keepalive(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_keepalive(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_keepalive(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_keepalive(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
//----------------------------------------------------------------------------//
// Test *_easy API //
//----------------------------------------------------------------------------//
//zts_connect_easy(0, ZTS_AF_INET, "192.168.7.9", 7878);
//zts_connect_easy(0, ZTS_AF_INET6, "FCC5:205E:4FF5:5311:DFF0::1", 7878);
//res = zts_bind_easy(0, ZTS_AF_INET6, "::", 8080);
//fprintf(stderr, "res=%d, zts_errno=%d\n", res, zts_errno);
//zts_delay_ms(60000);
//----------------------------------------------------------------------------//
// Test DNS client functionality //
//----------------------------------------------------------------------------//
/*
// Set first nameserver
char *ns1_addr_str = "FCC5:205E:4FF5:5311:DFF0::1";
zts_ip_addr ns1;
zts_ipaddr_aton(ns1_addr_str, &ns1);
zts_dns_set_server(0, &ns1);
// Get first nameserver
const zts_ip_addr *ns1_result;
ns1_result = zts_dns_get_server(0);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns1_result));
// Set second nameserver
char *ns2_addr_str = "192.168.22.1";
zts_ip_addr ns2;
zts_ipaddr_aton(ns2_addr_str, &ns2);
zts_dns_set_server(1, &ns2);
// Get second nameserver
const zts_ip_addr *ns2_result;
ns2_result = zts_dns_get_server(1);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns2_result));
// Check that each nameserver address was properly set and get
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns1_addr_str, zts_ipaddr_ntoa(ns1_result))));
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns2_addr_str, zts_ipaddr_ntoa(ns2_result))));
*/
//----------------------------------------------------------------------------//
// Test shutting down the service //
//----------------------------------------------------------------------------//
zts_stop();
s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s4 != ZTS_ERR_SERVICE, not shut down", s4 == ZTS_ERR_SERVICE));
}
//----------------------------------------------------------------------------//
// Server //
//----------------------------------------------------------------------------//
#define MAX_CONNECT_TIME 60
#define BUFLEN 128
char *msg = "welcome to the machine";
void start_server_app(uint16_t port4, uint16_t port6)
{
int err = ZTS_ERR_OK;
int bytes_read = 0;
int bytes_sent = 0;
int msglen = strlen(msg);
char dstbuf[BUFLEN];
int buflen = BUFLEN;
struct timespec start, now;
int time_diff = 0;
//
// IPv4 test
//
fprintf(stderr, "server4: will listen on: 0.0.0.0:%d\n", port4);
int s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(s4 == 0 && zts_errno == 0);
err = zts_bind_easy(s4, ZTS_AF_INET, "0.0.0.0", port4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
err = zts_listen(s4, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
struct zts_sockaddr_in in4;
zts_socklen_t addrlen4 = sizeof(in4);
int acc4 = -1;
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "server4: accepting...\n");
acc4 = zts_accept(s4, &in4, &addrlen4);
zts_delay_ms(250);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(acc4 == 1 && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(acc4, dstbuf, buflen);
fprintf(stderr, "server4: read (%d) bytes\n", bytes_read);
assert(bytes_read == msglen && zts_errno == 0);
// Send message
bytes_sent = zts_write(acc4, msg, msglen);
fprintf(stderr, "server4: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
zts_close(s4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_close(acc4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
//
// IPv6 test
//
fprintf(stderr, "server: will listen on: [::]:%d\n", port6);
int s6 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(s6 == 0 && zts_errno == 0);
err = zts_bind_easy(s6, ZTS_AF_INET6, "::", port6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
err = zts_listen(s6, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
struct zts_sockaddr_in6 in6;
zts_socklen_t addrlen6 = sizeof(in6);
int acc6 = -1;
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "server6: accepting...\n");
acc6 = zts_accept(s6, &in6, &addrlen6);
zts_delay_ms(250);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
fprintf(stderr, "server6: accepted connection (fd=%d)\n", acc6);
assert(acc6 == 1 && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(acc6, dstbuf, buflen);
fprintf(stderr, "server6: read (%d) bytes\n", bytes_read);
assert(bytes_read == msglen && zts_errno == 0);
// Send message
bytes_sent = zts_write(acc6, msg, msglen);
fprintf(stderr, "server6: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
zts_close(s6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_close(acc6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_stop();
assert(err == ZTS_ERR_OK && zts_errno == 0);
int s = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s != ZTS_ERR_SERVICE, not shut down", s == ZTS_ERR_SERVICE));
}
//----------------------------------------------------------------------------//
// Client //
//----------------------------------------------------------------------------//
void start_client_app(char *ip4, uint16_t port4, char *ip6, uint16_t port6)
{
int err = ZTS_ERR_OK;
int bytes_read = 0;
int bytes_sent = 0;
int msglen = strlen(msg);
char dstbuf[BUFLEN];
int buflen = BUFLEN;
struct timespec start, now;
int time_diff = 0;
//
// IPv4 test
//
err = ZTS_ERR_OK;
int s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_set_blocking(s4, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "client4: connecting to: %s:%d\n", ip4, port4);
err = zts_connect_easy(s4, ZTS_AF_INET, ip4, port4);
zts_delay_ms(500);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(err == ZTS_ERR_OK && zts_errno == 0);
fprintf(stderr, "client4: connected\n");
// Send message
bytes_sent = zts_write(s4, msg, msglen);
fprintf(stderr, "client4: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(s4, dstbuf, buflen);
assert(bytes_read == msglen && zts_errno == 0);
fprintf(stderr, "client4: read (%d) bytes\n", bytes_read);
assert(bytes_sent == bytes_read && zts_errno == 0);
zts_close(s4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
//
// IPv6 test
//
err = ZTS_ERR_OK;
int s6 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_set_blocking(s6, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "client6: connecting to: %s:%d\n", ip6, port6);
err = zts_connect_easy(s6, ZTS_AF_INET6, ip6, port6);
zts_delay_ms(500);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(err == ZTS_ERR_OK && zts_errno == 0);
fprintf(stderr, "client6: connected\n");
// Send message
bytes_sent = zts_write(s6, msg, msglen);
fprintf(stderr, "client6: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(s6, dstbuf, buflen);
assert(bytes_read == msglen && zts_errno == 0);
fprintf(stderr, "client6: read (%d) bytes\n", bytes_read);
assert(bytes_sent == bytes_read && zts_errno == 0);
zts_close(s6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_stop();
assert(err == ZTS_ERR_OK && zts_errno == 0);
int s = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s != ZTS_ERR_SERVICE, not shut down", s == ZTS_ERR_SERVICE));
}
//----------------------------------------------------------------------------//
// Start node //
//----------------------------------------------------------------------------//
void start_node(char *path, uint64_t nwid)
{
struct timespec start, now;
int time_diff = 0;
fprintf(stderr, "starting node...\n");
clock_gettime(CLOCK_MONOTONIC, &start);
int res = zts_start(path, &on_zts_event, 0);
assert(("error starting service: res != ZTS_ERR_OK", res == ZTS_ERR_OK));
do {
zts_delay_ms(25);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (!is_online && (time_diff < MAX_CONNECT_TIME));
if (!is_online) {
fprintf(stderr, "node failed to come online\n");
exit(-1);
}
fprintf(stderr, "joining: %llx\n", nwid);
clock_gettime(CLOCK_MONOTONIC, &start);
if (nwid) {
zts_join(nwid);
do {
zts_delay_ms(25);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while ((!has_ip4 || !has_ip6) && (time_diff < MAX_CONNECT_TIME));
if (!has_ip4 || !has_ip6) {
fprintf(stderr, "node failed to receive assigned addresses\n");
exit(-1);
}
}
}
//----------------------------------------------------------------------------//
// Main //
//----------------------------------------------------------------------------//
int main(int argc, char **argv)
{
if (argc != 1 && argc != 5 && argc != 7) {
fprintf(stderr, "Invalid number of arguments.\n");
exit(-1);
}
//
// API fuzz test
//
test_pre_service();
//
// Default test
//
// selftest
if (argc == 1) {
srand(time(NULL));
// Store identities in cwd, join 0x0
start_node(".",0x0);
test_service();
exit(0);
}
// Default test (single node)
// selftest <id-path>
/*
if (argc == 2) {
srand(time(NULL));
start_node(argv[1],0x0);
test_service();
exit(0);
}*/
//
// Client/Server communication test
//
// Server test
if (argc == 5) {
//fprintf(stderr, "server.path = %s\n", argv[1]);
//fprintf(stderr, "server.nwid = %s\n", argv[2]);
//fprintf(stderr, "server.port4 = %s\n", argv[3]);
//fprintf(stderr, "server.port6 = %s\n", argv[4]);
uint64_t nwid = strtoull(argv[2],NULL,16);
int port4 = atoi(argv[3]);
int port6 = atoi(argv[4]);
start_node(argv[1],nwid);
start_server_app(port4, port6);
exit(0);
}
// Client test
if (argc == 7) {
//fprintf(stderr, "client.path = %s\n", argv[1]);
//fprintf(stderr, "client.nwid = %s\n", argv[2]);
//fprintf(stderr, "client.port4 = %s\n", argv[3]);
//fprintf(stderr, "client.ip4 = %s\n", argv[4]);
//fprintf(stderr, "client.port6 = %s\n", argv[5]);
//fprintf(stderr, "client.ip6 = %s\n", argv[6]);
uint64_t nwid = strtoull(argv[2],NULL,16);
int port4 = atoi(argv[3]);
int port6 = atoi(argv[5]);
start_node(argv[1],nwid);
start_client_app(argv[4], port4, argv[6], port6);
exit(0);
}
return 0;
}

View File

@@ -1,272 +0,0 @@
/**
* Selftest. To be run for every commit.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <ZeroTierSockets.h>
#pragma GCC diagnostic ignored "-Wunused-value"
int random32() {
const int BITS_PER_RAND = (int)(log2(RAND_MAX/2 + 1) + 1.0);
int ret = 0;
for (int i = 0; i < sizeof(int) * CHAR_BIT; i += BITS_PER_RAND) {
ret <<= BITS_PER_RAND;
ret |= rand();
}
return ret;
}
uint64_t random64() {
return ((uint64_t)random32() << 32) | random32();
}
void api_value_arg_test(int8_t i8, int16_t i16, int32_t i32, int64_t i64, void* nullable)
{
//fprintf(stderr, "%d, %d, %d, %lld, %p\n", i8, i16, i32, i64, nullable);
int res = ZTS_ERR_OK;
//------------------------------------------------------------------------------
// Test uninitialized Network Stack API usage |
//------------------------------------------------------------------------------
res = zts_get_all_stats((struct zts_stats *)nullable);
assert(("pre-init call to zts_get_all_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_get_protocol_stats(i32, nullable);
assert(("pre-init call to zts_get_protocol_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_dns_set_server(i8, (const zts_ip_addr *)nullable);
assert(("pre-init call to zts_add_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
const zts_ip_addr *res_ptr = zts_dns_get_server(i8);
assert(("pre-init call to zts_del_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//------------------------------------------------------------------------------
// Test uninitialized Node API usage |
//------------------------------------------------------------------------------
res = zts_stop();
assert(("pre-init call to zts_stop(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_restart();
assert(("pre-init call to zts_restart(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_free();
assert(("pre-init call to zts_free(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_join(i64);
assert(("pre-init call to zts_join(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_leave(i64);
assert(("pre-init call to zts_leave(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_orbit(i64,i64);
assert(("pre-init call to zts_orbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_deorbit(i64);
assert(("pre-init call to zts_deorbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//------------------------------------------------------------------------------
// Test uninitialized Socket API usage |
//------------------------------------------------------------------------------
res = zts_socket(i32,i32,i32);
assert(("pre-init call to zts_socket(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_connect(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_connect(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_bind(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_bind(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_listen(i32, i32);
assert(("pre-init call to zts_listen(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_accept(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_accept(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_setsockopt(i32, i32, i32, nullable, i32);
assert(("pre-init call to zts_setsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockopt(i32, i32, i32, nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockname(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockname(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getpeername(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getpeername(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_close(i32);
assert(("pre-init call to zts_close(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_select(i32, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (struct zts_timeval *)nullable);
assert(("pre-init call to zts_select(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_fcntl(i32, i32, i32);
assert(("pre-init call to zts_fcntl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_poll((struct zts_pollfd *)nullable, i32, i32);
assert(("pre-init call to zts_poll(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_ioctl(i32, i64, nullable);
assert(("pre-init call to zts_ioctl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_send(i32, nullable, i32, i32);
assert(("pre-init call to zts_send(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendto(i32, nullable, i32, i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_sendto(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendmsg(i32, (const struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_sendmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recv(i32, nullable, i32, i32);
assert(("pre-init call to zts_recv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvfrom(i32, nullable, i32, i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_recvfrom(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvmsg(i32, (struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_recvmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_read(i32, nullable, i32);
assert(("pre-init call to zts_read(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_readv(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_readv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_write(i32, nullable, i32);
assert(("pre-init call to zts_write(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_writev(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_writev(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_shutdown(i32, i32);
assert(("pre-init call to zts_shutdown(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
}
int main()
{
srand(time(NULL));
//------------------------------------------------------------------------------
// Test service-related API functions before initializing service |
//------------------------------------------------------------------------------
// Test null values
api_value_arg_test(0,0,0,0,NULL);
// Test wild values
for (int i=0; i<1024; i++) {
int8_t i8 = (uint8_t)random64();
int16_t i16 = (uint16_t)random64();
int32_t i32 = (uint32_t)random64();
int64_t i64 = (uint64_t)random64();
int x;
void* nullable = &x;
api_value_arg_test(i8,i16,i32,i64,nullable);
}
//------------------------------------------------------------------------------
// Test non-service helper functions |
//------------------------------------------------------------------------------
// (B) Test zts_inet_ntop
char ipstr[ZTS_INET6_ADDRSTRLEN];
int16_t port = 0;
struct zts_sockaddr_in in4;
in4.sin_port = htons(8080);
#if defined(_WIN32)
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.S_addr));
#else
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.s_addr));
#endif
in4.sin_family = ZTS_AF_INET;
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in4;
if (sa->sa_family == ZTS_AF_INET) {
struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa;
zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr),
ipstr, ZTS_INET_ADDRSTRLEN);
port = ntohs(in4->sin_port);
}
if (sa->sa_family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa;
zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr),
ipstr, ZTS_INET6_ADDRSTRLEN);
}
assert(("zts_inet_ntop(): port != 8080", port == 8080));
assert(("zts_inet_ntop(): strcmp(ipstr, \"192.168.22.1\") != 0",
!strcmp(ipstr, "192.168.22.1")));
// (C) Test zts_inet_pton
uint8_t buf[sizeof(struct zts_in6_addr)];
char str[ZTS_INET6_ADDRSTRLEN];
zts_inet_pton(ZTS_AF_INET, "192.168.22.2", buf);
zts_inet_ntop(ZTS_AF_INET, buf, str, ZTS_INET6_ADDRSTRLEN);
assert(("zts_inet_pton(): strcmp(ipstr, \"192.168.22.2\") != 0",
!strcmp(str, "192.168.22.2")));
//------------------------------------------------------------------------------
// Start the service and test a few things |
//------------------------------------------------------------------------------
// zts_start();
//------------------------------------------------------------------------------
// Test DNS client functionality |
//------------------------------------------------------------------------------
/*
// Set first nameserver
char *ns1_addr_str = "FCC5:205E:4FF5:5311:DFF0::1";
zts_ip_addr ns1;
zts_ipaddr_aton(ns1_addr_str, &ns1);
zts_dns_set_server(0, &ns1);
// Get first nameserver
const zts_ip_addr *ns1_result;
ns1_result = zts_dns_get_server(0);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns1_result));
// Set second nameserver
char *ns2_addr_str = "192.168.22.1";
zts_ip_addr ns2;
zts_ipaddr_aton(ns2_addr_str, &ns2);
zts_dns_set_server(1, &ns2);
// Get second nameserver
const zts_ip_addr *ns2_result;
ns2_result = zts_dns_get_server(1);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns2_result));
// Check that each nameserver address was properly set and get
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns1_addr_str, zts_ipaddr_ntoa(ns1_result))));
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns2_addr_str, zts_ipaddr_ntoa(ns2_result))));
*/
return 0;
}