diff --git a/CMakeLists.txt b/CMakeLists.txt index 30bb7d2f6..7b908a8c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ option(USE_SSL "Build with SIPS support" OFF) option(USE_SCTP "Build with SCTP support" OFF) option(USE_PCAP "Build with PCAP playback support" OFF) option(USE_GSL "Build with improved statistical support" ON) +option(USE_LUA "Build with lua support for plugins" ON) file(GLOB all_SRCS "${PROJECT_SOURCE_DIR}/src/*.cpp" @@ -74,25 +75,50 @@ if(USE_SSL) add_definitions("-DUSE_TLS -DUSE_OPENSSL") endif() +if (USE_LUA) + add_definitions("-DUSE_LUA") +endif() + if(USE_PCAP) - add_definitions("-DPCAPPLAY") + find_library(PCAP_LIBRARY pcap) + if(PCAP_LIBRARY) + add_definitions("-DPCAPPLAY") + else() + message(FATAL_ERROR "libpcap not found") + endif() endif(USE_PCAP) if(USE_GSL) find_library(GSL_LIBRARY gsl) if(GSL_LIBRARY) add_definitions("-DHAVE_GSL") - endif(GSL_LIBRARY) + else() + message(FATAL_ERROR "libgsl not found") + endif() endif(USE_GSL) if(USE_SCTP) - add_definitions("-DUSE_SCTP") + find_library(SCTP_LIBRARY sctp) + if(SCTP_LIBRARY) + add_definitions("-DUSE_SCTP") + else() + message(FATAL_ERROR "libsctp not found") + endif() endif(USE_SCTP) + list(REMOVE_ITEM all_SRCS + "${PROJECT_SOURCE_DIR}/src/myapp.cpp") + + list(REMOVE_ITEM all_SRCS + "${PROJECT_SOURCE_DIR}/src/sipp-rpc_xdr.c") + # add the executable link_directories("/usr/local/lib") add_executable(sipp ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp.cpp") add_executable(sipp_unittest EXCLUDE_FROM_ALL ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp") +target_link_libraries(sipp -rdynamic -fPIC -dl -llua5.3 ) +add_library(myapp SHARED ${PROJECT_SOURCE_DIR}/src/myapp.cpp ${PROJECT_SOURCE_DIR}/src/sipp-rpc_xdr.c ) +target_link_libraries(myapp -rdynamic -fPIC -dl -llua5.3 -lcurl -lrt -lcrypto -lperconaserverclient ) target_compile_definitions(sipp_unittest PUBLIC "-DGTEST") # add version @@ -161,6 +187,8 @@ else() message(FATAL_ERROR "libcurses / libncurses was not found; please install devel package") endif() +include_directories(${LUA_LIBRARY_INCLUDE_DIR} ${CURL_LIBRARY_INCLUDE_DIR} ${MYSQL_LIBRARY_INCLUDE_DIR}) + find_library(RT_LIBRARY NAMES rt) if(RT_LIBRARY) target_link_libraries(sipp ${RT_LIBRARY}) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..633dcbf24 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,204 @@ +@AM_GIT_VERSION@ + +bin_PROGRAMS = sipp + +TESTS = sipp_unittest + +check_PROGRAMS = $(TESTS) + +DEFAULT_INCLUDES = -I. -I$(top_builddir)/include -I/usr/include/lua5.3 -I/usr/include/x86_64-linux-gnu/curl -I/usr/include/mysql +AM_CFLAGS=-I$(srcdir)/include -Wall -pedantic +AM_CXXFLAGS=-I$(srcdir)/include -Wall -pedantic + +CLEANFILES = +EXTRA_DIST = \ + $(srcdir)/src/fortune.cpp \ + $(srcdir)/LICENSE.txt \ + $(srcdir)/README.md \ + $(srcdir)/THANKS \ + $(srcdir)/sipp.dtd \ + $(srcdir)/cpplint.py + +if HAVE_OPENSSL +DEFS += -DUSE_OPENSSL +ssl_incl = \ + include/sslcommon.h +ssl_SOURCES = \ + $(ssl_incl) \ + src/sslinit.c \ + src/sslthreadsafe.c +endif + +if HAVE_PCAP +DEFS += -DPCAPPLAY +pcap_incl = \ + include/prepare_pcap.h \ + include/send_packets.h +pcap_SOURCES = \ + $(pcap_incl) \ + src/prepare_pcap.c \ + src/send_packets.c +endif + +if HAVE_RTP +DEFS += -DRTP_STREAM +rtp_SOURCES = \ + src/rtpstream.cpp \ + include/rtpstream.hpp +endif + +if HAVE_SCTP +DEFS += -DUSE_SCTP +endif + +if HAVE_GSL +DEFS += -DHAVE_GSL +endif + +if HAVE_EPOLL +DEFS += -DHAVE_EPOLL +endif + +common_incl = \ + include/comp.h \ + include/infile.hpp \ + include/listener.hpp \ + include/logger.hpp \ + include/md5.h \ + include/message.hpp \ + include/milenage.h \ + include/call_generation_task.hpp \ + include/ratetask.hpp \ + include/reporttask.hpp \ + include/rijndael.h \ + include/scenario.hpp \ + include/sip_parser.hpp \ + include/screen.hpp \ + include/socket.hpp \ + include/socketowner.hpp \ + include/stat.hpp \ + include/strings.hpp \ + include/task.hpp \ + include/time.hpp \ + include/variables.hpp \ + include/watchdog.hpp \ + include/xp_parser.h \ + include/sipp_rpc.h \ + include/actions.hpp \ + include/call.hpp \ + include/auth.hpp \ + include/deadcall.hpp + +common_SOURCES = \ + src/actions.cpp \ + src/auth.cpp \ + src/comp.c \ + src/call.cpp \ + src/apifunc.cpp \ + src/customfuncs.cpp \ + src/deadcall.cpp \ + src/infile.cpp \ + src/listener.cpp \ + src/logger.cpp \ + src/md5.c \ + src/message.cpp \ + src/milenage.c \ + src/call_generation_task.cpp \ + src/ratetask.cpp \ + src/reporttask.cpp \ + src/rijndael.c \ + src/scenario.cpp \ + src/sip_parser.cpp \ + src/screen.cpp \ + src/socket.cpp \ + src/socketowner.cpp \ + src/stat.cpp \ + src/strings.cpp \ + src/task.cpp \ + src/time.cpp \ + src/variables.cpp \ + src/watchdog.cpp \ + src/xp_parser.c \ + src/sipp-rpc_xdr.c \ + $(common_incl) \ + $(ssl_SOURCES) \ + $(pcap_SOURCES) \ + $(rtp_SOURCES) + +sipp_SOURCES = \ + $(common_SOURCES) \ + src/sipp.cpp \ + include/sipp.hpp + +sipp_CFLAGS = $(AM_CFLAGS) @GSL_CFLAGS@ +sipp_CXXFLAGS = $(AM_CXXFLAGS) @GSL_CXXFLAGS@ +sipp_LDADD = @LIBOBJS@ @GSL_LIBS@ + +# call.cpp and sipp.cpp use version.h; see AM_GIT_VERSION. +src/call.cpp: include/version.h +src/sipp.cpp: include/version.h + +# Make a shared library to use as a plugin +myapp: myapp.so + +src/myapp.o: src/myapp.cpp + $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) -rdynamic -fPIC $(CPPFLAGS) $(sipp_CXXFLAGS) $(CXXFLAGS) -c src/myapp.cpp -o src/myapp.o + echo "done building shared custom app" + + +myapp.so: src/myapp.o + g++ -rdynamic -fPIC -shared $(SO_OPTION) src/myapp.o $(LIBS) -o $@ + + +gtest_SOURCES = \ + gtest/src/gtest-death-test.cc \ + gtest/src/gtest-filepath.cc \ + gtest/src/gtest-internal-inl.h \ + gtest/src/gtest-port.cc \ + gtest/src/gtest-printers.cc \ + gtest/src/gtest-test-part.cc \ + gtest/src/gtest-typed-test.cc \ + gtest/src/gtest.cc \ + gmock/src/gmock-internal-utils.cc \ + gmock/src/gmock-matchers.cc + +sipp_unittest_SOURCES = \ + $(common_SOURCES) \ + src/sipp_unittest.cpp \ + src/xp_parser_ut.cpp \ + $(gtest_SOURCES) + +sipp_unittest_CFLAGS = $(AM_CFLAGS) -DGTEST=1 \ + -I$(srcdir)/gtest/include \ + -I$(srcdir)/gmock/include \ + -I$(srcdir)/gtest \ + -I$(srcdir)/gmock \ + @GSL_CFLAGS@ + +sipp_unittest_CXXFLAGS = $(AM_CXXFLAGS) -DGTEST=1 \ + -I$(srcdir)/gtest/include \ + -I$(srcdir)/gmock/include \ + -I$(srcdir)/gtest \ + -I$(srcdir)/gmock \ + @GSL_CXXFLAGS@ + +sipp_unittest_LDADD = @LIBOBJS@ @GSL_LIBS@ + +# Ensure that after a reconfigure the source is cleaned. +.autoclean: Makefile + make clean + echo > .autoclean +BUILT_SOURCES = .autoclean + +if HAVE_HELP2MAN +man_MANS = sipp.1 +CLEANFILES += $(man_MANS) + +sipp.1: ./sipp $(sipp_SOURCES) + $(HELP2MAN) --output=$@ -v "-v" --no-info \ + --name='SIP testing tool and traffic generator' \ + ./sipp +else +sipp.1: + @echo "Warning: help2man not available, no man page is created." +endif diff --git a/README.md b/README.md index 2cb9888ef..3ab7643a7 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ and for now, it only works on Alpine Linux. To build a static binary, pass `-DBUILD_STATIC=1` to cmake. +Note for trustid - normally we build with: build.sh --with-openssl + # Support I try and be responsive to issues raised on Github, and there's [a diff --git a/README_DEMO_PLUGIN.md b/README_DEMO_PLUGIN.md new file mode 100644 index 000000000..4f4bd40f5 --- /dev/null +++ b/README_DEMO_PLUGIN.md @@ -0,0 +1,15 @@ +A demo of the plugin functionality of sipp with a simple lua script can be found in the docs directory. +1. sipp_demo.sh - this will executing uas_w_script.xml +2. uas_w_script.xml + +Just send a call to it to see it utlize a plugin + +To create and use the plugin - +1. enable LUA and then build sipp with whatever other options you want to use. +example: +cmake . -DCMAKE_BUILD_TYPE=Debug -DUSE_GSL=0 -DUSE_SSL=1 -DUSE_SCTP=0 -DUSE_PCAP=0 -DUSE_LUA=1 +2. Use the -plugin option to specify the location of your plugin. There is a demo plugin in myapp.cpp which can be used the create libmyapp.so +3. The demo plugin adds functionality to interpret the new sipp flags -lua_file and -pid to record the pid of the file and to specify which lua script will be used +4. The xml to call the lua script and retrieve values can be found in the xml file uas_w_script.xml + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..627cb31b3 --- /dev/null +++ b/build.gradle @@ -0,0 +1,169 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample C++ project to get you started. + * For more details take a look at the Building C++ applications and libraries chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.0.2/userguide/building_cpp_projects.html + */ + +plugins { + // Apply the cpp-application plugin to add support for building C++ executables + id 'cpp-application' + + // Apply the cpp-unit-test plugin to add support for building and running C++ test executables + //id 'cpp-unit-test' +} + +// Set the target operating system and architecture for this application +application { + targetMachines.add(machines.linux.x86_64) +} + +task cleanrpc(type:Exec) { + + doFirst { + println("Cleaning files for sip:rpc") + } + workingDir 'rpc' + + //on linux + commandLine 'make','clean' + + //store the output instead of printing to the console: + standardOutput = new ByteArrayOutputStream() + + //extension method stopTomcat.output() can be used to obtain the output: + ext.output = { + return standardOutput.toString() + } + + doLast { + println("Finished cleaning files for sipp:rpc") + } +} + +task cleansipp(type:Exec) { + + doFirst { + println("Cleaning files") + } + workingDir '.' + + //on linux + commandLine 'make','clean' + + //store the output instead of printing to the console: + standardOutput = new ByteArrayOutputStream() + + //extension method stopTomcat.output() can be used to obtain the output: + ext.output = { + return standardOutput.toString() + } + + doLast { + println("Finished cleaning files") + } + +if (!file("./Makefile").exists()){ + enabled = false +} + +} + +task buildrpc(type:Exec) { + + doFirst { + println("Running build rpc for sipp") + } + + dependsOn tasks.cleanrpc + + workingDir 'rpc' + + //on linux + commandLine 'make' + + //store the output instead of printing to the console: + standardOutput = new ByteArrayOutputStream() + + //extension method stopTomcat.output() can be used to obtain the output: + ext.output = { + return standardOutput.toString() + } + + doLast { + if (!file("rpc/sa_ctl").exists()){ + throw new GradleException('Failed build sipp:rpc') + //throw new RuntimeException('Failed build sipp:rpc') + } else { + println("Finished build sipp:rpc") + } + } +} + +task buildsipp(type:Exec) { + + dependsOn tasks.cleansipp + workingDir '.' + + doFirst { + println("Running build sipp") + if (!file("./Makefile").exists()){ + commandLine './build.sh' + } else { + //on linux + commandLine 'make' + } + + //store the output instead of printing to the console: + standardOutput = new ByteArrayOutputStream() + + //extension method stopTomcat.output() can be used to obtain the output: + ext.output = { + return standardOutput.toString() + } + } + + doLast { + if (!file("sipp").exists()){ + throw new GradleException('Failed build sipp') + //throw new RuntimeException('Failed build sipp') + } else { + println("Finished build sipp") + } + } +} + +task packsipp(type: Copy) { + + doFirst { + println("Running pack sipp") + } + + dependsOn tasks.buildrpc + dependsOn tasks.buildsipp + + from('.') { + include 'sipp' + } + from('rpc') { + include 'sa_ctl' + } + into 'build/bin' + exclude '**/*.bak' + + includeEmptyDirs = false + + doLast { + if (!file("$buildDir/bin/sipp").exists()){ + throw new GradleException('Failed pack sipp') + //throw new RuntimeException('Failed pack sipp') + } else if (!file("$buildDir/bin/sa_ctl").exists()){ + throw new GradleException('Failed pack sipp:rpc') + //throw new RuntimeException('Failed pack sipp:rpc') + } else { + println("Finished running pack sipp") + } + } +} + diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000..63d3543e2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,308 @@ +# ======================== initialization =============================== + +AC_INIT([SIPp], [see-include/version.h], [sipp-users@lists.sourceforge.net], [sipp]) + +# Load non-standard m4 files from ./m4/*.m4. +# AC_CONFIG_MACRO_DIR([m4]) does not work on pre-1.13 aclocal, so we +# have to include all files by hand. +m4_include([m4/am_git_version.m4]) +m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_config_feature.m4]) +m4_include([m4/ax_cxx_compile_stdcxx.m4]) +m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) +m4_include([m4/ax_have_epoll.m4]) + +AC_CANONICAL_TARGET + +AC_CONFIG_SRCDIR([src/sipp.cpp]) +AC_CONFIG_HEADERS([include/config.h]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) + +AC_ARG_WITH([openssl],AC_HELP_STRING([--with-openssl], [build with OpenSSL support]), [openssl="$withval"],[openssl="no"]) +AC_ARG_WITH([pcap],AC_HELP_STRING([--with-pcap], [build with pcap support]), [pcap="$withval"],[pcap="no"]) +AC_ARG_WITH([sctp],AC_HELP_STRING([--with-sctp], [build with SCTP support]), [sctp="$withval"],[sctp="no"]) +AC_ARG_WITH([gsl],AC_HELP_STRING([--with-gsl], [build with GSL (GNU Scientific Library) support]), [gsl="$withval"],[gsl="no"]) +AC_ARG_WITH([rtpstream],AC_HELP_STRING([--without-rtpstream], [build without RTP streaming support]), [rtp="$withval"],[rtp="yes"]) + +AC_ARG_ENABLE([epoll], + AC_HELP_STRING([--enable-epoll], [build with epoll backend]), + [case "$enableval" in + yes | no) epoll="$enableval" ;; + *) AC_MSG_ERROR(bad value $enableval for --enable-epoll) ;; + esac], + [epoll="yes"]) + +AC_PATH_PROG([HELP2MAN], help2man) +AM_CONDITIONAL([HAVE_HELP2MAN], [test -n "$HELP2MAN"]) +if test x"$HELP2MAN" = x; then + AC_MSG_WARN([cannot find help2man, you will not be able to generate manpages]) +fi + +# ==================== basic compiler settings ========================== + +AC_PROG_CC +AC_PROG_CXX +AC_HEADER_STDC + +# Prefer ext (gnu++11) over noext (c++11) because on some platforms the +# noext (stricter) mode disables non-posix features like stdio fileno(). +AX_CXX_COMPILE_STDCXX_11(ext,optional) +AX_CHECK_COMPILE_FLAG([-std=gnu11], + [CFLAGS+=" -std=gnu11"], + []) + +case "$host" in + *-linux*) + CFLAGS="$CFLAGS -D__LINUX -rdynamic" + CPPFLAGS="$CPPFLAGS -D__LINUX -rdynamic" + LDFLAGS="$LDFLAGS -rdynamic" + ;; + *-darwin*) + CFLAGS="$CFLAGS -D__DARWIN" + CPPFLAGS="$CPPFLAGS -D__DARWIN" + ;; + *-hpux*) + CFLAGS="$CFLAGS -D__HPUX" + CPPFLAGS="$CPPFLAGS -D__HPUX" + ;; + *-freebsd*) + CFLAGS="$CFLAGS -D__LINUX -I/usr/local/include" + CPPFLAGS="$CPPFLAGS -D__LINUX -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + ;; + *-sunos*|*-solaris2*) + CFLAGS="$CFLAGS -D__SUNOS" + CPPFLAGS="$CPPFLAGS -D__SUNOS" + ;; + *-cygwin*) + CFLAGS="$CFLAGS -D__CYGWIN -I /usr/include/ncurses -I /usr/lib/WpdPack/Include -I /usr/include/SctpDrv" + CPPFLAGS="$CPPFLAGS -D__CYGWIN -I /usr/include/ncurses -I /usr/lib/WpdPack/Include -I /usr/include/SctpDrv" + LDFLAGS="$LDFLAGS -L /usr/lib/WpdPack/Lib -L /usr/lib/SctpDrv" + ;; + *-tru64*) + CFLAGS="$CFLAGS -D__OSF1" + CPPFLAGS="$CPPFLAGS -D__OSF1" + ;; +esac + +AC_CHECK_PROG([have_pkgconfig], [pkg-config], [yes], [no]) + +# ==================== checks for libraries ============================= + +LIBS="$LIBS -llua5.3 -lrt -lcrypto -lcurl -lperconaserverclient" +if test "$have_pkgconfig" = "yes"; then + # Use pkg-config when available + PKG_PROG_PKG_CONFIG() + PKG_CHECK_MODULES([ncurses], [ncurses], + LIBS="$LIBS $ncurses_LIBS", + [PKG_CHECK_MODULES([curses], [curses], + LIBS="$LIBS $curses_LIBS", + # On OSX we don't get pkg-config for (n)curses. + [AC_SEARCH_LIBS([initscr], [ncurses curses],, + AC_ERROR([Missing (n)curses library]))] + )] + ) +else + # Olden ways + AC_SEARCH_LIBS([initscr], [ncurses curses],,AC_ERROR([Missing (n)curses library])) + AC_SEARCH_LIBS([stdscr], [tinfo ncurses curses],,AC_ERROR([Missing (n)curses/tinfo library])) +fi + +AC_CHECK_LIB(pthread, pthread_mutex_init, THREAD_LIBS="-lpthread", + AC_MSG_ERROR(pthread library needed!)) + +# For Linux and SunOS +AC_SEARCH_LIBS([dlopen], [dl]) +AC_SEARCH_LIBS([dlerror], [dl]) +AC_SEARCH_LIBS([dlsym], [dl]) + +# For glibc pre-2.17 we need -rt +AC_SEARCH_LIBS([clock_gettime], [rt]) + +# For SunOS +AC_SEARCH_LIBS([inet_addr], [nsl]) +AC_SEARCH_LIBS([inet_ntoa], [nsl]) + +AC_SEARCH_LIBS([pthread_mutex_init], [pthread]) +AC_SEARCH_LIBS([pthread_mutex_destroy], [pthread]) +AC_SEARCH_LIBS([pthread_mutex_lock], [pthread]) +AC_SEARCH_LIBS([pthread_mutex_unlock], [pthread]) +AC_SEARCH_LIBS([pthread_self], [pthread]) +AC_SEARCH_LIBS([pthread_cancel], [pthread]) +AC_SEARCH_LIBS([pthread_join], [pthread]) +AC_SEARCH_LIBS([pthread_attr_init], [pthread]) +AC_SEARCH_LIBS([pthread_attr_setstacksize], [pthread]) +AC_SEARCH_LIBS([pthread_create], [pthread]) +AC_SEARCH_LIBS([pthread_attr_destroy], [pthread]) +AC_SEARCH_LIBS([pthread_setschedparam], [pthread]) +AC_SEARCH_LIBS([pthread_setcancelstate], [pthread]) +AC_SEARCH_LIBS([pthread_setcanceltype], [pthread]) +AC_SEARCH_LIBS([pthread_exit], [pthread]) +AC_SEARCH_LIBS([pthread_sigmask], [pthread]) +#AC_SEARCH_LIBS([pthread_cleanup_push], [pthread]) <- macro +#AC_SEARCH_LIBS([pthread_cleanup_pop], [pthread]) <- macro + +AC_SEARCH_LIBS([floor], [m]) +AC_SEARCH_LIBS([pow], [m]) + +# For SunOS +AC_SEARCH_LIBS([htons], [socket]) +AC_SEARCH_LIBS([ntohs], [socket]) +AC_SEARCH_LIBS([bind], [socket]) +AC_SEARCH_LIBS([freeaddrinfo], [socket]) +AC_SEARCH_LIBS([getaddrinfo], [socket]) +AC_SEARCH_LIBS([listen], [socket]) +AC_SEARCH_LIBS([recvfrom], [socket]) +AC_SEARCH_LIBS([shutdown], [socket]) + +# For Linux, SunOS and Cygwin +#AC_CHECK_LIB(stdc++,main,,[AC_MSG_ERROR([stdc++ library missing])]) + +# Conditional build with OpenSSL +if test "$openssl" = 'yes'; then + AC_CHECK_HEADERS([openssl/bio.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([openssl/err.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([openssl/rand.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([openssl/ssl.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([openssl/x509v3.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_LIB([ssl], [SSL_CTX_new],,[AC_MSG_ERROR([ssl library missing])]) + AC_CHECK_LIB([crypto], [CRYPTO_free],,[AC_MSG_ERROR([crypto library missing])]) +fi +# For Makefile.am +AM_CONDITIONAL(HAVE_OPENSSL, test "$openssl" = "yes") + +# Conditional build with SCTP +if test "$sctp" = 'yes'; then + AC_CHECK_HEADERS([netinet/sctp.h],,[AC_MSG_WARN([ header missing, but this is acceptable on Mac OS X Lion])]) + AC_SEARCH_LIBS([sctp_send],[sctp sctpsp],,[AC_MSG_ERROR([SCTP library missing])]) + AC_SEARCH_LIBS([sctp_freepaddrs],[sctp sctpsp],,[AC_MSG_ERROR([SCTP library missing])]) + AC_SEARCH_LIBS([sctp_bindx],[sctp sctpsp],,[AC_MSG_ERROR([SCTP library missing])]) + AC_SEARCH_LIBS([sctp_recvmsg],[sctp sctpsp],,[AC_MSG_ERROR([SCTP library missing])]) +fi +# For Makefile.am +AM_CONDITIONAL(HAVE_SCTP, test "$sctp" = "yes") + +# Conditional build with pcap +if test "$pcap" = 'yes'; then + AC_CHECK_HEADERS([pcap.h],,[AC_MSG_ERROR([ header missing])]) + AC_SEARCH_LIBS([pcap_open_offline],[pcap wpcap],,[AC_MSG_ERROR([pcap library missing])]) + AC_SEARCH_LIBS([pcap_next],[pcap wpcap],,[AC_MSG_ERROR([pcap library missing])]) + AC_SEARCH_LIBS([pcap_close],[pcap wpcap],,[AC_MSG_ERROR([pcap library missing])]) + AC_CHECK_LIB([pcap],[pcap_next_ex],AC_DEFINE([HAVE_PCAP_NEXT_EX],[1],[Define to 1 if libpcap has the pcap_next_ex function.])) +fi +# For Makefile.am +AM_CONDITIONAL(HAVE_PCAP, test "$pcap" = "yes") + +AM_CONDITIONAL(HAVE_RTP, test "$rtp" = "yes") + +# Conditional build with gsl +if test "$gsl" = "yes"; then + if test "$have_pkgconfig" != "yes" ; then + AC_MSG_ERROR([Please install pkg-config to use the gsl libraries]) + fi + AC_CHECK_HEADERS([gsl/gsl_randist.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([gsl/gsl_rng.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_HEADERS([gsl/gsl_cdf.h],,[AC_MSG_ERROR([ header missing])]) + AC_CHECK_LIB([m],[cos]) + AC_CHECK_LIB([gslcblas], [cblas_dgemm]) + AC_CHECK_LIB([gsl], [gsl_rng_alloc], + [ + GSL_CFLAGS=`pkg-config gsl --cflags` + GSL_CXXFLAGS=`pkg-config gsl --cflags` + GSL_LIBS=`pkg-config gsl --libs` + AC_SUBST([GSL_CFLAGS]) + AC_SUBST([GSL_CXXFLAGS]) + AC_SUBST([GSL_LIBS]) + ] + ,[AC_MSG_ERROR([gsl library missing])]) +fi +# For Makefile.am +AM_CONDITIONAL(HAVE_GSL, test "$gsl" = "yes") + +if test "$epoll" = 'yes'; then + AX_HAVE_EPOLL([AX_CONFIG_FEATURE_ENABLE(epoll)], + [AX_CONFIG_FEATURE_DISABLE(epoll)]) + AX_CONFIG_FEATURE([epoll], [This platform supports epoll(7)], + [HAVE_EPOLL], [This platform supports epoll(7).], + [epoll="yes"], [epoll="no"]) +fi + +AM_CONDITIONAL(HAVE_EPOLL, test "$epoll" = "yes") +# ==================== checks for header files ========================== + +AC_FUNC_ALLOCA +AC_CHECK_HEADERS([arpa/inet.h endian.h fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h sys/endian.h sys/socket.h sys/time.h unistd.h]) + +# ===== checks for typedefs, structures and compiler characteristics ==== + +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +# These conflict with netinet/in.h typedefs. +#AC_TYPE_UINT16_T +#AC_TYPE_UINT32_T +#AC_TYPE_UINT8_T + +# ==================== checks for library functions ===================== + +AC_FUNC_FORK +#AC_FUNC_MALLOC +#AC_FUNC_REALLOC +#AC_FUNC_STRTOD +AC_CHECK_FUNCS([alarm dup2 floor gethostname gettimeofday inet_ntoa memmove memset pow regcomp socket sqrt strcasecmp strchr strcspn strdup strerror strncasecmp strrchr strstr strtol strtoul strtoull]) + +# Check for le16toh in both endian.h and sys/endian.h (for BSD). +AC_CHECK_DECLS([le16toh], [], [], +[#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif]) + + +# ==================== check for clang/gmock workaround ================= + +# clang++ falls over the use of is_default_constructible, suggesting +# is_nothrow_constructible instead. +# +# ./gmock/include/gmock/gmock-actions.h:107:19: error: no template named +# 'is_default_constructible' in namespace 'std'; did you mean +# 'is_nothrow_constructible'? +# T, ::std::is_default_constructible::value>::Get(); +# ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~ +# is_nothrow_constructible +# /usr/include/c++/4.6/type_traits:705:12: note: +# 'is_nothrow_constructible' declared here +# +AC_DEFUN([AX_CXX_HAS_IS_DEFAULT_CONSTRUCTIBLE], [dnl + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports is_default_constructible (used by gmock), + ax_cv_cxx_has_is_default_constructible, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +#include +template +void test_is_default_constructible() { + static_assert(std::is_default_constructible::value, ""); +} +int main() { + test_is_default_constructible(); + return 0; +} +])], + [ax_cv_cxx_has_is_default_constructible=yes], + [ax_cv_cxx_has_is_default_constructible=no])]) + if test x$ax_cv_cxx_has_is_default_constructible = xno; then + CPPFLAGS="$CPPFLAGS -Dis_default_constructible=is_nothrow_constructible" + fi + AC_LANG_POP([C++]) +]) +AX_CXX_HAS_IS_DEFAULT_CONSTRUCTIBLE() + +# ==================== generate files =================================== + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/docs/sipp_demo.sh b/docs/sipp_demo.sh new file mode 100755 index 000000000..35d430eaf --- /dev/null +++ b/docs/sipp_demo.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# +# sipp remote_host[:remote_port] [options] +# +# - m : Stop the call after n calls are processed +# -r : Set the call rate (in calls per seconds). +# -l : Set the maximum number of simultaneous calls. Once this +# limit is reached, traffic is decreased until the number +# of open calls goes down. Default: +# (3 * call_duration (s) * rate). +# -p : Set the local port number. +# -s : Set the username part of the resquest URI. Default is +# 'service'. +# -sf : Loads an alternate xml scenario file. To learn more +# about XML scenario syntax, use the -sd option to dump +# embedded scenarios. They contain all the necessary help. +# -inf : Inject values from an external CSV file during calls into +# the scenarios. +# First line of this file say whether the data is to be +# read in sequence (SEQUENTIAL), random (RANDOM), or user +# (USER) order. +# Each line corresponds to one call and has one or more +# ';' delimited data fields. Those fields can be referred +# as [field0], [field1], ... in the xml scenario file. +# Several CSV files can be used simultaneously (syntax: +# -inf f1.csv -inf f2.csv ...) +#-nd : No Default. Disable all default behavior of SIPp which +# are the following: +# - On UDP retransmission timeout, abort the call by +# sending a BYE or a CANCEL +# - On receive timeout with no ontimeout attribute, abort +# the call by sending a BYE or a CANCEL +# - On unexpected BYE send a 200 OK and close the call +# - On unexpected CANCEL send a 200 OK and close the call +# - On unexpected PING send a 200 OK and continue the call +# - On any other unexpected message, abort the call by +# sending a BYE or a CANCEL +# +#-rp : Specify the rate period for the call rate. Default is 1 +# second and default unit is milliseconds. This allows +# you to have n calls every m milliseconds (by using -r n +# -rp m). +# Example: -r 7 -rp 2000 ==> 7 calls every 2 seconds. +# -r 10 -rp 5s => 10 calls every 5 seconds. +# -aa : Enable automatic 200 OK answer for INFO, UPDATE and NOTIFY messages. +# +# + + + + +ulimit -n 65533 +# These should be set in the environment +LOGDIR=./ +REMOTEHOST=172.29.31.14 +LOCALIP=172.20.2.35 + +pidfile="$RUNDIR/sipp.pid" +logfiles="-ringbuffer_files 1000 -ringbuffer_size 50000000 -trace_msg -trace_logs -trace_err -message_file $LOGDIR/sipp_message.log -message_overwrite false -error_file $LOGDIR/sipp_error.log -error_overwrite false -calldebug_file $LOGDIR/sipp_debug.log -calldebug_overwrite false -log_file $LOGDIR/sipp.log -log_overwrite false " + +remotehost=$REMOTEHOST +username=demo +srchost=$LOCALIP +srcport=5060 + +if [ $# -eq 1 ] && [ $1 = "stop" ] +then +/bin/cat $pidfile | xargs kill -9 +exit +fi + +# +# Execute with custom script +# +sipp -plugin libmyapp.so -lua_file sipp_demo.lua -pid_file $pidfile -aa $logfiles -m 1000000 -l 1 -i $srchost -p $srcport -s $username -nd -sf ./uas_w_script.xml $remotehost diff --git a/docs/uas_w_script.xml b/docs/uas_w_script.xml new file mode 100644 index 000000000..e180b6251 --- /dev/null +++ b/docs/uas_w_script.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Content-Length: 0 + + ]]> + + + + + + + + + + + + + + + + + diff --git a/example/rpc/Makefile.sipp-rpc b/example/rpc/Makefile.sipp-rpc new file mode 100644 index 000000000..99f628bf0 --- /dev/null +++ b/example/rpc/Makefile.sipp-rpc @@ -0,0 +1,47 @@ + +# This is a template Makefile generated by rpcgen + +# Parameters + +CLIENT = sipp-rpc_client +SERVER = sipp-rpc_server + +SOURCES_CLNT.c = +SOURCES_CLNT.h = +SOURCES_SVC.c = +SOURCES_SVC.h = +SOURCES.x = sipp-rpc.x + +TARGETS_SVC.c = sipp-rpc_svc.c sipp-rpc_server.c sipp-rpc_xdr.c +TARGETS_CLNT.c = sipp-rpc_clnt.c sipp-rpc_client.c sipp-rpc_xdr.c +TARGETS = sipp-rpc.h sipp-rpc_xdr.c sipp-rpc_clnt.c sipp-rpc_svc.c sipp-rpc_client.c sipp-rpc_server.c + +OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o) +OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o) +# Compiler flags + +CPPFLAGS += -D_REENTRANT +CFLAGS += -g +LDLIBS += -lnsl -lpthread + RPCGENFLAGS = + +# Targets + +all : $(CLIENT) $(SERVER) + +$(TARGETS) : $(SOURCES.x) + rpcgen $(RPCGENFLAGS) $(SOURCES.x) + +$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c) + +$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c) + +$(CLIENT) : $(OBJECTS_CLNT) + $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS) + +$(SERVER) : $(OBJECTS_SVC) + $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS) + + clean: + $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) + diff --git a/example/rpc/readme.txt b/example/rpc/readme.txt new file mode 100644 index 000000000..383110680 --- /dev/null +++ b/example/rpc/readme.txt @@ -0,0 +1,13 @@ +################################################################ +#Instructions to build rpc interface +#you will need the client sa_ctl to send rpc commands to sipp +#Example: sa_ctl get_callcount will get the current call count +# +Steps to regenerate the files:: +0. Make a new .x file with whatever commands you're trying to implement +1. Install the portmap service which is required for rpc services - apt-get install portmap +2. Start clean - make clean +3. Backup any mods you might have made - cp sipp-rpc_client.c sipp-rpc_client.c.bak +4. Regenerate a new raw client - rpcgen -M -C -a sipp-rpc.x +5. Restore your old mods, or modify the client as needed - cp sipp-rpc_client.c.bak sipp-rpc_client.c +6. Build it - make - this will build a client and server for testing or use the client to try your new commands out on a compatible server process - like sipp! diff --git a/example/rpc/sipp-rpc.h b/example/rpc/sipp-rpc.h new file mode 100644 index 000000000..ac29fdbda --- /dev/null +++ b/example/rpc/sipp-rpc.h @@ -0,0 +1,72 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _SIPP-RPC_H_RPCGEN +#define _SIPP-RPC_H_RPCGEN + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct callcounter { + int incomingcalls; + int outgoingcalls; + int successcalls; + int failurecalls; +}; +typedef struct callcounter callcounter; + +typedef callcounter callcounter_t; + +#define SIPPRPCPROG 0x20000002 +#define SIPPRPCVERS 1 + +#if defined(__STDC__) || defined(__cplusplus) +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(callcounter_t *, callcounter_t *, CLIENT *); +extern bool_t getcallcounter_1_svc(callcounter_t *, callcounter_t *, struct svc_req *); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t enablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t disablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +extern int sipprpcprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(); +extern bool_t getcallcounter_1_svc(); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(); +extern bool_t enablelog4autoanswer_1_svc(); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(); +extern bool_t disablelog4autoanswer_1_svc(); +extern int sipprpcprog_1_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_callcounter (XDR *, callcounter*); +extern bool_t xdr_callcounter_t (XDR *, callcounter_t*); + +#else /* K&R C */ +extern bool_t xdr_callcounter (); +extern bool_t xdr_callcounter_t (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_SIPP-RPC_H_RPCGEN */ diff --git a/example/rpc/sipp-rpc.x b/example/rpc/sipp-rpc.x new file mode 100644 index 000000000..396bf9972 --- /dev/null +++ b/example/rpc/sipp-rpc.x @@ -0,0 +1,19 @@ +struct callcounter +{ + int incomingcalls; + int outgoingcalls; + int successcalls; + int failurecalls; +}; + +typedef struct callcounter callcounter_t; + +program SIPPRPCPROG +{ + version SIPPRPCVERS + { + callcounter_t GETCALLCOUNTER(callcounter_t) = 1; + bool ENABLELOG4AUTOANSWER() = 2; + bool DISABLELOG4AUTOANSWER() = 3; + } = 1; +} = 0x20000002; diff --git a/example/rpc/sipp-rpc_client.c b/example/rpc/sipp-rpc_client.c new file mode 100644 index 000000000..284af95bd --- /dev/null +++ b/example/rpc/sipp-rpc_client.c @@ -0,0 +1,84 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + + +void +sipprpcprog_1(char *command) +{ + char *host = "127.0.0.1"; + CLIENT *clnt; + enum clnt_stat retval_1; + callcounter_t result_1; + callcounter_t getcallcounter_1_arg; + enum clnt_stat retval_2; + bool_t result_2; + char *enablelog4autoanswer_1_arg; + enum clnt_stat retval_3; + bool_t result_3; + char *disablelog4autoanswer_1_arg; + +#ifndef DEBUG + clnt = clnt_create (host, SIPPRPCPROG, SIPPRPCVERS, "udp"); + if (clnt == NULL) { + clnt_pcreateerror (host); + exit (1); + } +#endif /* DEBUG */ + + if (strcmp(command,"get_callcount") ==0 ) { + retval_1 = getcallcounter_1(&getcallcounter_1_arg, &result_1, clnt); + if (retval_1 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Total incoming calls: %d\n",result_1.incomingcalls); + printf("Total outgoing calls: %d\n",result_1.outgoingcalls); + printf("Total success calls: %d\n",result_1.successcalls); + printf("Total failure calls: %d\n",result_1.failurecalls); + } + } + if (strcmp(command,"enable_log4aa") ==0 ) { + retval_2 = enablelog4autoanswer_1((void*)&enablelog4autoanswer_1_arg, &result_2, clnt); + if (retval_2 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response enabled.\n"); + } + } + if (strcmp(command,"disable_log4aa") ==0 ) { + retval_3 = disablelog4autoanswer_1((void*)&disablelog4autoanswer_1_arg, &result_3, clnt); + if (retval_3 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response disabled.\n"); + } + } +#ifndef DEBUG + clnt_destroy (clnt); +#endif /* DEBUG */ +} + + +int +main (int argc, char *argv[]) +{ + char *command; + + if (argc < 2) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + command = argv[1]; + if (strcmp(command,"get_callcount") != 0 && + strcmp(command,"enable_log4aa") != 0 && + strcmp(command,"disable_log4aa") != 0 ) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + sipprpcprog_1 (command); +exit (0); +} diff --git a/example/rpc/sipp-rpc_client.c.bak b/example/rpc/sipp-rpc_client.c.bak new file mode 100644 index 000000000..284af95bd --- /dev/null +++ b/example/rpc/sipp-rpc_client.c.bak @@ -0,0 +1,84 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + + +void +sipprpcprog_1(char *command) +{ + char *host = "127.0.0.1"; + CLIENT *clnt; + enum clnt_stat retval_1; + callcounter_t result_1; + callcounter_t getcallcounter_1_arg; + enum clnt_stat retval_2; + bool_t result_2; + char *enablelog4autoanswer_1_arg; + enum clnt_stat retval_3; + bool_t result_3; + char *disablelog4autoanswer_1_arg; + +#ifndef DEBUG + clnt = clnt_create (host, SIPPRPCPROG, SIPPRPCVERS, "udp"); + if (clnt == NULL) { + clnt_pcreateerror (host); + exit (1); + } +#endif /* DEBUG */ + + if (strcmp(command,"get_callcount") ==0 ) { + retval_1 = getcallcounter_1(&getcallcounter_1_arg, &result_1, clnt); + if (retval_1 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Total incoming calls: %d\n",result_1.incomingcalls); + printf("Total outgoing calls: %d\n",result_1.outgoingcalls); + printf("Total success calls: %d\n",result_1.successcalls); + printf("Total failure calls: %d\n",result_1.failurecalls); + } + } + if (strcmp(command,"enable_log4aa") ==0 ) { + retval_2 = enablelog4autoanswer_1((void*)&enablelog4autoanswer_1_arg, &result_2, clnt); + if (retval_2 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response enabled.\n"); + } + } + if (strcmp(command,"disable_log4aa") ==0 ) { + retval_3 = disablelog4autoanswer_1((void*)&disablelog4autoanswer_1_arg, &result_3, clnt); + if (retval_3 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response disabled.\n"); + } + } +#ifndef DEBUG + clnt_destroy (clnt); +#endif /* DEBUG */ +} + + +int +main (int argc, char *argv[]) +{ + char *command; + + if (argc < 2) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + command = argv[1]; + if (strcmp(command,"get_callcount") != 0 && + strcmp(command,"enable_log4aa") != 0 && + strcmp(command,"disable_log4aa") != 0 ) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + sipprpcprog_1 (command); +exit (0); +} diff --git a/example/rpc/sipp-rpc_clnt.c b/example/rpc/sipp-rpc_clnt.c new file mode 100644 index 000000000..5342bf936 --- /dev/null +++ b/example/rpc/sipp-rpc_clnt.c @@ -0,0 +1,37 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include /* for memset */ +#include "sipp-rpc.h" + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +enum clnt_stat +getcallcounter_1(callcounter_t *argp, callcounter_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, GETCALLCOUNTER, + (xdrproc_t) xdr_callcounter_t, (caddr_t) argp, + (xdrproc_t) xdr_callcounter_t, (caddr_t) clnt_res, + TIMEOUT)); +} + +enum clnt_stat +enablelog4autoanswer_1(void *argp, bool_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, ENABLELOG4AUTOANSWER, + (xdrproc_t) xdr_void, (caddr_t) argp, + (xdrproc_t) xdr_bool, (caddr_t) clnt_res, + TIMEOUT)); +} + +enum clnt_stat +disablelog4autoanswer_1(void *argp, bool_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, DISABLELOG4AUTOANSWER, + (xdrproc_t) xdr_void, (caddr_t) argp, + (xdrproc_t) xdr_bool, (caddr_t) clnt_res, + TIMEOUT)); +} diff --git a/example/rpc/sipp-rpc_server.c b/example/rpc/sipp-rpc_server.c new file mode 100644 index 000000000..9dbfd1914 --- /dev/null +++ b/example/rpc/sipp-rpc_server.c @@ -0,0 +1,55 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + +bool_t +getcallcounter_1_svc(callcounter_t *argp, callcounter_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +enablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +disablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +int +sipprpcprog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) +{ + xdr_free (xdr_result, result); + + /* + * Insert additional freeing code here, if needed + */ + + return 1; +} diff --git a/example/rpc/sipp-rpc_server.c.bak b/example/rpc/sipp-rpc_server.c.bak new file mode 100644 index 000000000..9dbfd1914 --- /dev/null +++ b/example/rpc/sipp-rpc_server.c.bak @@ -0,0 +1,55 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + +bool_t +getcallcounter_1_svc(callcounter_t *argp, callcounter_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +enablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +disablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +int +sipprpcprog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) +{ + xdr_free (xdr_result, result); + + /* + * Insert additional freeing code here, if needed + */ + + return 1; +} diff --git a/example/rpc/sipp-rpc_svc.c b/example/rpc/sipp-rpc_svc.c new file mode 100644 index 000000000..40d136400 --- /dev/null +++ b/example/rpc/sipp-rpc_svc.c @@ -0,0 +1,111 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "sipp-rpc.h" +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIG_PF +#define SIG_PF void(*)(int) +#endif + +static void +sipprpcprog_1(struct svc_req *rqstp, register SVCXPRT *transp) +{ + union { + callcounter_t getcallcounter_1_arg; + } argument; + union { + callcounter_t getcallcounter_1_res; + bool_t enablelog4autoanswer_1_res; + bool_t disablelog4autoanswer_1_res; + } result; + bool_t retval; + xdrproc_t _xdr_argument, _xdr_result; + bool_t (*local)(char *, void *, struct svc_req *); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); + return; + + case GETCALLCOUNTER: + _xdr_argument = (xdrproc_t) xdr_callcounter_t; + _xdr_result = (xdrproc_t) xdr_callcounter_t; + local = (bool_t (*) (char *, void *, struct svc_req *))getcallcounter_1_svc; + break; + + case ENABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))enablelog4autoanswer_1_svc; + break; + + case DISABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))disablelog4autoanswer_1_svc; + break; + + default: + svcerr_noproc (transp); + return; + } + memset ((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + svcerr_decode (transp); + return; + } + retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp); + if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) { + svcerr_systemerr (transp); + } + if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + fprintf (stderr, "%s", "unable to free arguments"); + exit (1); + } + if (!sipprpcprog_1_freeresult (transp, _xdr_result, (caddr_t) &result)) + fprintf (stderr, "%s", "unable to free results"); + + return; +} + +int +main (int argc, char **argv) +{ + register SVCXPRT *transp; + + pmap_unset (SIPPRPCPROG, SIPPRPCVERS); + + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + fprintf (stderr, "%s", "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_UDP)) { + fprintf (stderr, "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, udp)."); + exit(1); + } + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) { + fprintf (stderr, "%s", "cannot create tcp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_TCP)) { + fprintf (stderr, "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, tcp)."); + exit(1); + } + + svc_run (); + fprintf (stderr, "%s", "svc_run returned"); + exit (1); + /* NOTREACHED */ +} diff --git a/example/rpc/sipp-rpc_xdr.c b/example/rpc/sipp-rpc_xdr.c new file mode 100644 index 000000000..9dd8e3835 --- /dev/null +++ b/example/rpc/sipp-rpc_xdr.c @@ -0,0 +1,71 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "sipp-rpc.h" + +bool_t +xdr_callcounter (XDR *xdrs, callcounter *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + IXDR_PUT_LONG(buf, objp->incomingcalls); + IXDR_PUT_LONG(buf, objp->outgoingcalls); + IXDR_PUT_LONG(buf, objp->successcalls); + IXDR_PUT_LONG(buf, objp->failurecalls); + } + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + objp->incomingcalls = IXDR_GET_LONG(buf); + objp->outgoingcalls = IXDR_GET_LONG(buf); + objp->successcalls = IXDR_GET_LONG(buf); + objp->failurecalls = IXDR_GET_LONG(buf); + } + return TRUE; + } + + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + return TRUE; +} + +bool_t +xdr_callcounter_t (XDR *xdrs, callcounter_t *objp) +{ + register int32_t *buf; + + if (!xdr_callcounter (xdrs, objp)) + return FALSE; + return TRUE; +} diff --git a/include/call.hpp b/include/call.hpp index 7f5924665..38f5ffaff 100644 --- a/include/call.hpp +++ b/include/call.hpp @@ -95,12 +95,51 @@ class call : virtual public task, virtual public listener, public virtual socket E_AM_OOCALL }; + // Access functions void setLastMsg(const char *msg); bool automaticResponseMode(T_AutoMode P_case, const char* P_recv); - const char *getLastReceived() { + + char *getLastReceived() { return last_recv_msg; }; + void setLastReceived(char *msg){ + last_recv_msg = msg; + } + void set_realloc_ptr(char *ptr){ + realloc_ptr = ptr; + } + char *get_realloc_ptr(){ + return realloc_ptr; + } + int get_msg_index() { + return msg_index; + } + + void public_sendBuffer(char *buf, int len = 0){ // send a message out of a scenario + sendBuffer(buf, len); + } + void increment_call_scenario_unexpected_count() { + call_scenario->messages[msg_index] -> nb_unexp++; + } + char *get_id(){ + return id; + } + char* public_createSendingMessage(SendingMessage *src, int P_index, int *msgLen=NULL){ + return createSendingMessage(src,P_index,msgLen); + } + char* public_createSendingMessage(char * src, int P_index, bool skip_sanity = false){ + return createSendingMessage(src,P_index, skip_sanity); + } + + int public_sendCmdBuffer(char* cmd){ + return sendCmdBuffer(cmd); + } + void public_computeStat (CStat::E_Action P_action){ + computeStat (P_action); + } + + private: /* This is the core constructor function. */ void init(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall); diff --git a/include/logger.hpp b/include/logger.hpp index 798c54da5..5fac32138 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -54,6 +54,7 @@ struct logfile_info { bool fixedname; time_t starttime; unsigned int count; + pthread_mutex_t *lockfile; }; void print_header_line(FILE *f); @@ -79,6 +80,7 @@ LOGFILE(log_lfi, "logs", true); LOGFILE(error_lfi, "errors", false); void rotate_logfile(); +void rotate_errorf_nolock(); void rotate_shortmessagef(); void rotate_errorf(); void rotate_messagef(); diff --git a/include/rtpstream.hpp b/include/rtpstream.hpp index 6835033ed..238f9f761 100644 --- a/include/rtpstream.hpp +++ b/include/rtpstream.hpp @@ -27,8 +27,6 @@ struct taskentry_t; struct rtpstream_callinfo_t { taskentry_t* taskinfo; - int audioport; - int videoport; }; struct rtpstream_actinfo_t @@ -45,8 +43,6 @@ int rtpstream_new_call(rtpstream_callinfo_t *callinfo); void rtpstream_end_call(rtpstream_callinfo_t *callinfo); void rtpstream_shutdown(void); -int rtpstream_get_audioport(rtpstream_callinfo_t *callinfo); -int rtpstream_get_videoport(rtpstream_callinfo_t *callinfo); void rtpstream_set_remote(rtpstream_callinfo_t* callinfo, int ip_ver, const char* ip_addr, int audio_port, int video_port); diff --git a/include/sipp-rpc.h b/include/sipp-rpc.h new file mode 100644 index 000000000..cdbaaf4c8 --- /dev/null +++ b/include/sipp-rpc.h @@ -0,0 +1,72 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _SIPP_RPC_H_RPCGEN +#define _SIPP_RPC_H_RPCGEN + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct callcounter { + int incomingcalls; + int outgoingcalls; + int successcalls; + int failurecalls; +}; +typedef struct callcounter callcounter; + +typedef callcounter callcounter_t; + +#define SIPPRPCPROG 0x20000002 +#define SIPPRPCVERS 1 + +#if defined(__STDC__) || defined(__cplusplus) +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(callcounter_t *, callcounter_t *, CLIENT *); +extern bool_t getcallcounter_1_svc(callcounter_t *, callcounter_t *, struct svc_req *); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t enablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t disablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +extern int sipprpcprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(); +extern bool_t getcallcounter_1_svc(); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(); +extern bool_t enablelog4autoanswer_1_svc(); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(); +extern bool_t disablelog4autoanswer_1_svc(); +extern int sipprpcprog_1_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_callcounter (XDR *, callcounter*); +extern bool_t xdr_callcounter_t (XDR *, callcounter_t*); + +#else /* K&R C */ +extern bool_t xdr_callcounter (); +extern bool_t xdr_callcounter_t (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_SIPP_RPC_H_RPCGEN */ diff --git a/include/sipp.hpp b/include/sipp.hpp index 21ebf2041..45c8adf83 100644 --- a/include/sipp.hpp +++ b/include/sipp.hpp @@ -437,6 +437,7 @@ MAYBE_EXTERN struct sockaddr_storage local_addr_storage; MAYBE_EXTERN SIPpSocket *twinSippSocket DEFVAL(NULL); MAYBE_EXTERN SIPpSocket *localTwinSippSocket DEFVAL(NULL); MAYBE_EXTERN struct sockaddr_storage twinSipp_sockaddr; +MAYBE_EXTERN void *g_plugin_handle DEFVAL(NULL); // Global Plugin handle - so we can use dlsym to call various predefined utility functions outside of main() /* 3pcc extended mode */ typedef struct _T_peer_infos { diff --git a/include/strings.hpp b/include/strings.hpp index 6f5618cb2..e2c4711c7 100644 --- a/include/strings.hpp +++ b/include/strings.hpp @@ -21,6 +21,12 @@ #ifndef __SIPP_STRINGS_H__ #define __SIPP_STRINGS_H__ +//#include +#include + +char *strcasestr2 (char *__haystack, const char *__needle); +char *strncasestr (char *s, const char *find, size_t n); +void init_tolower_table(); int get_decimal_from_hex(char hex); void get_host_and_port(const char *addr, char *host, int *port); void trim(char *s); diff --git a/include/xp_parser.h b/include/xp_parser.h index b9d5bd615..48f0ccc05 100644 --- a/include/xp_parser.h +++ b/include/xp_parser.h @@ -28,8 +28,7 @@ int xp_set_xml_buffer_from_string(const char *str); int xp_set_xml_buffer_from_file(const char *filename); char* xp_open_element(int index); void xp_close_element(void); -int xp_is_invalid(); -const char* xp_get_value(const char *name); +char* xp_get_value(const char *name); char* xp_get_cdata(void); int xp_get_content_length(const char *P_buffer); diff --git a/rpc/sipp-rpc.h b/rpc/sipp-rpc.h new file mode 100644 index 000000000..ac29fdbda --- /dev/null +++ b/rpc/sipp-rpc.h @@ -0,0 +1,72 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _SIPP-RPC_H_RPCGEN +#define _SIPP-RPC_H_RPCGEN + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct callcounter { + int incomingcalls; + int outgoingcalls; + int successcalls; + int failurecalls; +}; +typedef struct callcounter callcounter; + +typedef callcounter callcounter_t; + +#define SIPPRPCPROG 0x20000002 +#define SIPPRPCVERS 1 + +#if defined(__STDC__) || defined(__cplusplus) +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(callcounter_t *, callcounter_t *, CLIENT *); +extern bool_t getcallcounter_1_svc(callcounter_t *, callcounter_t *, struct svc_req *); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t enablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(void *, bool_t *, CLIENT *); +extern bool_t disablelog4autoanswer_1_svc(void *, bool_t *, struct svc_req *); +extern int sipprpcprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define GETCALLCOUNTER 1 +extern enum clnt_stat getcallcounter_1(); +extern bool_t getcallcounter_1_svc(); +#define ENABLELOG4AUTOANSWER 2 +extern enum clnt_stat enablelog4autoanswer_1(); +extern bool_t enablelog4autoanswer_1_svc(); +#define DISABLELOG4AUTOANSWER 3 +extern enum clnt_stat disablelog4autoanswer_1(); +extern bool_t disablelog4autoanswer_1_svc(); +extern int sipprpcprog_1_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_callcounter (XDR *, callcounter*); +extern bool_t xdr_callcounter_t (XDR *, callcounter_t*); + +#else /* K&R C */ +extern bool_t xdr_callcounter (); +extern bool_t xdr_callcounter_t (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_SIPP-RPC_H_RPCGEN */ diff --git a/rpc/sipp-rpc.x b/rpc/sipp-rpc.x new file mode 100644 index 000000000..396bf9972 --- /dev/null +++ b/rpc/sipp-rpc.x @@ -0,0 +1,19 @@ +struct callcounter +{ + int incomingcalls; + int outgoingcalls; + int successcalls; + int failurecalls; +}; + +typedef struct callcounter callcounter_t; + +program SIPPRPCPROG +{ + version SIPPRPCVERS + { + callcounter_t GETCALLCOUNTER(callcounter_t) = 1; + bool ENABLELOG4AUTOANSWER() = 2; + bool DISABLELOG4AUTOANSWER() = 3; + } = 1; +} = 0x20000002; diff --git a/rpc/sipp-rpc_client.c b/rpc/sipp-rpc_client.c new file mode 100644 index 000000000..284af95bd --- /dev/null +++ b/rpc/sipp-rpc_client.c @@ -0,0 +1,84 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + + +void +sipprpcprog_1(char *command) +{ + char *host = "127.0.0.1"; + CLIENT *clnt; + enum clnt_stat retval_1; + callcounter_t result_1; + callcounter_t getcallcounter_1_arg; + enum clnt_stat retval_2; + bool_t result_2; + char *enablelog4autoanswer_1_arg; + enum clnt_stat retval_3; + bool_t result_3; + char *disablelog4autoanswer_1_arg; + +#ifndef DEBUG + clnt = clnt_create (host, SIPPRPCPROG, SIPPRPCVERS, "udp"); + if (clnt == NULL) { + clnt_pcreateerror (host); + exit (1); + } +#endif /* DEBUG */ + + if (strcmp(command,"get_callcount") ==0 ) { + retval_1 = getcallcounter_1(&getcallcounter_1_arg, &result_1, clnt); + if (retval_1 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Total incoming calls: %d\n",result_1.incomingcalls); + printf("Total outgoing calls: %d\n",result_1.outgoingcalls); + printf("Total success calls: %d\n",result_1.successcalls); + printf("Total failure calls: %d\n",result_1.failurecalls); + } + } + if (strcmp(command,"enable_log4aa") ==0 ) { + retval_2 = enablelog4autoanswer_1((void*)&enablelog4autoanswer_1_arg, &result_2, clnt); + if (retval_2 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response enabled.\n"); + } + } + if (strcmp(command,"disable_log4aa") ==0 ) { + retval_3 = disablelog4autoanswer_1((void*)&disablelog4autoanswer_1_arg, &result_3, clnt); + if (retval_3 != RPC_SUCCESS) { + clnt_perror (clnt, "call failed"); + } else { + printf("Error log for auto response disabled.\n"); + } + } +#ifndef DEBUG + clnt_destroy (clnt); +#endif /* DEBUG */ +} + + +int +main (int argc, char *argv[]) +{ + char *command; + + if (argc < 2) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + command = argv[1]; + if (strcmp(command,"get_callcount") != 0 && + strcmp(command,"enable_log4aa") != 0 && + strcmp(command,"disable_log4aa") != 0 ) { + printf ("usage: %s [get_callcount|enable_log4aa|disable_log4aa]\n", argv[0]); + exit (1); + } + sipprpcprog_1 (command); +exit (0); +} diff --git a/rpc/sipp-rpc_clnt.c b/rpc/sipp-rpc_clnt.c new file mode 100644 index 000000000..5342bf936 --- /dev/null +++ b/rpc/sipp-rpc_clnt.c @@ -0,0 +1,37 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include /* for memset */ +#include "sipp-rpc.h" + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +enum clnt_stat +getcallcounter_1(callcounter_t *argp, callcounter_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, GETCALLCOUNTER, + (xdrproc_t) xdr_callcounter_t, (caddr_t) argp, + (xdrproc_t) xdr_callcounter_t, (caddr_t) clnt_res, + TIMEOUT)); +} + +enum clnt_stat +enablelog4autoanswer_1(void *argp, bool_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, ENABLELOG4AUTOANSWER, + (xdrproc_t) xdr_void, (caddr_t) argp, + (xdrproc_t) xdr_bool, (caddr_t) clnt_res, + TIMEOUT)); +} + +enum clnt_stat +disablelog4autoanswer_1(void *argp, bool_t *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, DISABLELOG4AUTOANSWER, + (xdrproc_t) xdr_void, (caddr_t) argp, + (xdrproc_t) xdr_bool, (caddr_t) clnt_res, + TIMEOUT)); +} diff --git a/rpc/sipp-rpc_server.c b/rpc/sipp-rpc_server.c new file mode 100644 index 000000000..9dbfd1914 --- /dev/null +++ b/rpc/sipp-rpc_server.c @@ -0,0 +1,55 @@ +/* + * This is sample code generated by rpcgen. + * These are only templates and you can use them + * as a guideline for developing your own functions. + */ + +#include "sipp-rpc.h" + +bool_t +getcallcounter_1_svc(callcounter_t *argp, callcounter_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +enablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +bool_t +disablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + return retval; +} + +int +sipprpcprog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) +{ + xdr_free (xdr_result, result); + + /* + * Insert additional freeing code here, if needed + */ + + return 1; +} diff --git a/rpc/sipp-rpc_svc.c b/rpc/sipp-rpc_svc.c new file mode 100644 index 000000000..40d136400 --- /dev/null +++ b/rpc/sipp-rpc_svc.c @@ -0,0 +1,111 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "sipp-rpc.h" +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIG_PF +#define SIG_PF void(*)(int) +#endif + +static void +sipprpcprog_1(struct svc_req *rqstp, register SVCXPRT *transp) +{ + union { + callcounter_t getcallcounter_1_arg; + } argument; + union { + callcounter_t getcallcounter_1_res; + bool_t enablelog4autoanswer_1_res; + bool_t disablelog4autoanswer_1_res; + } result; + bool_t retval; + xdrproc_t _xdr_argument, _xdr_result; + bool_t (*local)(char *, void *, struct svc_req *); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); + return; + + case GETCALLCOUNTER: + _xdr_argument = (xdrproc_t) xdr_callcounter_t; + _xdr_result = (xdrproc_t) xdr_callcounter_t; + local = (bool_t (*) (char *, void *, struct svc_req *))getcallcounter_1_svc; + break; + + case ENABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))enablelog4autoanswer_1_svc; + break; + + case DISABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))disablelog4autoanswer_1_svc; + break; + + default: + svcerr_noproc (transp); + return; + } + memset ((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + svcerr_decode (transp); + return; + } + retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp); + if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) { + svcerr_systemerr (transp); + } + if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + fprintf (stderr, "%s", "unable to free arguments"); + exit (1); + } + if (!sipprpcprog_1_freeresult (transp, _xdr_result, (caddr_t) &result)) + fprintf (stderr, "%s", "unable to free results"); + + return; +} + +int +main (int argc, char **argv) +{ + register SVCXPRT *transp; + + pmap_unset (SIPPRPCPROG, SIPPRPCVERS); + + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + fprintf (stderr, "%s", "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_UDP)) { + fprintf (stderr, "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, udp)."); + exit(1); + } + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) { + fprintf (stderr, "%s", "cannot create tcp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_TCP)) { + fprintf (stderr, "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, tcp)."); + exit(1); + } + + svc_run (); + fprintf (stderr, "%s", "svc_run returned"); + exit (1); + /* NOTREACHED */ +} diff --git a/rpc/sipp-rpc_xdr.c b/rpc/sipp-rpc_xdr.c new file mode 100644 index 000000000..9dd8e3835 --- /dev/null +++ b/rpc/sipp-rpc_xdr.c @@ -0,0 +1,71 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "sipp-rpc.h" + +bool_t +xdr_callcounter (XDR *xdrs, callcounter *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + IXDR_PUT_LONG(buf, objp->incomingcalls); + IXDR_PUT_LONG(buf, objp->outgoingcalls); + IXDR_PUT_LONG(buf, objp->successcalls); + IXDR_PUT_LONG(buf, objp->failurecalls); + } + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + objp->incomingcalls = IXDR_GET_LONG(buf); + objp->outgoingcalls = IXDR_GET_LONG(buf); + objp->successcalls = IXDR_GET_LONG(buf); + objp->failurecalls = IXDR_GET_LONG(buf); + } + return TRUE; + } + + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + return TRUE; +} + +bool_t +xdr_callcounter_t (XDR *xdrs, callcounter_t *objp) +{ + register int32_t *buf; + + if (!xdr_callcounter (xdrs, objp)) + return FALSE; + return TRUE; +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..f68b0e39e --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.0.2/userguide/multi_project_builds.html + */ + +rootProject.name = 'sipp' diff --git a/src/apifunc.cpp b/src/apifunc.cpp new file mode 100644 index 000000000..be01315fd --- /dev/null +++ b/src/apifunc.cpp @@ -0,0 +1,303 @@ +/* Functions to handle loading/unloading .so files to replace certain functions in an API table + * Author: Nathan Franzmeier + * March 15, 2022 + * This provides a framework for adding a plugin which overrides the exec_command function in SIPP with new user defined functions + * that are implemented via the sipp plugin mechanism + * The format of these new functions in the xml script is + * +#include + +static pthread_mutex_t ApiFuncMutex = PTHREAD_MUTEX_INITIALIZER; + + +API_FUNC_TABLE ApiHandler[MAXFUNCS][MAXAPPLICATIONS] = { + {(API_FUNC)customFunc,(char *)"custom1"}, + {(API_FUNC)customFunc,(char *)"custom2"}, + {(API_FUNC)customFunc,(char *)"custom3"}, + {(API_FUNC)customFunc,(char *)"custom4"}, + {(API_FUNC)customFunc,(char *)"custom5"}, + {(API_FUNC)customFunc,(char *)"custom6"}, + {(API_FUNC)customFunc,(char *)"custom7"}, + {(API_FUNC)customFunc,(char *)"custom8"}, + {(API_FUNC)customFunc,(char *)"custom9"}, + {(API_FUNC)customFunc,(char *)"custom10"}, + {(API_FUNC)NULL,(char *)NULL} +}; + + +struct SAVEAPPDLL +{ + INT index; + INT appId; + BYTE filename[80]; + API_FUNC currentApiFunc[MAXFUNCS]; + API_FUNC oldApiFunc[MAXFUNCS]; + char * oldApiCmd[MAXFUNCS]; + char * currentCmd[MAXFUNCS]; + void (*unLoadFunc)(void); + void *handle; +}; + +CUSTOMAPI CustomApi[MAXCUSTOMAPPLICATIONS]; // Use this table to define any override functions + +static SAVEAPPDLL *SaveApiDll; +static INT CurrentApiIndex=0; +static INT MyID = 1; +INT MaxCustomApiTable=MAXCUSTOMAPPLICATIONS; + +// Call the generic handler at this index + +API_FUNC _ApiCustomFunc(int funcIndex, int app, char *data_str, void *userVars, void *dispPtr) { + + API_FUNC handler=NULL; + + if(app < MAXAPPLICATIONS && app >=0) + { + pthread_mutex_lock(&ApiFuncMutex); + if(ApiHandler[funcIndex][app].funcPtr) + handler = ApiHandler[funcIndex][app].funcPtr; + else + handler = ApiHandler[funcIndex][0].funcPtr; + pthread_mutex_unlock(&ApiFuncMutex); + if (handler != NULL) + handler(app,data_str,userVars,dispPtr); + } + return EXIT_SUCCESS; +} + +void setApiFunc(APIFUNCID funcId, API_FUNC handle, char *cmd, INT index) +{ + INT appId; + + if(index < 0 || index >= MaxCustomApiTable) + index = CurrentApiIndex; + + appId = SaveApiDll[index].appId; + + if(appId >= 0 && appId < MAXAPPLICATIONS && funcId < API_MAX_FUNC_ID) + { + SaveApiDll[index].oldApiFunc[funcId] = ApiHandler[funcId][appId].funcPtr; + SaveApiDll[index].oldApiCmd[funcId] = ApiHandler[funcId][appId].cmdPtr; + pthread_mutex_lock(&ApiFuncMutex); + ApiHandler[funcId][appId].funcPtr = handle; + ApiHandler[funcId][appId].cmdPtr = cmd; + pthread_mutex_unlock(&ApiFuncMutex); + SaveApiDll[index].currentApiFunc[funcId] = handle; + } +} + +API_FUNC getApiFunc (APIFUNCID funcId, char *cmd, INT appId) +{ + API_FUNC handler = NULL; + if(appId >= 0 && appId < MAXAPPLICATIONS && funcId < API_MAX_FUNC_ID) + { + pthread_mutex_lock(&ApiFuncMutex); + if (ApiHandler[funcId][appId].funcPtr != handler) + handler = ApiHandler[funcId][appId].funcPtr; + pthread_mutex_unlock(&ApiFuncMutex); + } + return handler; +} + +void resetApiFunc(INT index) +{ + APIFUNCID funcId; + INT appId; + + if(index < 0 || index >= MaxCustomApiTable) + index = CurrentApiIndex; + + appId = SaveApiDll[index].appId; + + if(appId >= 0 && appId < MAXAPPLICATIONS ) + { + for(funcId = API_CUSTOM_FUNC_1; funcId < API_MAX_FUNC_ID; + funcId = APIFUNCID(funcId+1)) + { + if(SaveApiDll[index].currentApiFunc[funcId]) + { + pthread_mutex_lock(&ApiFuncMutex); + ApiHandler[funcId][appId].funcPtr = SaveApiDll[index].oldApiFunc[funcId]; + ApiHandler[funcId][appId].cmdPtr = SaveApiDll[index].oldApiCmd[funcId]; + pthread_mutex_unlock(&ApiFuncMutex); + SaveApiDll[index].currentApiFunc[funcId] = NULL; + } + } + } +} +INT saveDLLSetting(INT index,void *handle) { + + SaveApiDll[index].index = index; + SaveApiDll[index].appId = CustomApi[index].appFlag; + SaveApiDll[index].handle = handle; + SaveApiDll[index].unLoadFunc = (VOID (*)(VOID))dlsym(SaveApiDll[index].handle, "appDllUnload"); + + return EXIT_SUCCESS; +} + +INT loadApiDLL(INT index) +{ + + VOID (*appDllInit)(INT index); + + if(index >=0 && index < MaxCustomApiTable && + (CustomApi[index].instanceId == MyID || CustomApi[index].instanceId == 0) ) + { + if(strlen((const char *)CustomApi[index].filename) > 0) + { + if(SaveApiDll[index].handle) + { + ERROR( "Error: Custom APP %d (%s) already loaded.\n", index, SaveApiDll[index].filename); + } + else + { + SaveApiDll[index].index = index; + SaveApiDll[index].appId = CustomApi[index].appFlag; + strcpy((char *)SaveApiDll[index].filename, (const char *)CustomApi[index].filename); + CurrentApiIndex = index; + SaveApiDll[index].handle = dlopen(((const char *)SaveApiDll[index].filename), RTLD_NOW); + if(SaveApiDll[index].handle == NULL) + { + ERROR( "Error: Failed to create handle - Custom APP %d (%s).\n", index, SaveApiDll[index].filename); + return EXIT_FAILURE; + } + else + { + appDllInit = (VOID (*)(const INT))dlsym(SaveApiDll[index].handle, "appDllLoad"); + const char* err = dlerror(); + if(err) + { + ERROR( "Error: Unable to find 'appDllLoad' function for Custom APP %d (%s).\n", index, SaveApiDll[index].filename); + return EXIT_FAILURE; + } + + SaveApiDll[index].unLoadFunc = (VOID (*)(VOID))dlsym(SaveApiDll[index].handle, "appDllUnload"); + + (*appDllInit)(index); + } + } + } + else + { + ERROR( "Error: Custom APP %d name empty(%s).\n", index, CustomApi[index].filename); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +INT reloadApiDll(INT index) +{ + INT ret = EXIT_SUCCESS; + SAVEAPPDLL tempSaveDLL; + APIFUNCID funcId; + INT appId; + + /// save the dll ptrs first, then load new ones, then delete + if(SaveApiDll[index].handle) + { + ERROR( "save DLL index %d name %s\n", index, SaveApiDll[index].filename); + memcpy(&tempSaveDLL, &SaveApiDll[index], sizeof(SAVEAPPDLL)); + SaveApiDll[index].handle = NULL; + SaveApiDll[index].filename[0] = 0; + SaveApiDll[index].appId = 0; + + ret = loadApiDLL(index); + if(ret != EXIT_SUCCESS) + { + ERROR( "reload DLL Failed: index %d name %s, rollback to previous one\n", index, SaveApiDll[index].filename); + memcpy(&SaveApiDll[index], &tempSaveDLL, sizeof(SAVEAPPDLL)); + } + else /// load new one succeeded, delete old ones + { + ERROR( "delete old DLL index %d name %s\n", index, SaveApiDll[index].filename); + + appId = tempSaveDLL.appId; + + if(appId >= 0 && appId < MAXAPPLICATIONS ) + { + for(funcId = API_CUSTOM_FUNC_1; funcId < API_MAX_FUNC_ID; + funcId = APIFUNCID(funcId+1)) + { + if(tempSaveDLL.currentApiFunc[funcId] && SaveApiDll[index].currentApiFunc[funcId] == NULL) /// no more needed + { + pthread_mutex_lock(&ApiFuncMutex); + ApiHandler[funcId][appId].funcPtr = tempSaveDLL.oldApiFunc[funcId]; + ApiHandler[funcId][appId].cmdPtr = tempSaveDLL.oldApiCmd[funcId]; + pthread_mutex_unlock(&ApiFuncMutex); + } + } + + } + } + } + else /// last time failed to load + { + /// just load it + ret = loadApiDLL(index); + } + + ERROR( "reload DLL index %d name %s finished %d\n", index, SaveApiDll[index].filename, ret); + return ret; +} + +INT unloadApiDll(INT index) +{ + INT ret = EXIT_SUCCESS; + + if(SaveApiDll[index].handle) + { + CurrentApiIndex = index; + resetApiFunc(index); + + if(SaveApiDll[index].unLoadFunc) + { + (*SaveApiDll[index].unLoadFunc)(); + } + + dlclose(SaveApiDll[index].handle); + SaveApiDll[index].handle = NULL; + ERROR( "Unloaded index %d name %s\n", index, SaveApiDll[index].filename); + SaveApiDll[index].filename[0] = 0; + SaveApiDll[index].appId = 0; + } + else + { + ERROR( "Error: appId or name mismatch for Custom APP %d(%s).\n", index, SaveApiDll[index].filename); + ret = EXIT_FAILURE; + } + return ret; +} + + +INT loadCustomApis(VOID) +{ + initSaveApi(); + for(INT i=0; i #include #include +#include #include #include @@ -60,6 +61,11 @@ #include "deadcall.hpp" #include "config.h" #include "version.h" +#include "apifunc.h" + +using namespace std; + +bool log4auto_answer = true; // This just adds the ability to not log auto_answer via a plugin template void split(const std::string &s, char delim, Out result) { @@ -3005,7 +3011,25 @@ bool call::process_incoming(const char* msg, const struct sockaddr_storage* src) } } else { // call aborted by automatic response mode if needed - return automaticResponseMode(L_case, msg); + // + int handled=0; + bool (*handle_auto_response)(T_AutoMode, const char* ,int *, call *); // Plugin function for auto response + handle_auto_response = (bool (*)(T_AutoMode,const char* ,int *, call * ))dlsym(g_plugin_handle, "handle_auto_response"); + const char* error; + if ((error = dlerror())) { + ERROR("Could not locate handle_auto_response function in plugin: error:(%s)", error); + }else { + + // If there's a hook, then run it to do whatever processing is defined in the plugin + // and if there's no more processing required, return something > 0 + bool ret = handle_auto_response(L_case,msg,&handled,this); + if (handled > 0) { + return ret; + }else { + return automaticResponseMode(L_case, msg); + } + + } } } } @@ -3658,6 +3682,39 @@ call::T_ActionResult call::executeAction(const char* msg, message* curmsg) } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); // TRACE_MSG("Trying to execute [%s]", x); + + // Find out what command we're doing + std::string str = x; + std::string delimiter = "@"; + int end = str.find(delimiter); + std::string command = str.substr(0, end); + std::string data = str.substr(end+1); + char data_str[4096]; + memset(data_str,0,sizeof(data_str)); + strcpy(data_str,data.c_str()); + + // Process possible commands that override the default functionality: format is command@ + // custom calls - user defined + // + // + // then check the magic variable $msgDone for a non-zero value indicating the other args are also updated - usually the same list of tag value pairs sent down + // + // if we don't find anything - do a forked system call - default sipp behavior + // We pass in pointers to the call variables and the current scenario to allow manipulation of sipp variables + // These should be initialized in a plugin - see myapp.cpp for a sample + + int found = false; + for (int i=0;(igetActionType() == CAction::E_AT_EXEC_INTCMD) { switch (currentAction->getIntCmd()) { case CAction::E_INTCMD_STOP_ALL: @@ -4042,8 +4101,10 @@ bool call::automaticResponseMode(T_AutoMode P_case, const char* P_recv) strcpy(last_recv_msg, P_recv); - WARNING("Automatic response mode for an unexpected INFO, NOTIFY, OPTIONS or UPDATE for call: %s", - (id == NULL) ? "none" : id); + if (log4auto_answer) { + WARNING("Automatic response mode for an unexpected INFO, NOTIFY, OPTIONS or UPDATE for call: %s", + (id == NULL) ? "none" : id); + } sendBuffer(createSendingMessage(get_default_message("200"), -1)); // restore previous last msg @@ -4065,6 +4126,7 @@ bool call::automaticResponseMode(T_AutoMode P_case, const char* P_recv) } } CStat::globalStat(CStat::E_AUTO_ANSWERED); + delete this; // 2/16/2022 - NVF Added this for auto response messages as it looked like it wasn't being done otherwise - resulting in a memory leak return true; break; diff --git a/src/customfuncs.cpp b/src/customfuncs.cpp new file mode 100644 index 000000000..47625b3d5 --- /dev/null +++ b/src/customfuncs.cpp @@ -0,0 +1,18 @@ +/* + * This is a stub function that serves as a placeholder for any plugins that might be added + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "apifunc.h" + + + +API_FUNC customFunc(int app, char *data_str, void *userVars, void *dispPtr){return EXIT_SUCCESS;} diff --git a/src/logger.cpp b/src/logger.cpp index 7d031d52a..a3ba201fa 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -47,16 +47,20 @@ unsigned long total_errors = 0; -void log_off(struct logfile_info* lfi) + +void log_off(struct logfile_info *lfi) { if (lfi->fptr) { + pthread_mutex_lock(lfi->lockfile); fflush(lfi->fptr); fclose(lfi->fptr); lfi->fptr = NULL; lfi->overwrite = false; + pthread_mutex_unlock(lfi->lockfile); } } + void print_count_file(FILE* f, int header) { char temp_str[256]; @@ -217,12 +221,14 @@ void print_error_codes_file(FILE* f) fflush(f); } + /* Function to dump all available screens in a file */ void print_screens(void) { int oldScreen = currentScreenToDisplay; int oldRepartition = currentRepartitionToDisplay; + pthread_mutex_lock(screen_lfi.lockfile); currentScreenToDisplay = DISPLAY_SCENARIO_SCREEN; sp->print_to_file(screen_lfi.fptr); @@ -239,6 +245,7 @@ void print_screens(void) sp->print_to_file(screen_lfi.fptr); } + pthread_mutex_unlock(screen_lfi.lockfile); currentScreenToDisplay = oldScreen; currentRepartitionToDisplay = oldRepartition; } @@ -315,27 +322,63 @@ static void rotatef(struct logfile_info* lfi) ERROR("Unable to create '%s'", lfi->file_name); } } +void rotate_screenf() +{ + pthread_mutex_lock(screen_lfi.lockfile); + rotatef(&screen_lfi); + pthread_mutex_unlock(screen_lfi.lockfile); +} -void rotate_screenf() { rotatef(&screen_lfi); } +void rotate_calldebugf() +{ + pthread_mutex_lock(calldebug_lfi.lockfile); + rotatef(&calldebug_lfi); + pthread_mutex_unlock(calldebug_lfi.lockfile); +} -void rotate_calldebugf() { rotatef(&calldebug_lfi); } +void rotate_messagef() +{ + pthread_mutex_lock(message_lfi.lockfile); + rotatef(&message_lfi); + pthread_mutex_unlock(message_lfi.lockfile); +} -void rotate_messagef() { rotatef(&message_lfi); } -void rotate_shortmessagef() { rotatef(&shortmessage_lfi); } +void rotate_shortmessagef() +{ + pthread_mutex_lock(shortmessage_lfi.lockfile); + rotatef(&shortmessage_lfi); + pthread_mutex_unlock(shortmessage_lfi.lockfile); +} + -void rotate_logfile() { rotatef(&log_lfi); } +void rotate_logfile() +{ + pthread_mutex_lock(log_lfi.lockfile); + rotatef(&log_lfi); + pthread_mutex_unlock(log_lfi.lockfile); +} void rotate_errorf() { + pthread_mutex_lock(error_lfi.lockfile); rotatef(&error_lfi); + pthread_mutex_unlock(error_lfi.lockfile); strcpy(screen_logfile, error_lfi.file_name); } +void rotate_errorf_nolock() +{ + rotatef(&error_lfi); + strcpy(screen_logfile, error_lfi.file_name); +} + + static int _trace(struct logfile_info* lfi, const char* fmt, va_list ap) { int ret = 0; - if (lfi->fptr) { + if(lfi->fptr) { + pthread_mutex_lock(lfi->lockfile); ret = vfprintf(lfi->fptr, fmt, ap); fflush(lfi->fptr); @@ -350,10 +393,12 @@ static int _trace(struct logfile_info* lfi, const char* fmt, va_list ap) rotatef(lfi); lfi->count = 0; } + pthread_mutex_unlock(lfi->lockfile); } return ret; } + int TRACE_MSG(const char* fmt, ...) { int ret; diff --git a/src/myapp.cpp b/src/myapp.cpp new file mode 100644 index 000000000..01beab80f --- /dev/null +++ b/src/myapp.cpp @@ -0,0 +1,994 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Description: This is a demo program demonstrating customizations to sipp via a plugin to support + * + * 1. Running LUA inside of a sipp test script, sending values to lua from sipp + * and returning values to local sipp variables. This includes the ability to utilize curl and mysql in the lua scripts + * + * 2. The capability to do RPC calls to a running sipp instance to get various call counts and enable/disable + * logging on auto response messages + * + * 3. Processing some custom startup arguments to capture the running pid and to get the name of the lua file + * + * + * Author: Nathan Franzmeier Sky Networks LLC 2022 + * Mail: Nathan.Franzmeier@sky-networks.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +// Added for LUA support + +#ifdef __cplusplus + #include "lua.hpp" +#else + #include "lua.h" + #include "lualib.h" + #include "lauxlib.h" +#endif + + +// Added for shared memory support +#include +#include +#include +#include + + +#include +#include + +#ifdef PCAPPLAY +#include "send_packets.h" +#endif +#include "sipp.hpp" +#include "auth.hpp" +#include "deadcall.hpp" +#include "config.h" +#include "version.h" +#include "apifunc.h" +#include "call.hpp" + +// From call.hpp + /* Automatic */ + enum T_AutoMode { + E_AM_DEFAULT, + E_AM_UNEXP_BYE, + E_AM_UNEXP_CANCEL, + E_AM_PING, + E_AM_AA, + E_AM_OOCALL + }; + + +// For CURL support + +#include "curl.h" +#include "easy.h" + +// For mysql thread support +#include "mysql.h" + +// For rpc support +#include "sipp-rpc.h" +#include + +#define callDebug(...) do { if (useCallDebugf) { _callDebug( __VA_ARGS__ ); } } while (0) + +#define MAXARGS 50 // Most argument pairs that can be passed into a lua script +extern map map_perip_fd; +typedef struct tag_value { + char tag[80]; + char value[2048]; + +}TAG_VAL; + +typedef struct tv_array { + + int count; + bool useThread; + bool dataIsReady; // Flag indicating data has been set + pthread_t myThread; + TAG_VAL element[MAXARGS]; + +} TV_ARRAY; + + +void bail(lua_State *L, char *msg){ +//WARNING( "\nLUA ERROR:\n %s: %s\n\n", +fprintf( stderr, "\nLUA ERROR:\n %s: %s\n\n", + msg, lua_tostring(L, -1)); +} +typedef struct thread_data { + char data_str[4096]; + char name[256]; // shared memory name + int fd; + lua_State *L; + +} THREAD_DATA; + +void do_lua_thread(THREAD_DATA *thread_data); +void do_lua_thread_detached(THREAD_DATA *thread_data); +void rpc_thread(void* param); + +pthread_t pthread4_id = 0; // Thread id for the RPC thread + +#define LUA_LIB + +extern "C" +{ + + static const luaL_Reg lualibs[] = { + {"", luaopen_base}, + {LUA_COLIBNAME, luaopen_coroutine}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_UTF8LIBNAME, luaopen_utf8}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {LUA_LOADLIBNAME, luaopen_package}, + {NULL, NULL} + }; + + LUALIB_API void my_openlibs (lua_State *L) { + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + fprintf(stderr,"lua_call result (%s):%s\n",lib->name, lua_tostring(L, -1)); + } + } +} +extern "C" +{ +char lua_file[128]; +lua_State* openLua() { + + + lua_State *L = luaL_newstate(); + if (L) { + + luaL_openlibs(L); /* Load Lua libraries */ + //my_openlibs(L); + + if (luaL_loadfile(L, lua_file)) /* Load but don't run the Lua script */ + { + bail(L, (char *)"luaL_loadfile() failed"); /* Error out if file can't be read */ + L=0; + } else{ + + if (lua_pcall(L, 0, 0, 0)) /* PRIMING RUN. FORGET THIS AND YOU'RE TOAST */ + { + bail(L, (char *)"lua_pcall1() failed"); /* Error out if Lua file has an error */ + L=0; + } + } + } + return L; +} +} + +API_FUNC customFunc(int app, char *data_str, void *userVars, void *dispPtr){ return EXIT_SUCCESS;} +API_FUNC processLuaRun(int app, char *data_str, void *tblPtr, void *dispPtr){ + VariableTable *userVars = (VariableTable *)tblPtr; + scenario *display_scenario = (scenario *)dispPtr; + char tmp[80]; + char *fname; + char *name; + int msize = sizeof(TV_ARRAY); + TV_ARRAY *shared_memory; + fname = strdup(tmpnam(tmp)); + name = strdup(basename(fname)); + free(fname); + + // Allocate some shared memory to pass the variables later after this executes + TRACE_MSG("Opening shared memory for [%s] - data:(%s)",name,data_str); + int shm_fd = shm_open (name, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG); + if (shm_fd < 0) + { + WARNING("Error in shm_open(%s)",name); + goto fail; + } + ftruncate(shm_fd, msize); + + TRACE_MSG("Created shared memory object %s\n", name); + + + // attach the shared memory segment + shared_memory = (TV_ARRAY *) mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shared_memory == MAP_FAILED) + { + WARNING("Error 1 in mmap()"); + goto fail; + } + + TRACE_MSG("Shared memory segment allocated correctly (%d bytes).\n", msize); + // Save off the shared memory segment name for now so we can get it later - pass this back to the sipp script + shared_memory->count = 0; + shared_memory->dataIsReady = 0; + strcpy(shared_memory->element[shared_memory->count].tag,"retkey"); // This is a magic variable name which is used by the script to get our values back with a luaread@retkey + strcpy(shared_memory->element[shared_memory->count].value,name); + free(name); + shared_memory->useThread = false; + shared_memory->count++; + + pid_t l_pid; + switch(l_pid = fork()) { + case -1: + // error when forking ! + ERROR_NO("Forking error main"); + break; + + case 0: + // first child process - execute the command + if((l_pid = fork()) < 0) { + ERROR_NO("Forking error child"); + } else { + if( l_pid == 0) { + THREAD_DATA *thread_data = (THREAD_DATA *)calloc(1,sizeof(THREAD_DATA)); + strcpy(thread_data->data_str,data_str); + strcpy(thread_data->name,name); + do_lua_thread(thread_data); + } + + exit(EXIT_OTHER); + }// end else + break; + default: + // parent process continue + // reap first child immediately + pid_t ret; + while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) { + if (ret != -1) { + ERROR("waitpid returns %1d for child %1d",ret,l_pid); + } + } + int index; + for (int i=0;icount;i++) + { + // Set the internal variable value based on the returned table + index = display_scenario->allocVars->find(shared_memory->element[i].tag,false); + if (index>0) + userVars->getVar(index)->setString(strdup(shared_memory->element[i].value)); + TRACE_MSG("Setting TV pair 1 (%s/%s)\n",shared_memory->element[i].tag, shared_memory->element[i].value); + //printf("Setting TV pair (%s/%s)\n",shared_memory->element[i].tag, shared_memory->element[i].value); + } + munmap(shared_memory,sizeof(TV_ARRAY)); + // Don't unlink here, we will do this later when we retrieve the value + //shm_unlink(name); + break; + } + fail: + TRACE_MSG("End luarun\n"); + return EXIT_SUCCESS; +} +API_FUNC processLuaRead(int app, char *data_str, void *tblPtr, void *dispPtr){ + VariableTable *userVars = (VariableTable *)tblPtr; + scenario *display_scenario = (scenario *)dispPtr; + + // Retrieve previously read variables + + int msize = sizeof(TV_ARRAY); + TV_ARRAY *shared_memory; + char *tmptr; + // char *name = data_str; + // + char *name = strtok_r(data_str," ",&tmptr); + char *deleteMem = strtok_r(NULL," ",&tmptr); + /*char *timeout = strtok_r(NULL," ",&tmptr); + uintmax_t millisecondsToWait = 0; + // Get the timeout + if (timeout!=0) + { + millisecondsToWait = strtoumax(timeout, NULL, 10); + if (millisecondsToWait == UINTMAX_MAX && errno == ERANGE){ + WARNING("Failed to convert timeout"); + millisecondsToWait = 0; + } + + + } + */ + + // Reattach to the named segment and read the valuse from it + int shm_fd = shm_open(name, O_RDONLY, 0666); + // allocating the shared memory + shared_memory = (TV_ARRAY *) mmap(NULL, msize, PROT_READ, MAP_SHARED, shm_fd, 0); + if (shared_memory == MAP_FAILED) + { + WARNING("Error 3 in mmap()"); + + } else { + + /* + // Pause for the required time and then retrieve the data + //for (int t=0;tdataIsReady;t++) { + for (int t=0;tuseThread && shared_memory->myThread.joinable()) + //shared_memory->myThread.join(); + + // Read the data + int index; + for (int i=0;icount;i++) + { + // Set the internal variable value based on the returned table + index = display_scenario->allocVars->find(shared_memory->element[i].tag,false); + if (index>0) + userVars->getVar(index)->setString(strdup(shared_memory->element[i].value)); + TRACE_MSG("Setting TV pair 3(%s/%s)\n",shared_memory->element[i].tag, shared_memory->element[i].value); + //printf("Setting TV pair (%s/%s)\n",shared_memory->element[i].tag, shared_memory->element[i].value); + } + munmap(shared_memory,sizeof(TV_ARRAY)); + close(shm_fd); + if (strcmp(deleteMem,"1")==0) { + shm_unlink(name); + TRACE_MSG("Deleting shared memory(%s)\n",name); + } + + + } + return EXIT_SUCCESS; + +} +//_ApiProcessLuaThread +API_FUNC processLuaThread(int app, char *data_str, void *tblPtr, void *dispPtr){ + VariableTable *userVars = (VariableTable *)tblPtr; + scenario *display_scenario = (scenario *)dispPtr; + char tmp[128]; + char *fname; + char tmpfname[128]; + char *name; + int msize = sizeof(TV_ARRAY); + TV_ARRAY *shared_memory; + fname = tmpnam(tmp); + strcpy(tmpfname,fname); + name = basename(tmpfname); + + //if (true) { + // Allocate some shared memory to pass the variables later after this executes + TRACE_MSG("Opening shared memory for [%s] - data:(%s)",name,data_str); + int shm_fd = shm_open (name, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG); + if (shm_fd < 0) + { + WARNING("Error in shm_open(%s)",name); + } else { + + ftruncate(shm_fd, msize); + TRACE_MSG("Created shared memory object %s\n", name); + // attach the shared memory segment + shared_memory = (TV_ARRAY *) mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shared_memory == MAP_FAILED) + { + WARNING("Error 2 in mmap()"); + } else { + + TRACE_MSG("Shared memory segment allocated correctly (%d bytes).\n", msize); + // Save off the shared memory segment name for now so we can get it later - pass this back to the sipp script + shared_memory->count = 0; + shared_memory->dataIsReady = 0; + strcpy(shared_memory->element[shared_memory->count].tag,"retkey"); // This is a magic variable name which is used by the script to get our values back with a luaread@retkey + strcpy(shared_memory->element[shared_memory->count].value,name); + shared_memory->useThread = true; + shared_memory->count++; + + THREAD_DATA *thread_data = (THREAD_DATA *)calloc(1,sizeof(THREAD_DATA)); + strcpy(thread_data->data_str,data_str); + strcpy(thread_data->name,name); + // Create a detached thread so we don't need to join it later + TRACE_MSG("Create pthread shared memory\n"); + if (pthread_create(&shared_memory->myThread, NULL, (void *(*)(void *))do_lua_thread_detached, thread_data) == -1) { + ERROR_NO("Unable to create lua thread"); + } + + + int index; + for (int i=0;icount;i++) + { + // Set the internal variable value based on the returned table + index = display_scenario->allocVars->find(shared_memory->element[i].tag,false); + if (index>0) + userVars->getVar(index)->setString(strdup(shared_memory->element[i].value)); + TRACE_MSG("Setting TV pair 2(%s/%s)\n",shared_memory->element[i].tag, shared_memory->element[i].value); + } + munmap(shared_memory,sizeof(TV_ARRAY)); + } + close(shm_fd); + } + //} + //shm_unlink(name); + TRACE_MSG("Ending lua thread\n"); + return EXIT_SUCCESS; + +} + +// This is a thread for executing our lua command +void do_lua_thread_detached(THREAD_DATA *thread_data) { + mysql_thread_init(); + pthread_detach(pthread_self()); + do_lua_thread(thread_data); + mysql_thread_end(); + pthread_exit(NULL); + +} + +void do_lua_thread(THREAD_DATA *thread_data) { + + char data_str[4096]; + TV_ARRAY *shared_memory; + strcpy(data_str,thread_data->data_str); + int shm_fd = shm_open (thread_data->name, O_RDWR, S_IRWXU | S_IRWXG); + char *tmptr; + if (shm_fd < 0) + { + ERROR("Error in shm_open(%s)",thread_data->name); + } else { + + // attach the shared memory segment + shared_memory = (TV_ARRAY *) mmap(NULL, sizeof(TV_ARRAY), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shared_memory == MAP_FAILED) + { + ERROR("Error 4 in mmap()"); + } else { + //strcpy(data_str,data.c_str()); + + //int shm_fd = thread_data->fd; + char *prog = strtok_r(data_str," ",&tmptr); + char *name[80]; + char *val[2048]; + int i=0; + + if (prog!=NULL) + { + thread_data->L = openLua(); + if (!thread_data->L) { + WARNING("Unable to create a new LUA state!\n"); + } + else { + //printf("In C, calling Lua->%s()\n",x); + lua_getglobal(thread_data->L, prog); /* Tell it to run sipp.lua->xxx() */ + lua_newtable(thread_data->L); /* Push empty table onto stack table now at -1 */ + + for (int i=0;iL, name[i]); /* Push a key onto the stack, table now at -2 */ + lua_pushstring(thread_data->L, val[i]); /* Push a value onto the stack, table now at -3 */ + lua_settable(thread_data->L, -3); /* Take key and value, put into table at -3, */ + + } + + int totalargs = i; + + if (lua_pcall(thread_data->L, 1, 1, 0)) // Run function, !!! NRETURN=1 !!! + + bail(thread_data->L, (char *)"lua_pcall2() failed"); + else { + + //printf("============ Back in C again, Iterating thru returned table ============\n"); + + // table is in the stack at index 't' + lua_pushnil(thread_data->L); // Make sure lua_next starts at beginning + const char *k, *v; + int index; + while (lua_next(thread_data->L, -2)) { // TABLE LOCATED AT -2 IN STACK + + v = lua_tostring(thread_data->L, -1); // Value at stacktop + lua_pop(thread_data->L,1); // Remove value + k = lua_tostring(thread_data->L, -1); // Read key at stacktop, + if (k != 0) + { + strcpy(shared_memory->element[shared_memory->count].tag,k); + if (v != 0) { + strcpy(shared_memory->element[shared_memory->count].value,v); + shared_memory->count++; + TRACE_MSG("Fromc k=>%s<, v=>%s<\n", k, v); + } + } + else { + TRACE_MSG("Nothing on return stack\n"); + } + } + shared_memory->dataIsReady = 1; // Mark the data as being available. + } + lua_gc(thread_data->L, LUA_GCCOLLECT, 0); + lua_close(thread_data->L); + + // 02/15/2022 - NVF Added trim capability based on the environment variable TRIM_MEMORY + char* trim_mem = getenv("TRIM_MEMORY"); + if (trim_mem) { + malloc_trim(M_TOP_PAD); + TRACE_MSG("Trim memory\n"); + } + + } + } + munmap(shared_memory,sizeof(TV_ARRAY)); + close(shm_fd); + } + } + free(thread_data); +} + +bool_t getcallcounter_1_svc(callcounter_t *argp, callcounter_t *result, struct svc_req *rqstp) +{ + bool_t retval; + + /* + * insert server code here + */ + + unsigned long long incomingcalls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated); + unsigned long long outgoingcalls = main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); + unsigned long long successcalls = main_scenario->stats->GetStat(CStat::CPT_C_SuccessfulCall); + unsigned long long failurecalls = main_scenario->stats->GetStat(CStat::CPT_C_FailedCall); + + result->incomingcalls=incomingcalls; + result->outgoingcalls=outgoingcalls; + result->successcalls=successcalls; + result->failurecalls=failurecalls; + + retval = true; + return retval; +} + +bool_t enablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + extern bool log4auto_answer; + /* + * insert server code here + */ + + log4auto_answer=true; + retval = true; + return retval; +} + +bool_t disablelog4autoanswer_1_svc(void *argp, bool_t *result, struct svc_req *rqstp) +{ + bool_t retval; + extern bool log4auto_answer; + + /* + * insert server code here + */ + + log4auto_answer=false; + retval = true; + return retval; +} + +int sipprpcprog_1_freeresult (SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) +{ + xdr_free (xdr_result, result); + + /* + * Insert additional freeing code here, if needed + */ + + return 1; +} +void sipprpcprog_1(struct svc_req *rqstp, register SVCXPRT *transp) +{ + union { + callcounter_t getcallcounter_1_arg; + } argument; + union { + callcounter_t getcallcounter_1_res; + bool_t enablelog4autoanswer_1_res; + bool_t disablelog4autoanswer_1_res; + } result; + bool_t retval; + xdrproc_t _xdr_argument, _xdr_result; + bool_t (*local)(char *, void *, struct svc_req *); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); + return; + + case GETCALLCOUNTER: + _xdr_argument = (xdrproc_t) xdr_callcounter_t; + _xdr_result = (xdrproc_t) xdr_callcounter_t; + local = (bool_t (*) (char *, void *, struct svc_req *))getcallcounter_1_svc; + break; + + case ENABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))enablelog4autoanswer_1_svc; + break; + + case DISABLELOG4AUTOANSWER: + _xdr_argument = (xdrproc_t) xdr_void; + _xdr_result = (xdrproc_t) xdr_bool; + local = (bool_t (*) (char *, void *, struct svc_req *))disablelog4autoanswer_1_svc; + break; + + default: + svcerr_noproc (transp); + return; + } + memset ((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + svcerr_decode (transp); + return; + } + retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp); + if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) { + svcerr_systemerr (transp); + } + if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + fprintf (stderr, "%s", "unable to free arguments"); + exit (1); + } + if (!sipprpcprog_1_freeresult (transp, _xdr_result, (caddr_t) &result)) + fprintf (stderr, "%s", "unable to free results"); + + return; +} + +// Remote procedure calls thread +void rpc_thread(void* param) +{ + register SVCXPRT *transp; + sigset_t mask; + sigfillset(&mask); /* Mask all allowed signals */ + int rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); + if (rc) { + WARNING("pthread_sigmask returned %d", rc); + return; + } + + pmap_unset (SIPPRPCPROG, SIPPRPCVERS); + + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + fprintf (stderr, "%s", "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_UDP)) { + ERROR( "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, udp)."); + exit(1); + } + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) { + ERROR( "%s", "cannot create tcp service."); + exit(1); + } + if (!svc_register(transp, SIPPRPCPROG, SIPPRPCVERS, sipprpcprog_1, IPPROTO_TCP)) { + ERROR( "%s", "unable to register (SIPPRPCPROG, SIPPRPCVERS, tcp)."); + exit(1); + } + + svc_run (); + ERROR( "rpc_thread svc_run returned\n"); + pthread_exit(NULL); // 2/8/2022 - NVF Added + //exit (1); + /* NOTREACHED */ +} + + +// +// Replace various custom functions with our own +// +extern "C" void appDllLoad(INT index) +{ + + TRACE_MSG( "Custom App loaded!.\n"); + + + /* and/or by overriding default app event handler here */ + setApiFunc(API_CUSTOM_FUNC_1, (API_FUNC)processLuaRun,(char *)"luarun",index); + setApiFunc(API_CUSTOM_FUNC_2, (API_FUNC)processLuaThread,(char *)"luathread",index); + setApiFunc(API_CUSTOM_FUNC_3, (API_FUNC)processLuaRead,(char *)"luaread",index); + +} + +// This hooks into the built-in plugin capability of sipp and allows you to add EXEC commands + +extern "C" int init(void *handle,int argc, char *argv[]) { + + extern bool log4auto_answer; + + // 2/9/2022 NVF This has to be above the SIGPIPE handler (added to support mysqlclient multithreaded): https://dev.mysql.com/doc/c-api/8.0/en/c-api-threaded-clients.html + if (mysql_library_init(0, NULL, NULL)) { + fprintf(stderr, "Warning: Could not initialize MySQL client library. MySQL should not be used in a threaded call as a result\n"); + exit(1); + } + + log4auto_answer = false; + + curl_global_init(CURL_GLOBAL_DEFAULT); // This is so we can use CURL inside our lua scripts in multi-threaded environments + + // Enable the rpc thread + if (pthread_create(&pthread4_id, NULL, + (void *(*)(void *)) rpc_thread, NULL) == -1) { + ERROR_NO("Unable to create rpc thread"); + } + // Get the name of the lua file + for (int i=0;igetLastReceived(); + int msg_index = call_ptr->get_msg_index(); + char * id = call_ptr->get_id(); + extern bool log4auto_answer; + + *handled = 1; // Mark it as handled + + + WARNING("Processing auto_response for unexpected message\n"); + + switch (P_case) { + case E_AM_UNEXP_BYE: // response for an unexpected BYE + // usage of last_ keywords + WARNING("Processing auto_response for unexpected BYE\n"); + call_ptr->set_realloc_ptr ( (char *) realloc(last_recv_msg, strlen(P_recv) + 1)); + if (call_ptr->get_realloc_ptr()) { + call_ptr->setLastReceived(call_ptr->get_realloc_ptr()); + } else { + free(last_recv_msg); + ERROR("Out of memory!"); + return false; + } + + + strcpy(call_ptr->getLastReceived(), P_recv); + + // The BYE is unexpected, count it + call_ptr->increment_call_scenario_unexpected_count(); + if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { + WARNING("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { + call_ptr->public_sendBuffer(call_ptr->public_createSendingMessage(get_default_message("200"), -1)); + } + + // if twin socket call => reset the other part here + if (twinSippSocket && (msg_index > 0)) { + res = call_ptr->public_sendCmdBuffer(call_ptr->public_createSendingMessage(get_default_message("3pcc_abort"), -1)); + if (res) { + WARNING("sendCmdBuffer returned %d", res); + return false; + } + } + call_ptr->public_computeStat(CStat::E_CALL_FAILED); + call_ptr->public_computeStat(CStat::E_FAILED_UNEXPECTED_MSG); + delete call_ptr; + } else { + WARNING("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id); + } + break ; + + case E_AM_UNEXP_CANCEL: // response for an unexpected cancel + WARNING("Processing auto_response for unexpected CANCEL\n"); + // usage of last_ keywords + call_ptr->set_realloc_ptr ( (char *) realloc(last_recv_msg, strlen(P_recv) + 1)); + if (call_ptr->get_realloc_ptr()) { + call_ptr->setLastReceived(call_ptr->get_realloc_ptr()); + } else { + free(last_recv_msg); + ERROR("Out of memory!"); + return false; + } + + + strcpy(call_ptr->getLastReceived(), P_recv); + + // The CANCEL is unexpected, count it + call_ptr->increment_call_scenario_unexpected_count(); + if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { + WARNING("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id); + if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { + call_ptr->public_sendBuffer(call_ptr->public_createSendingMessage(get_default_message("200"), -1)); + } + + // if twin socket call => reset the other part here + if (twinSippSocket && (msg_index > 0)) { + res = call_ptr->public_sendCmdBuffer(call_ptr->public_createSendingMessage(get_default_message("3pcc_abort"), -1)); + if (res) { + WARNING("sendCmdBuffer returned %d", res); + return false; + } + } + + call_ptr->public_computeStat(CStat::E_CALL_FAILED); + call_ptr->public_computeStat(CStat::E_FAILED_UNEXPECTED_MSG); + delete call_ptr; + } else { + WARNING("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id); + } + break ; + + case E_AM_PING: // response for a random ping + // usage of last_ keywords + WARNING("Processing auto_response for unexpected PING\n"); + call_ptr->set_realloc_ptr ( (char *) realloc(last_recv_msg, strlen(P_recv) + 1)); + if (call_ptr->get_realloc_ptr()) { + call_ptr->setLastReceived(call_ptr->get_realloc_ptr()); + } else { + free(last_recv_msg); + ERROR("Out of memory!"); + return false; + } + + + strcpy(call_ptr->getLastReceived(), P_recv); + + if (default_behaviors & DEFAULT_BEHAVIOR_PINGREPLY) { + WARNING("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id); + call_ptr->public_sendBuffer(call_ptr->public_createSendingMessage(get_default_message("200"), -1)); + // Note: the call ends here but it is not marked as bad. PING is a + // normal message. + // if twin socket call => reset the other part here + if (twinSippSocket && (msg_index > 0)) { + res = call_ptr->public_sendCmdBuffer(call_ptr->public_createSendingMessage(get_default_message("3pcc_abort"), -1)); + if (res) { + WARNING("sendCmdBuffer returned %d", res); + return false; + } + } + + CStat::globalStat(CStat::E_AUTO_ANSWERED); + delete call_ptr; + } else { + WARNING("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id); + } + break ; + + case E_AM_AA: // response for a random INFO, NOTIFY, OPTIONS or UPDATE + // store previous last msg if msg is INFO, NOTIFY, OPTIONS or UPDATE + // restore last_recv_msg to previous one + // after sending ok + WARNING("Processing auto_response for unexpected INFO, NOTIFY, OPTIONS or UPDATE\n"); + old_last_recv_msg = NULL; + if (last_recv_msg != NULL) { + last_recv_msg_saved = true; + old_last_recv_msg = (char *) malloc(strlen(last_recv_msg)+1); + strcpy(old_last_recv_msg, last_recv_msg); + } + // usage of last_ keywords + call_ptr->set_realloc_ptr ( (char *) realloc(last_recv_msg, strlen(P_recv) + 1)); + if (call_ptr->get_realloc_ptr()) { + call_ptr->setLastReceived(call_ptr->get_realloc_ptr()); + } else { + free(last_recv_msg); + free(old_last_recv_msg); + ERROR("Out of memory!"); + return false; + } + + + strcpy(call_ptr->getLastReceived(), P_recv); + if (log4auto_answer) { + WARNING("Automatic response mode for an unexpected INFO, NOTIFY, OPTIONS or UPDATE for call: %s", + (id == NULL) ? "none" : id); + } + call_ptr->public_sendBuffer(call_ptr->public_createSendingMessage(get_default_message("200"), -1)); + + // restore previous last msg + if (last_recv_msg_saved == true) { + call_ptr->set_realloc_ptr((char *) realloc(call_ptr->getLastReceived(), strlen(old_last_recv_msg) + 1)); + if (call_ptr->get_realloc_ptr()) { + call_ptr->setLastReceived(call_ptr->get_realloc_ptr()); + } else { + free(call_ptr->getLastReceived()); + ERROR("Out of memory!"); + return false; + } + + + strcpy(call_ptr->getLastReceived(), old_last_recv_msg); + + if (old_last_recv_msg != NULL) { + free(old_last_recv_msg); + old_last_recv_msg = NULL; + } + } + CStat::globalStat(CStat::E_AUTO_ANSWERED); + delete call_ptr; // 2/16/2022 - NVF Added this for auto response messages as it looked like it wasn't being done otherwise - resulting in a memory leak + return true; + break; + + default: + ERROR("Internal error for automaticResponseMode - mode %d is not implemented!", P_case); + break ; + } + + return false; +} diff --git a/src/scenario.cpp b/src/scenario.cpp index 1ef47393c..be39501ba 100644 --- a/src/scenario.cpp +++ b/src/scenario.cpp @@ -1008,9 +1008,11 @@ scenario::scenario(char * filename, int deflt) /* Close scenario element */ xp_close_element(); - if (xp_is_invalid()) { + /* 3/23/2022 - NVF Commented when trying to use the old xml_parser.c so our current SIPP scripts work! + if (xp_is_invalid()) { ERROR("Invalid XML in scenario"); } + */ str_int_map::iterator label_it = labelMap.find("_unexp.main"); if (label_it != labelMap.end()) { diff --git a/src/sipp-rpc_xdr.c b/src/sipp-rpc_xdr.c new file mode 100644 index 000000000..9dd8e3835 --- /dev/null +++ b/src/sipp-rpc_xdr.c @@ -0,0 +1,71 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "sipp-rpc.h" + +bool_t +xdr_callcounter (XDR *xdrs, callcounter *objp) +{ + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + IXDR_PUT_LONG(buf, objp->incomingcalls); + IXDR_PUT_LONG(buf, objp->outgoingcalls); + IXDR_PUT_LONG(buf, objp->successcalls); + IXDR_PUT_LONG(buf, objp->failurecalls); + } + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + } else { + objp->incomingcalls = IXDR_GET_LONG(buf); + objp->outgoingcalls = IXDR_GET_LONG(buf); + objp->successcalls = IXDR_GET_LONG(buf); + objp->failurecalls = IXDR_GET_LONG(buf); + } + return TRUE; + } + + if (!xdr_int (xdrs, &objp->incomingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->outgoingcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->successcalls)) + return FALSE; + if (!xdr_int (xdrs, &objp->failurecalls)) + return FALSE; + return TRUE; +} + +bool_t +xdr_callcounter_t (XDR *xdrs, callcounter_t *objp) +{ + register int32_t *buf; + + if (!xdr_callcounter (xdrs, objp)) + return FALSE; + return TRUE; +} diff --git a/src/sipp.cpp b/src/sipp.cpp index 545668240..ff4eaa5d7 100644 --- a/src/sipp.cpp +++ b/src/sipp.cpp @@ -58,6 +58,14 @@ extern char** environ; #include "config.h" #include "version.h" +// Added for LUA support inside plugins - otherwise the requires won't work in lua scripts - this just causes liblua.so to be part of the sipp executable so lua functions are global to any .so loaded + +#ifdef USE_LUA +#include "lua.hpp" +lua_State *L = luaL_newstate(); // This is a throwaway - we don't actually use this but it forces the lua shared lib to be loaded. +#endif + + extern SIPpSocket *ctrl_socket; extern SIPpSocket *stdin_socket; @@ -114,6 +122,7 @@ struct sipp_option { #define SIPP_OPTION_LFOVERWRITE 37 #define SIPP_OPTION_PLUGIN 38 #define SIPP_OPTION_NEED_SCTP 39 +#define SIPP_OPTION_CUSTOM 99 #define SIPP_HELP_TEXT_HEADER 255 /* Put each option, its help text, and type in this table. */ @@ -1271,6 +1280,77 @@ static void setup_media_sockets() media_port += 2; } } +#define USE_OPENSSLX /* or USE_GNUTLS accordingly */ + +/* we have this global to let the callback get easy access to it */ +static pthread_mutex_t *lockarray; + +#ifdef USE_OPENSSLX +#include +static void lock_callback(int mode, int type, char *file, int line) +{ + (void)file; + (void)line; + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(&(lockarray[type])); + } else { + pthread_mutex_unlock(&(lockarray[type])); + } +} + +static unsigned long thread_id(void) +{ + unsigned long ret; + + ret = (unsigned long)pthread_self(); + return (ret); +} + +static void init_locks(void) +{ + int i; + struct logfile_info** logfile_ptr; + struct logfile_info* logfiles[] = { + &screen_lfi, &calldebug_lfi, &message_lfi, &shortmessage_lfi, &log_lfi, &error_lfi, NULL}; + + lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * + sizeof(pthread_mutex_t)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_init(&(lockarray[i]), NULL); + } + + CRYPTO_set_id_callback((unsigned long (*)())thread_id); + CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback); + + /* Inite logfile mutexs. */ + for (logfile_ptr = logfiles; *logfile_ptr; ++logfile_ptr) { + (*logfile_ptr)->lockfile = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init((*logfile_ptr)->lockfile, NULL); + } + +} + +static void kill_locks(void) +{ + int i; + struct logfile_info** logfile_ptr; + struct logfile_info* logfiles[] = { + &screen_lfi, &calldebug_lfi, &message_lfi, &shortmessage_lfi, &log_lfi, &error_lfi, NULL}; + + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) + pthread_mutex_destroy(&(lockarray[i])); + + OPENSSL_free(lockarray); + + /* Inite logfile mutexs. */ + for (logfile_ptr = logfiles; *logfile_ptr; ++logfile_ptr) { + (*logfile_ptr)->lockfile = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_destroy((*logfile_ptr)->lockfile); + } +} +#endif + /* Main */ int main(int argc, char *argv[]) @@ -1279,6 +1359,10 @@ int main(int argc, char *argv[]) pthread_t pthread2_id = 0, pthread3_id = 0; unsigned int generic_count = 0; bool slave_masterSet = false; + void* handle = NULL; // Plugin handle + char *plugin_name = NULL; // Plugin name + int (*init)(void *, int, char **); // Plugin init function + bool plugin_enabled = false; generic[0] = NULL; @@ -1289,12 +1373,15 @@ int main(int argc, char *argv[]) } { /* Ignore the SIGPIPE signal */ + /* Moved this down so if we want to do a mysql_library_init or some other function that needs this handler it will be done later struct sigaction action_pipe; memset(&action_pipe, 0, sizeof(action_pipe)); action_pipe.sa_handler=SIG_IGN; sigaction(SIGPIPE, &action_pipe, NULL); + */ /* The Window Size change Signal is also useless, and causes failures. */ + struct sigaction action_pipe; #ifdef SIGWINCH sigaction(SIGWINCH, &action_pipe, NULL); #endif @@ -1311,6 +1398,8 @@ int main(int argc, char *argv[]) action_usr2.sa_handler = sipp_sigusr2; sigaction(SIGUSR2, &action_usr2, NULL); } + // Added pthread locks around the logs so these can be reused in a multithreaded environment in plugins etc. + init_locks(); pid = getpid(); memset(local_ip, 0, sizeof(local_ip)); @@ -1329,6 +1418,34 @@ int main(int argc, char *argv[]) ERROR("Missing argument for param '%s'.\nUse 'sipp -h' for details", argv[argi - 1]); } #define CHECK_PASS() if (option->pass != pass) { break; } + /* checking if we need to launch the tool in background mode */ + if (backgroundMode == true) { + pid_t l_pid; + switch (l_pid = fork()) { + case -1: + // error when forking ! + ERROR_NO("Forking error"); + exit(EXIT_FATAL_ERROR); + case 0: + // child process - poursuing the execution + // close all of our file descriptors + { + int nullfd = open("/dev/null", O_RDWR); + + dup2(nullfd, fileno(stdin)); + dup2(nullfd, fileno(stdout)); + dup2(nullfd, fileno(stderr)); + + close(nullfd); + } + break; + default: + // parent process - killing the parent - the child get the parent pid + printf("Background mode - PID=[%ld]\n", (long) l_pid); + exit(EXIT_OTHER); + } + } + for (int pass = 0; pass <= 3; pass++) { for(argi = 1; argi < argc; argi++) { struct sipp_option *option = find_option(argv[argi]); @@ -1337,9 +1454,13 @@ int main(int argc, char *argv[]) strncpy(remote_host, argv[argi], sizeof(remote_host) - 1); continue; } + /* Commented this so we can let the plugin handle it if there is one help(); ERROR("Invalid argument: '%s'.\n" "Use 'sipp -h' for details", argv[argi]); + */ + struct sipp_option temp = {"custom_option", "Set the value for the custom option", SIPP_OPTION_CUSTOM, NULL, 1}; + option = &temp; } switch(option->type) { @@ -1814,33 +1935,71 @@ int main(int argc, char *argv[]) REQUIRE_ARG(); CHECK_PASS(); - void* handle = dlopen(argv[argi], RTLD_NOW); + g_plugin_handle = handle = dlopen(argv[argi], RTLD_NOW); + plugin_name = argv[argi]; if (!handle) { ERROR("Could not open plugin %s: %s", argv[argi], dlerror()); } - - int (*init)(); - void* funcptr = dlsym(handle, "init"); + //void* funcptr = dlsym(handle, "init"); /* http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c */ - *reinterpret_cast(&init) = funcptr; // yuck - + //*reinterpret_cast(&init)(void *) = funcptr; // yuck + init = (int (*)(void *,int, char **))dlsym(handle, "init"); const char* error; if ((error = dlerror())) { ERROR("Could not locate init function in %s: %s", argv[argi], error); } + plugin_enabled = true; +#if 0 // Moved this to after the fork so everything will still work even if we start threads in the plugin - ret = init(); + ret = init(handle,argc,argv); if (ret != 0) { ERROR("Plugin %s initialization failed.", argv[argi]); } +#endif } break; + default: - ERROR("Internal error: I don't recognize the option type for %s", argv[argi]); - } + int ret; + if (option->pass == pass) { + if (plugin_name != NULL) { + if ((++argi) < argc) { + int (*handle_args)(char *,char *); + handle_args = (int (*)(char *, char *))dlsym(handle, "handle_args"); + const char* error; + if ((error = dlerror())) { + ERROR("Could not locate arg_handling function in %s: %s", plugin_name, error); + } + + ret = handle_args(argv[argi-1],argv[argi]); + if (ret != 0) { + ERROR("Plugin %s arg handling failed.", argv[argi]); + } + } else { + ERROR("Missing argument for param '%s'.\n", argv[argi - 1]); + } + } else { + ERROR("Internal error: I don't recognize the option type for %s", argv[argi]); + } + } else { + // Just skip it if we're not processing it + argi++; + } + break; + } } } + { + /* Ignore the SIGPIPE signal */ + /* 3/23/2022 Moved this down so if we want to do a mysql_library_init or some other function that needs this handler it will be done later */ + struct sigaction action_pipe; + memset(&action_pipe, 0, sizeof(action_pipe)); + action_pipe.sa_handler=SIG_IGN; + sigaction(SIGPIPE, &action_pipe, NULL); + } + + /* Load compression plugin if needed/available. */ if (compression) { comp_load(); @@ -2038,6 +2197,14 @@ int main(int argc, char *argv[]) exit(EXIT_OTHER); } } + // Run the plugin from in the child - did this so any threads we run will stay running after backgrounding + if (plugin_enabled) { + int ret; + ret = init(handle,argc,argv); + if (ret != 0) { + ERROR("Plugin %s initialization failed.", plugin_name); + } + } sipp_usleep(sleeptime * 1000); @@ -2118,4 +2285,21 @@ int main(int argc, char *argv[]) free(scenario_file); free(scenario_path); sipp_exit(EXIT_TEST_RES_UNKNOWN); + kill_locks(); + // Cleanup call to any plugin + if (plugin_name != NULL) { + int ret=0; + int (*app_shutdown)(void *); + app_shutdown = (int (*)(void *))dlsym(handle, "app_shutdown"); + const char* error; + if ((error = dlerror())) { + ERROR("Could not locate shutdown function in %s: %s", plugin_name, error); + } + + ret = app_shutdown(handle); + if (ret != 0) { + ERROR("Plugin %s shutdown failed.", argv[argi]); + } + } + } diff --git a/src/xp_parser.c b/src/xp_parser.c index 0c40d2315..2b91ad14c 100644 --- a/src/xp_parser.c +++ b/src/xp_parser.c @@ -48,56 +48,12 @@ #define XP_MAX_FILE_LEN 65536 #define XP_MAX_STACK_LEN 256 -static char xp_file[XP_MAX_FILE_LEN + 1]; -static char *xp_position[XP_MAX_STACK_LEN]; -static int xp_stack = 0; -static int xp_stack_invalid = 0; - -static char xp_history[XP_MAX_FILE_LEN + 1]; -static char *xp_history_pos; -#define xp_history_reset() do { \ - xp_history[0] = xp_history[1] = '\0'; \ - xp_history_pos = &xp_history[0]; \ - } while(0) -#define xp_history_push(n) do { \ - strcpy(xp_history_pos + 1, n); \ - xp_history_pos += strlen(n) + 1; \ - /*xp_history_debug();*/ \ - } while(0) -#define xp_history_pop() do { \ - while (xp_history_pos > xp_history && *--xp_history_pos != '\0'); \ - /*xp_history_debug();*/ \ - } while(0) +char xp_file [XP_MAX_FILE_LEN + 1]; +char *xp_position [XP_MAX_STACK_LEN]; +int xp_stack = 0; /****************** Internal routines ********************/ -static const char *find_first_of(const char *ptr, const char *needles, const char *end) { - while (ptr < end) { - const char *q; - for (q = needles; *q; ++q) { - if (*ptr == *q) { - return ptr; - } - } - ++ptr; - } - return NULL; -} - -static void extract_name(char *name, const char *ptr, const char **end) { - const char *p; - name[0] = '\0'; - if (!*end || *end < ptr) { - return; - } - p = find_first_of(ptr, " \t\r\n/>", *end); - if (p) { - *end = p; - } - memcpy(name, ptr, *end - ptr); - name[*end - ptr] = '\0'; -} - static const char *xp_find_escape(const char *escape, size_t len) { static struct escape { @@ -119,20 +75,6 @@ static const char *xp_find_escape(const char *escape, size_t len) return NULL; } -#if 0 -static void xp_history_debug() { - char *p = &xp_history[0]; - fprintf(stderr, "DBG:"); - for (;;) { - if (p >= xp_history_pos) - break; - fprintf(stderr, " %s", p + 1); - p += strlen(p + 1) + 1; - } - fprintf(stderr, "\n"); -} -#endif - /* This finds the end of something like , and does not recurse * into other elements. */ static char *xp_find_start_tag_end(char *ptr) @@ -280,8 +222,7 @@ int xp_set_xml_buffer_from_string(const char *str) } strcpy(xp_file, str); - xp_stack = xp_stack_invalid = 0; - xp_history_reset(); + xp_stack = 0; xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], "= XP_MAX_FILE_LEN) { xp_file[index++] = 0; - xp_stack = xp_stack_invalid = 0; - xp_history_reset(); + xp_stack = 0; xp_position[xp_stack] = xp_file; fclose(f); return 0; @@ -320,8 +260,7 @@ int xp_set_xml_buffer_from_file(const char *filename) xp_file[index++] = 0; fclose(f); - xp_stack = xp_stack_invalid = 0; - xp_history_reset(); + xp_stack = 0; xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], " 0) { - xp_history_pop(); - } - while (*ptr) { if (*ptr == '<') { if ((*(ptr+1) == '!') && @@ -365,86 +299,74 @@ char *xp_open_element(int index) if (!doctype_end) return NULL; ptr = doctype_end; - } else if (strstartswith(ptr, ""); - if (!xmlmodel_end) - return NULL; - ptr = xmlmodel_end; } else if (*(ptr+1) == '/') { - char *end = xp_find_start_tag_end(ptr + 2); - if (!end) { - return NULL; - } - extract_name(name, ptr + 2, (const char**)&end); - level--; if (level < 0) return NULL; - - xp_history_pop(); - if (strcmp(xp_history_pos + 1, name) && !xp_stack_invalid) { - xp_stack_invalid = 1; - fprintf(stderr, "Unexpected (expected )\n", - name, xp_history_pos + 1); - } } else { - char *end = xp_find_start_tag_end(ptr + 1); - if (!end) { - return NULL; - } - extract_name(name, ptr + 1, (const char**)&end); - xp_history_push(name); - if (level == 0) { - if (index_left) { - index_left--; + if (index) { + index--; } else { + char *end = xp_find_start_tag_end(ptr + 1); + char *p; + if (!end) { + return NULL; + } + p = strchr(ptr, ' '); + if (p && (p < end)) { + end = p; + } + p = strchr(ptr, '\t'); + if (p && (p < end)) { + end = p; + } + p = strchr(ptr, '\r'); + if (p && (p < end)) { + end = p; + } + p = strchr(ptr, '\n'); + if (p && (p < end)) { + end = p; + } + p = strchr(ptr, '/'); + if (p && (p < end)) { + end = p; + } + + memcpy(name, ptr + 1, end-ptr-1); + name[end-ptr-1] = 0; + xp_position[++xp_stack] = end; return name; } } /* We want to skip over this particular element .*/ - ptr = end - 1; + ptr = xp_find_start_tag_end(ptr + 1); + if (!ptr) + return NULL; + ptr--; level++; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { level--; if (level < 0) return NULL; - xp_history_pop(); } ptr++; } return NULL; } -void xp_close_element() -{ - if (!xp_stack) { - xp_stack_invalid = 1; - return; - } - xp_stack--; -} - -int xp_is_invalid(void) +void xp_close_element(void) { - const char *elem; - if (xp_stack_invalid) { - return 1; - } if (xp_stack) { - return 1; - } - if ((elem = xp_open_element(1))) { /* anything after ? */ - xp_close_element(); - return 1; + xp_stack--; } - return 0; } -const char *xp_get_value(const char *name) +char *xp_get_value(const char *name) { int index = 0; static char buffer[XP_MAX_FILE_LEN + 1];