sipp-3.7.2/0000775000000000000000000000000014525516253007376 5ustar sipp-3.7.2/.github/0000775000000000000000000000000014525516253010736 5ustar sipp-3.7.2/.github/workflows/0000775000000000000000000000000014525516253012773 5ustar sipp-3.7.2/.github/workflows/ccpp.yml0000664000000000000000000000241014525516253014440 0ustar name: C/C++ CI on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: lint run: ./validate-src.sh - name: lint-xml run: sudo apt-get update && sudo apt-get install --yes libxml2-utils && ./dtd_check.sh build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: prepare run: git submodule update --init - name: installdeps run: sudo apt-get update && sudo apt-get install --yes build-essential cmake libpcap-dev openssl libssl-dev libgsl-dev libsctp-dev libncurses5-dev libncurses5 - name: build run: ./build.sh --full - name: test run: TEST_SKIP_VALGRIND=1 ./regress/runtests build-osx: runs-on: macOS-latest steps: - uses: actions/checkout@v1 - name: prepare run: git submodule update --init - name: build run: ./build.sh --none build-static: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: prepare run: git submodule update --init - name: build-static run: sudo docker build -t sipp-build docker && sudo docker run -v $PWD:/src sipp-build - uses: actions/upload-artifact@v1.0.0 with: name: sipp path: ./sipp sipp-3.7.2/.gitignore0000664000000000000000000000113114525516253011362 0ustar *.o *~ .*.sw* .autoclean .deps/ .version Makefile Makefile.in Makefile.am aclocal.m4 autom4te.cache/ configure config.guess config.h config.log config.status config.sub compile depcomp .dirstamp install-sh missing sipp sipp.1 sipp-*.tar.gz sipp_unittest stamp-h1 test-driver *.log *.csv vim_ctags compile_commands.json .*.sw? stamp-h.in *.orig *.rej *.patch compile_commands.json CMakeCache.txt CMakeFiles/ cmake_install.cmake # Ignore *.xml in root dir, but not in regression tests dir. /*.xml # Sphinx build /docs/_build *.pyc # Ignore autogenerated version.h. /include/version.cmake /version.h sipp-3.7.2/.gitmodules0000664000000000000000000000013614525516253011553 0ustar [submodule "gtest"] path = gtest url = https://github.com/google/googletest ignore = dirty sipp-3.7.2/CHANGES.md0000664000000000000000000003006614525516253010775 0ustar Bugs fixed in 3.7.2 =================== - Remove excessive log Bugs fixed in 3.7.1 ======================= - Correctly open the control socket - The SIPp binary can now be built even when the `gtest` checkout is missing - rtpstream files are now also found next to the scenario. If it is not found there, it will be treated as a relative path as usual. Features added in 3.7.0 =========================== - RTPstream can now handle .wav files with a WAV header (by Orgad Shaneh) Bugs fixed in 3.7.0 ======================= - RTPCHECK stability fixes (by Jeannot Langlois) - Support CRLF-format injection files (by Orgad Shaneh) - Fix to [next_url] when a display name is present in the contact (by enneig) - Add 'transport' to the Contact header for UAC scenarios (by Martin Flaska) - Update built-in scenarios to Copy Record-Route from INVITE to 200OK to comply with RFC 3261 (by kadabusha) - Fix for local_port keyword using TCP or TLS (by Felippe Silvestre) - Correct handling of IMS-AKA RES values contianing null bytes (by Sergey Zyrianov) - Fix potential overwrite of auth value when calculating auth (by ZhaohuiLiu) - Diagnostics improvements: - Print, rather than lose, any buffered response time data on exit (by Orgad Shaneh) - Add the IPs and remote address family to 'Network family mismatch' log (by Rob Day) - Print OpenSSL error reason when certificate load fails (by Rajesh Singh) - Give clear error if multiple command-line parameters are being interpreted as remote_host - Prevent clock_tick moving backwards (and getting behind wheel_base and causing an assert) (by Rob Day) - Ensure that sockets are marked as non-blocking before OpenSSL calls are made (by Rob Day) - Prevent RTPStream crash due to a thread ID of 0 (by Rob Day) - Cygwin, FreeBSD and Hurd build fixes (by Orgad Shaneh, kadabusha and Zopolis4) - Static build fixes (by Aaron Meriwether) Features added in 3.7.0~rc1 =========================== * B2BUA Media Gateway RTP/SRTP bit pattern testing -- see `docs/rtpcheck_xml_syntax_reference.pdf`. Command line examples: ``` # UAS (RTP) ./sipp -m 1 -sf sipp_scenarios/pfca_uas.xml \ -i 127.0.0.3 -t u1 -p 5060 -rtp_echo # UAC (RTP) ./sipp -m 1 -sf sipp_scenarios/pfca_uac_apattern.xml \ -t u1 -i 127.0.0.2 -p 5060 127.0.0.3:5060 ``` ``` # UAS (audio SRTP) ./sipp -m 1 -sf sipp_scenarios/pfca_uas_audio_crypto_simple.xml \ -t u1 -i 127.0.0.3 -p 5060 -srtpcheck_debug # UAC (audio SRTP) ./sipp -m 1 -sf sipp_scenarios/pfca_uac_apattern_crypto_simple.xml \ -t u1 -i 127.0.0.2 -p 5060 -rtpcheck_debug -srtpcheck_debug \ 127.0.0.3:5060 ``` By Jeannot Langlois. * Removed `-mp` in favor of `-min_rtp_port` and `-max_rtp_port`. Also removed `[auto_media_port]`. There are way too many (conflicting) options to specify ports here. * URL encode/decode `` for scenarios (by Jérôme Poulin). * Variables in the rtpstream/pcap filenames (by Orgad Shaneh). * WolfSSL/WolfCrypt library support (as alternative to OpenSSL, by Thomas Uhle). Bugs fixed in 3.7.0~rc1 ======================= * Documentation updates. Code cleanups. Build fixes. (By Walter Doekes, Thomas Uhle, ChanderG, Lin Sun, Markus Goetzl, Rob Day, Stefan Mititelu, Orgad Shaneh, Karn Saheb). * Fix socket/tcp refcount/order issue (by Orgad Shaneh). * Fix timezone in [date] on FreeBSD (by kadabusha). * Track auto-answered messages as a visible counter rather than an error log (by Rob Day). * Unconditionally show index in scenario screen (by Rob Day). Bugs fixed in 3.6.2 =================== * Fix crash when abusing authentication method (#503, by Markus). * Fix crash when trying to change an unset ooc scenario (#463, by @jquinn60137). * Fix various build issues with CMake and/or missing version.h and/or compiler warnings. By Walter Doekes, by Silver Chan, Thomas Uhle, Orgad Shaneh. * Remove RTP\_STREAM define. The code is always included. (By Orgad Shaneh.) * Various minor documentation fixes. By Walter Doekes, kadabusha, Thomas Uhle, Alexander Traud. BREAKING(!) changes in 3.6.1 ============================ * CMake is now used as build environment: autoconf and friends are gone (#430, by Rob Day (@rkday)). See `build.sh` for CMake invocations. For a full build, do: ``` cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL=1 -DUSE_SCTP=1 make -j4 ``` Bugs fixed in 3.6.1 =================== * Consistently unescape XML attributes when loading scenario (#458, by Steve Frécinaux (@nud)). * Fix buffer overflow in screen output (#479, reported by @brettowe). * Fix nonce count in auth headers (#421, by Cody Herzog (@codyherzog)). * Fix parser warning when trying to access 0-byte SDP body (by Lin Sun (@sunlin7)). * Fix pcapplay on FreeBSD (#434, by Rob Day (@rkday)). * Improve build validation (#424, by Stanislav Litvinenko (@dolk13)), a few compiler fixes, a few ncurses fixes (including #436, reported by @TamerL), build cleanup after CMake (#443, #442, by Orgad Shaneh (@orgads)) and libtinfo linker issues (Jeannot Langlois (@jeannotlanglois)). * Improve provided sipp.dtd file (#425, by David M. Lee (@leedm777)), and XML fixes by Rob Day. * Make it easier to deal with large SIP packets by adding an optional `-DSIPP_MAX_MSG_SIZE=262144` to the `cmake` command (#422, by Cody Herzog (@codyherzog)). BREAKING(!) changes in 3.6.0 ============================ * Automatic filenames (trace files, error files, etc..) are now created in the current working directory instead of in the directory of the scenario file. (Issue #399, reported by @sergey-safarov.) * Only validates SSL certficate if CA-file is separately specified! (PR #335, by Patrick Wildt @bluerise.) * Angle brackets `<` and `>` need to be escaped inside XML attributes. See #414. So, not `regexp="<(sip:.*)>"` but `regexp="<(sip:.*)>"`. Bugs fixed in 3.6.0 =================== * Fix `[routes]` header in UAS scenario's. (Issue #262, reported by Stefan Mititelu (@smititelu).) * last\_Keyword does not search in SIP body anymore (#207, reported by Zoltan). Changes in 3.6.0 ================ * Added PAGER by default to the extremely large sipp help output. * Removed unused RTPStream code concerning video streams. Also consolidated the rtpstream audio port usage to reuse the global `[media_port]` instead of the `[rtpstream_audio_port]`. Also the `-min_rtp_port` and `-max_rtp_port` options have been removed. Advantages: cleaner code, fewer scenario variables. Drawbacks: possible ICMP port unreachable messages for RCTP and video. Also, no easy way to discern different streams if you want to bombard a single UAS with multiple RTP streams. (Issue #192, reported by @atsakiridis.) Features added in 3.6.0 ======================= * Add `play_dtmf` code originally from https://sourceforge.net/p/sipp/patches/50/ (Dmitry Kunilov), then pull #82 (@horacimacias) and then #141 (@vodik). Compile with pcap-play support, and use it by adding `` similar to how you use `play_pcap_audio`. - Add RTP payload 96 in your SDP: m=audio [media_port] RTP/AVP 0 96 97 a=rtpmap:0 PCMU/8000 a=rtpmap:96 telephone-event/8000 a=fmtp:96 0-15 a=rtpmap:97 no-op/8000 - Exec syntax is `` where digits can be one or more of "0123456789#*ABCD" and length defaults to 200 and must be between 50 and 2000. - Instead of digits a `[field...]` keyword is also accepted. - Make sure you add enough `` after `play_dtmf`. * Add `rtp_echo` action (pull #259 by Snom Technology). Compile with `--with-rtpstream` and use it by adding `` to stop the RTP echo enabled via `-rtp_echo`. RTP echo can be restarted via `` action. Usage example in `regress/github-#0259/uas.xml` * Added the required constants for G722 (payload 9) and iLBC at 30ms per frame to rtp\_stream media actions. (PR #366, by Jasper Hafkenscheid @hafkensite.) * Add quick and dirty detection of invalid XML (issue #322). * Clarify that `-infindex` should takes a basename only (issue #395, reported by @sergey-safarov). Bugs fixed in 3.5.3 =================== * Fix `[routes]` header in UAS scenario's. (Issue #262, reported by Stefan Mititelu (@smititelu).) (Backported from b6c7b209 from 3.6.) * Fix bad Content-Length calculation when whitespace was between the CRLF pairs that separate the body. (Issue #337, fixed by Serg Stetsuk (@sergstetsuk)). * Fix crash in pcap play on send failure because of pthread\_cleanup macros. (Issue #74, #370, reported by various people.) Bugs fixed in 3.5.2 =================== Build issues: * Improve ncurses/gsl detection and linkage on various platforms. (Issue #205, #271, #275, reported by Paul Malpass, AlexB, Leon Roy, Victor Seva.) * Fix compile issues on old CentOS and Solaris. (Issue #211, #245, #252, reported by sjthomason, mscdex.) * Fix newer openssl detection. (Issue #302, #304, #315, #328.) * Recompile entire source after a reconfigure. * Reduce confusion when someone downloads a tag from git instead of the tar.gz with the autogenerated files and valid version.h (#270, reported by AlexB). * Remove hardcoded build datetime from binary, for "reproducible builds". (#286, by Victor Seva.) * Replace underscore in tag-name with tilde, for debian-style "~rc1" version suffix. Bugs: * Handle Contact header with extra angle brackets ('<...>') outside of the uri-parameters (in the contact-params). The contact params should not be used in the `next_url`. (Issue #234, reported by Justin Zimmer.) * Fix TLS issues for during high load. (Issue #241, #243, reported by sgel83, and fixes by Rob Day.) * Fix problem with `get_inet_address` on FreeBSD (#331, reported by tsgan.) * Retry video RTP bind if port is taken (#276, thanks Corey Farrell). Tests: * Document `search_in="hdr"` test. * Also test without `HAVE_EPOLL`. Bugs fixed in 3.5.1 =================== * Fix qop-value in authorization Digest. It can only hold a single value (auth, auth-int, ...) and does not take double quotes, in contrast to the challenge. Some servers returned a 400 upon receiving this. (Issue #191, reported by @artlov.) * Fix compile error on Cygwin. (Issue #193, reported by @Gankarloo.) Features added in 3.5.0 ======================= * Clean up source code, fix typo's, alter warning and error messages, fix pedantic coding style. Add gtest framework and tests. Add regression tests. Fix and improve build scripts (see also: `build.sh`). * Use better timing with `clock_gettime` (or `clock_get_time` on OSX). * Don't complain about the dummy variable `_` being used only once. * Ignore 4x NUL keepalive (next to ignoring the CRLF CRLF keepalive). * Add `[date]` keyword. * Add `-trace_screen` option to log screen output in a file. * Add `-rate_increase` option to increase load periodically. * Add `-callid_slash_ign` to disable the magic triple slash behaviour.a * Alter `-aa` to also reply to OPTIONS. * Allow replaying pcaps with `LINUX_SLL`, `EN10MB` and 802.11 (and ratiotap) link layer types. Handle 802.1Q tagged frames. * Allow starting SIPp without a TERM setting (a "working" terminal). * Allow m=image in SDP to pcapplay faxes/images. * `` and friends: - If the argument is not an absolute path, the pcap is searched next to the scenario, before falling back to checking the current working directory. - The argument may be enclosed in brackets, in which case it is interpreted as a keyword value; set through the `-key` command line option. Example: `` with option `-key file1 /path/to/pcap`. Bugs fixed in 3.5.0 =================== * Start SDP search in body instead of in header. Fix IPv6 media address in SDP. * Allow single CR and single LF in SDP. * Don't confuse cnonce with nonce, improve other auth parsing. * Don't abort SIPp if the To header is missing. * Fixes to XML parser; improve `get_peer_tag` behaviour. * Fix jump recursion crashes. * Fix a few (potential) memory leaks and dangerous code. * Remove a few autogenerated files from tree (configure, manpage). * Fix digest calculation when `qop` is given. 3.4.1 ===== * Not documented here. sipp-3.7.2/CMakeLists.txt0000664000000000000000000002002214525516253012132 0ustar cmake_minimum_required(VERSION 3.4) # set the project name project(SIPp) # specify the C++ standard set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_EXTENSIONS False) # specify the C++ standard on older CMake (<3.8) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -pedantic -Wno-error=format-truncation -Wno-error=deprecated-declarations") # Include binary dir first, where we add generated config.h and # version.h. If nothing is found there (release tar) use the version.h # from the source. include_directories( ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/include "/usr/local/include") option(BUILD_STATIC "Build a statically-linked binary" OFF) 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) file(GLOB all_SRCS "${PROJECT_SOURCE_DIR}/src/*.cpp" "${PROJECT_SOURCE_DIR}/src/*.c" ) include(${CMAKE_ROOT}/Modules/CheckCXXSourceCompiles.cmake) include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) include(${CMAKE_ROOT}/Modules/CheckSymbolExists.cmake) include(${CMAKE_ROOT}/Modules/CheckStructHasMember.cmake) CHECK_INCLUDE_FILE("endian.h" HAVE_ENDIAN_H) CHECK_INCLUDE_FILE("sys/endian.h" HAVE_SYS_ENDIAN_H) CHECK_INCLUDE_FILE("sys/epoll.h" HAVE_EPOLL) CHECK_STRUCT_HAS_MEMBER("struct udphdr" uh_sport "sys/types.h;netinet/udp.h" HAVE_UDP_UH_PREFIX) CHECK_SYMBOL_EXISTS(le16toh "endian.h" HAVE_DECL_LE16TOH) CHECK_SYMBOL_EXISTS(le16toh "sys/endian.h" HAVE_DECL_LE16TOH_BSD) configure_file("${PROJECT_SOURCE_DIR}/include/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ) if(BUILD_STATIC) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static") set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) endif(BUILD_STATIC) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp") list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sipp.cpp") if(NOT USE_SSL) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sslsocket.cpp") endif(NOT USE_SSL) if(NOT USE_PCAP) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/prepare_pcap.c") list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/send_packets.c") endif(NOT USE_PCAP) find_package(PkgConfig QUIET) # import pkg_check_modules() and friends if(USE_SSL) if(PKG_CONFIG_FOUND) pkg_search_module(SSL openssl>=0.9.8 wolfssl>=3.15.0) endif() if(SSL_FOUND) if("${SSL_LIBRARIES}" MATCHES "wolfssl") set(WOLFSSL_FOUND True) else() set(OPENSSL_FOUND True) endif() else() find_library(OPENSSL_SSL_LIBRARY NAMES ssl) find_library(OPENSSL_CRYPTO_LIBRARY NAMES crypto) if(OPENSSL_SSL_LIBRARY AND OPENSSL_CRYPTO_LIBRARY) set(SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) set(OPENSSL_FOUND True) else() find_library(WOLFSSL_LIBRARY NAMES wolfssl) if(WOLFSSL_LIBRARY) set(SSL_LIBRARIES ${WOLFSSL_LIBRARY}) set(WOLFSSL_FOUND True) endif() endif() if(NOT OPENSSL_FOUND AND NOT WOLFSSL_FOUND) message(FATAL_ERROR "Neither OpenSSL nor WolfSSL was found; please install a devel package") endif() endif() if(OPENSSL_FOUND) add_definitions("-DUSE_TLS" "-DUSE_OPENSSL") elseif(WOLFSSL_FOUND) add_definitions("-DUSE_TLS" "-DUSE_WOLFSSL" "-DOPENSSL_ALL") endif() endif() if(USE_PCAP) add_definitions("-DPCAPPLAY") endif(USE_PCAP) if(USE_GSL) find_library(GSL_LIBRARY gsl) if(GSL_LIBRARY) add_definitions("-DHAVE_GSL") endif(GSL_LIBRARY) endif(USE_GSL) if(USE_SCTP) add_definitions("-DUSE_SCTP") endif(USE_SCTP) # add the executable link_directories("/usr/local/lib") add_executable(sipp ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp.cpp") target_compile_features(sipp PUBLIC cxx_auto_type cxx_range_for) if(EXISTS ${PROJECT_SOURCE_DIR}/gtest/googletest) add_executable(sipp_unittest EXCLUDE_FROM_ALL ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp" "${PROJECT_SOURCE_DIR}/gtest/googletest/src/gtest-all.cc" "${PROJECT_SOURCE_DIR}/gtest/googlemock/src/gmock-all.cc" ) target_compile_features(sipp_unittest PUBLIC cxx_auto_type cxx_range_for) target_compile_definitions(sipp_unittest PUBLIC "-DGTEST") target_include_directories(sipp_unittest SYSTEM PUBLIC ${PROJECT_SOURCE_DIR}/gtest/googletest ${PROJECT_SOURCE_DIR}/gtest/googlemock ${PROJECT_SOURCE_DIR}/gtest/googletest/include ${PROJECT_SOURCE_DIR}/gtest/googlemock/include ) else() add_executable(sipp_unittest EXCLUDE_FROM_ALL ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp") endif() # add version find_package(Git) file(WRITE ${PROJECT_SOURCE_DIR}/include/version.cmake "execute_process( COMMAND ${GIT_EXECUTABLE} describe --tags --always --first-parent WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) configure_file(\${SRC} \${DST} @ONLY) ") if(EXISTS ${PROJECT_SOURCE_DIR}/.git) add_custom_target( version ${CMAKE_COMMAND} -D SRC=${PROJECT_SOURCE_DIR}/include/version.h.in -D DST=${PROJECT_BINARY_DIR}/version.h -P ${PROJECT_SOURCE_DIR}/include/version.cmake ) add_dependencies(sipp version) add_dependencies(sipp_unittest version) endif() if(PKG_CONFIG_FOUND) pkg_search_module(CURSES_LIBRARY ncursesw cursesw ncurses curses) if(CURSES_LIBRARY_FOUND) set(CURSES_LIBRARY ${CURSES_LIBRARY_LIBRARIES}) endif() pkg_search_module(TINFO_LIBRARY tinfo) if(TINFO_LIBRARY_FOUND) set(TINFO_LIBRARY ${TINFO_LIBRARY_LIBRARIES}) endif() endif() if(NOT CURSES_LIBRARY) find_library(CURSES_LIBRARY NAMES ncursesw cursesw ncurses curses) endif() if(NOT TINFO_LIBRARY) find_library(TINFO_LIBRARY NAMES tinfo) endif() if(CURSES_LIBRARY) target_link_libraries(sipp dl ${CURSES_LIBRARY} pthread) target_link_libraries(sipp_unittest dl ${CURSES_LIBRARY} pthread) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(TINFO_LIBRARY) target_link_libraries(sipp ${TINFO_LIBRARY}) target_link_libraries(sipp_unittest ${TINFO_LIBRARY}) else() if(BUILD_STATIC) message(WARNING "libtinfo.a was not found -- please install package if linking fails") else() message(FATAL_ERROR "libtinfo.so was not found -- please install package") endif(BUILD_STATIC) endif(TINFO_LIBRARY) endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CURSES_LIBRARY_INCLUDE_DIRS OR TINFO_LIBRARY_INCLUDE_DIRS) target_include_directories(sipp SYSTEM PUBLIC ${CURSES_LIBRARY_INCLUDE_DIRS} ${TINFO_LIBRARY_INCLUDE_DIRS}) target_include_directories(sipp_unittest SYSTEM PUBLIC ${CURSES_LIBRARY_INCLUDE_DIRS} ${TINFO_LIBRARY_INCLUDE_DIRS}) endif() else() message(FATAL_ERROR "libcurses / libncurses was not found; please install devel package") endif() find_library(RT_LIBRARY NAMES rt) if(RT_LIBRARY) target_link_libraries(sipp ${RT_LIBRARY}) target_link_libraries(sipp_unittest ${RT_LIBRARY}) endif() if(DEBUG) add_definitions("-g -O0") endif(DEBUG) if(SIPP_MAX_MSG_SIZE) add_definitions("-DSIPP_MAX_MSG_SIZE=${SIPP_MAX_MSG_SIZE}") endif(SIPP_MAX_MSG_SIZE) if(CYGWIN) add_definitions("-D_GNU_SOURCE") endif(CYGWIN) if(USE_GSL AND GSL_LIBRARY) target_link_libraries(sipp gsl gslcblas) target_link_libraries(sipp_unittest gsl gslcblas) endif(USE_GSL AND GSL_LIBRARY) if(USE_SSL AND SSL_LIBRARIES) target_link_libraries(sipp ${SSL_LIBRARIES}) target_link_libraries(sipp_unittest ${SSL_LIBRARIES}) if(SSL_INCLUDE_DIRS) target_include_directories(sipp SYSTEM PUBLIC ${SSL_INCLUDE_DIRS}) target_include_directories(sipp_unittest SYSTEM PUBLIC ${SSL_INCLUDE_DIRS}) endif(SSL_INCLUDE_DIRS) endif(USE_SSL AND SSL_LIBRARIES) if(USE_PCAP) target_link_libraries(sipp pcap) target_link_libraries(sipp_unittest pcap) endif(USE_PCAP) if(USE_SCTP) find_library(SCTP_LIBRARY sctp) if(SCTP_LIBRARY) target_link_libraries(sipp ${SCTP_LIBRARY}) target_link_libraries(sipp_unittest ${SCTP_LIBRARY}) endif() endif() install(TARGETS sipp DESTINATION bin) sipp-3.7.2/LICENSE.txt0000664000000000000000000004326214525516253011230 0ustar SIPp as a whole is licensed under the GNU General Public License, version 2 or any later version (copy below). send_packets.c, send_packets.h, md5.c and md5.h have separate copyright licenses, which apply only to those files. The following license applies to send_packets.c and send_packets.h only: * Copyright (c) 2001-2004 Aaron Turner. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright owners nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following license applies to md5.c and md5.h only: /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ All other parts of SIPp are under the following license (or, at your option, any later version of the GNU GPL): GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS sipp-3.7.2/README.md0000664000000000000000000001051314525516253010655 0ustar Travis Build Status Coverity Scan Build Status SIPp - a SIP protocol test tool Copyright (C) 2003-2020 - The Authors 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 3 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, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # Documentation See the `docs/` directory. It should also be available in html format at: https://sipp.readthedocs.io/en/latest/ Build a local copy using: ``sphinx-build docs _build`` # Building This is the SIPp package. Please refer to the [webpage](http://sipp.sourceforge.net/) for details and documentation. Normally, you should be able to build SIPp by using CMake: ``` cmake . make ``` _The SIPp master branch (3.7.x) requires a modern C++11 compiler._ There are several optional flags to enable features (SIP-over-TLS, SIP-over-SCTP, media playback from PCAP files and the GNU Scientific Libraries for random distributions): ``` cmake . -DUSE_SSL=1 -DUSE_SCTP=1 -DUSE_PCAP=1 -DUSE_GSL=1 ``` ## Static builds SIPp can be built into a single static binary, removing the need for libraries to exist on the target system and maximising portability. This is a [fairly complicated process](https://medium.com/@neunhoef/static-binaries-for-a-c-application-f7c76f8041cf), and for now, it only works on Alpine Linux. To build a static binary, pass `-DBUILD_STATIC=1` to cmake. Two Alpine-based `Dockerfile`s are provided, which can be used as a build-environment. Use either `Dockerfile` or `Dockerfile.full` in the following commands: ``` git submodule update --init docker build -t sipp-build -f docker/Dockerfile docker docker run --rm -v $PWD:/src sipp-build ``` # Support I try and be responsive to issues raised on Github, and there's [a reasonably active mailing list](https://lists.sourceforge.net/lists/listinfo/sipp-users). # Making a release * Update CHANGES.md. Tag release. Do a build. * Make `sipp.1` by calling: ``` help2man --output=sipp.1 -v -v --no-info \ --name='SIP testing tool and traffic generator' ./sipp ``` * Then: ``` mkdir sipp-$VERSION git ls-files -z | tar -c --null \ --exclude=gmock --exclude=gtest --files-from=- | tar -xC sipp-$VERSION cp sipp.1 sipp-$VERSION/ # check version, and do cp ${PROJECT_BINARY_DIR:-.}/version.h sipp-$VERSION/include/ tar --sort=name --mtime="@$(git log -1 --format=%ct)" \ --owner=0 --group=0 --numeric-owner \ -czf sipp-$VERSION.tar.gz sipp-$VERSION ``` * Upload to github as "binary". Note that github replaces tilde sign (for ~rcX) with a period. * Create a static binary and upload this to github as well: ``` sudo docker build -t sipp-build docker && sudo docker run -it -v $PWD:/src sipp-build ``` * Note that the static build is broken at the moment. See `ldd sipp`. # Contributing SIPp is free software, under the terms of the GPL licence (see the LICENCE.txt file for details). You can contribute to the development of SIPp and use the standard Github fork/pull request method to integrate your changes integrate your changes. If you make changes in SIPp, *PLEASE* follow a few coding rules: - Please stay conformant with the current indentation style (4 spaces indent, standard Emacs-like indentation). Examples: ``` if (condition) { /* "{" even if only one instruction */ f(); /* 4 space indents */ } else { char* p = ptr; /* C++-style pointer declaration placement */ g(p); } ``` - If possible, check that your changes can be compiled on: - Linux, - Cygwin, - Mac OS X, - FreeBSD. Thanks, Rob Day sipp-3.7.2/THANKS0000664000000000000000000000173414525516253010316 0ustar The following people have contributed code or other elements to SIPp: Richard Gayraud Marc Lamberton Olivier Jacques Herve Pellan David Mansutti Francois-Xavier Kowalski Gerard Lyonnaz Francois Draperi F. Tarek Rogers Peter Higginson Vincent Luba Shriram Natarajan Guillaume Teissier Clement Chen Wolfgang Beck Charles P Wright Martin Van Leeuwen Andy Aicken Michael Hirschbichler Rob Day Dmitry Semyonov Jordan Walbesser Ken Crowell Peter Lemenkov Menyus Hegedűs Mitko Mitev Matt Williams Richard Brady Natanael Copa Walter Doekes (OSSO B.V.) Simon Gomizelj (Sangoma) Peter Wu Ben Langfeld Richard van den Berg Mustafa Kocaturk Konstantin S. Vishnivetsky SIPp also uses code originally by Aaron Turner (send_packets.c) and L Peter Deutsch (md5.c). The following people have contributed to SIPp in other useful ways (bug reports, testing, etc.): Paul Belanger Jan Stanek Enrico Hartung Daniel Swärd Paul D Smith Ferenc Wágner Carlo R. Carrano Kayode Olajide Daniel Goepp Vijay Goje sipp-3.7.2/build.sh0000775000000000000000000000211114525516253011027 0ustar #!/bin/sh set -e # abort on error MAKE=`which gmake make 2>/dev/null | head -n1` # prefer GNU make test -z "$MAKE" && echo "No (g)make found" >&2 && exit 1 CPUCOUNT=$(nproc --all 2>/dev/null || echo 1) MAKEFLAGS="-j$CPUCOUNT" if test -z "$*"; then echo "build.sh: Please specify configure options," \ "--none for defaults, or --full for all" >&2 exit 1 elif test "$*" = "--help" || test "$*" = "-h"; then echo "build.sh: Please specify configure options," \ "--none for defaults, or --full for all" >&2 exit 1 fi if test "$*" = "--none"; then cmake . -DUSE_GSL= elif test "$*" = "--common"; then cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL= -DUSE_SCTP= elif test "$*" = "--full"; then cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL=1 -DUSE_SCTP=1 else # Debug build? Add -DDEBUG=1 # Adjusted SIP max size? Add -DSIPP_MAX_MSG_SIZE=262144 cmake . "$@" fi # For git checkout, run unit tests. if test -e gtest/.git; then "$MAKE" $MAKEFLAGS sipp_unittest ./sipp_unittest fi # You want verbose or NOISY_BUILD? Use VERBOSE=1 "$MAKE" $MAKEFLAGS sipp-3.7.2/cpplint.py0000664000000000000000000046622114525516253011434 0ustar #!/usr/bin/python # # Copyright (c) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Here are some issues that I've had people identify in my code during reviews, # that I think are possible to flag automatically in a lint tool. If these were # caught by lint, it would save time both for myself and that of my reviewers. # Most likely, some of these are beyond the scope of the current lint framework, # but I think it is valuable to retain these wish-list items even if they cannot # be immediately implemented. # # Suggestions # ----------- # - Check for no 'explicit' for multi-arg ctor # - Check for boolean assign RHS in parens # - Check for ctor initializer-list colon position and spacing # - Check that if there's a ctor, there should be a dtor # - Check accessors that return non-pointer member variables are # declared const # - Check accessors that return non-const pointer member vars are # *not* declared const # - Check for using public includes for testing # - Check for spaces between brackets in one-line inline method # - Check for no assert() # - Check for spaces surrounding operators # - Check for 0 in pointer context (should be NULL) # - Check for 0 in char context (should be '\0') # - Check for camel-case method name conventions for methods # that are not simple inline getters and setters # - Do not indent namespace contents # - Avoid inlining non-trivial constructors in header files # - Check for old-school (void) cast for call-sites of functions # ignored return value # - Check gUnit usage of anonymous namespace # - Check for class declaration order (typedefs, consts, enums, # ctor(s?), dtor, friend declarations, methods, member vars) # """Does google-lint on c++ files. The goal of this script is to identify places in the code that *may* be in non-compliance with google style. It does not attempt to fix up these problems -- the point is to educate. It does also not attempt to find all problems, or to ensure that everything it does find is legitimately a problem. In particular, we can get very confused by /* and // inside strings! We do a small hack, which is to ignore //'s with "'s after them on the same line, but it is far from perfect (in either direction). """ import codecs import copy import getopt import math # for log import os import re import sre_compile import string import sys import unicodedata _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [file] ... The style guidelines this tries to follow are those in http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. This will miss some errors, and is not a substitute for a code review. To suppress false-positive errors of a certain category, add a 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) suppresses errors of all categories on that line. The files passed in will be linted; at least one file must be provided. Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. Flags: output=vs7 By default, the output is formatted to ease emacs parsing. Visual Studio compatible output (vs7) may also be used. Other formats are unsupported. verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. filter=-x,+y,... Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed. (Category names are printed with the message and look like "[whitespace/indent]".) Filters are evaluated left to right. "-FOO" and "FOO" means "do not print categories that start with FOO". "+FOO" means "do print categories that start with FOO". Examples: --filter=-whitespace,+whitespace/braces --filter=whitespace,runtime/printf,+runtime/printf_format --filter=-,+build/include_what_you_use To see a list of all the categories used in cpplint, pass no arg: --filter= counting=total|toplevel|detailed The total number of errors found is always printed. If 'toplevel' is provided, then the count of errors in each of the top-level categories like 'build' and 'whitespace' will also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. root=subdir The root directory used for deriving header guard CPP variable. By default, the header guard CPP variable is calculated as the relative path to the directory that contains .git, .hg, or .svn. When this flag is specified, the relative path is calculated from the specified directory. If the specified directory does not exist, this flag is ignored. Examples: Assuing that src/.git exists, the header guard CPP variables for src/chrome/browser/ui/browser.h are: No flag => CHROME_BROWSER_UI_BROWSER_H_ --root=chrome => BROWSER_UI_BROWSER_H_ --root=chrome/browser => UI_BROWSER_H_ """ # We categorize each error message we print. Here are the categories. # We want an explicit list so we can list them all in cpplint --filter=. # If you add a new error message with a new category, add it to the list # here! cpplint_unittest.py should tell you if you forget to do this. # \ used for clearer layout -- pylint: disable-msg=C6013 _ERROR_CATEGORIES = [ 'build/class', 'build/deprecated', 'build/endif_comment', 'build/explicit_make_pair', 'build/forward_decl', 'build/header_guard', 'build/include', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', 'build/namespaces', 'build/printf_format', 'build/storage_class', 'legal/copyright', 'readability/alt_tokens', 'readability/braces', 'readability/casting', 'readability/check', 'readability/constructors', 'readability/fn_size', 'readability/function', 'readability/multiline_comment', 'readability/multiline_string', 'readability/namespace', 'readability/nolint', 'readability/streams', 'readability/todo', 'readability/utf8', 'runtime/arrays', 'runtime/casting', 'runtime/explicit', 'runtime/int', 'runtime/init', 'runtime/invalid_increment', 'runtime/member_string_references', 'runtime/memset', 'runtime/operator', 'runtime/printf', 'runtime/printf_format', 'runtime/references', 'runtime/rtti', 'runtime/sizeof', 'runtime/string', 'runtime/threadsafe_fn', 'whitespace/blank_line', 'whitespace/braces', 'whitespace/comma', 'whitespace/comments', 'whitespace/empty_loop_body', 'whitespace/end_of_line', 'whitespace/ending_newline', 'whitespace/forcolon', 'whitespace/indent', 'whitespace/labels', 'whitespace/line_length', 'whitespace/newline', 'whitespace/operators', 'whitespace/parens', 'whitespace/semicolon', 'whitespace/tab', 'whitespace/todo' ] # The default state of the category filter. This is overrided by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. _DEFAULT_FILTERS = ['-build/include_alpha'] # We used to check for high-bit characters, but after much discussion we # decided those were OK, as long as they were in UTF-8 and didn't represent # hard-coded international strings, which belong in a separate i18n file. # Headers that we consider STL headers. _STL_HEADERS = frozenset([ 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', 'utility', 'vector', 'vector.h', ]) # Non-STL C++ system headers. _CPP_HEADERS = frozenset([ 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', ]) # Assertion macros. These are defined in base/logging.h and # testing/base/gunit.h. Note that the _M versions need to come first # for substring matching to work. _CHECK_MACROS = [ 'DCHECK', 'CHECK', 'EXPECT_TRUE_M', 'EXPECT_TRUE', 'ASSERT_TRUE_M', 'ASSERT_TRUE', 'EXPECT_FALSE_M', 'EXPECT_FALSE', 'ASSERT_FALSE_M', 'ASSERT_FALSE', ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) for op, replacement in [('==', 'EQ'), ('!=', 'NE'), ('>=', 'GE'), ('>', 'GT'), ('<=', 'LE'), ('<', 'LT')]: _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement # Alternative tokens and their replacements. For full list, see section 2.5 # Alternative tokens [lex.digraph] in the C++ standard. # # Digraphs (such as '%:') are not included here since it's a mess to # match those on a word boundary. _ALT_TOKEN_REPLACEMENT = { 'and': '&&', 'bitor': '|', 'or': '||', 'xor': '^', 'compl': '~', 'bitand': '&', 'and_eq': '&=', 'or_eq': '|=', 'xor_eq': '^=', 'not': '!', 'not_eq': '!=' } # Compile regular expression that matches all the above keywords. The "[ =()]" # bit is meant to avoid matching these keywords outside of boolean expressions. # # False positives include C-style multi-line comments (http://go/nsiut ) # and multi-line strings (http://go/beujw ), but those have always been # troublesome for cpplint. _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). _C_SYS_HEADER = 1 _CPP_SYS_HEADER = 2 _LIKELY_MY_HEADER = 3 _POSSIBLE_MY_HEADER = 4 _OTHER_HEADER = 5 # These constants define the current inline assembly state _NO_ASM = 0 # Outside of inline assembly block _INSIDE_ASM = 1 # Inside inline assembly block _END_ASM = 2 # Last line of inline assembly block _BLOCK_ASM = 3 # The whole block is an inline assembly block # Match start of assembly blocks _MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' r'(?:\s+(volatile|__volatile__))?' r'\s*[{(]') _regexp_compile_cache = {} # Finds occurrences of NOLINT or NOLINT(...). _RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. _error_suppressions = {} # The root directory used for deriving header guard CPP variable. # This is set by --root flag. _root = None def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of error-suppressions. Parses any NOLINT comments on the current line, updating the global error_suppressions store. Reports an error if the NOLINT comment was malformed. Args: filename: str, the name of the input file. raw_line: str, the line of input text, with comments. linenum: int, the number of the current line. error: function, an error handler. """ # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). matched = _RE_SUPPRESSION.search(raw_line) if matched: category = matched.group(1) if category in (None, '(*)'): # => "suppress all" _error_suppressions.setdefault(None, set()).add(linenum) else: if category.startswith('(') and category.endswith(')'): category = category[1:-1] if category in _ERROR_CATEGORIES: _error_suppressions.setdefault(category, set()).add(linenum) else: error(filename, linenum, 'readability/nolint', 5, 'Unknown NOLINT error category: %s' % category) def ResetNolintSuppressions(): "Resets the set of NOLINT suppressions to empty." _error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by ParseNolintSuppressions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: bool, True iff the error should be suppressed due to a NOLINT comment. """ return (linenum in _error_suppressions.get(category, set()) or linenum in _error_suppressions.get(None, set())) def Match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" # The regexp compilation caching is inlined in both Match and Search for # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].search(s) class _IncludeState(dict): """Tracks line numbers for includes, and the order in which includes appear. As a dict, an _IncludeState object serves as a mapping between include filename and line number on which that file was included. Call CheckNextIncludeOrder() once for each header in the file, passing in the type constants defined above. Calls in an illegal order will raise an _IncludeError with an appropriate error message. """ # self._section will move monotonically through this set. If it ever # needs to move backwards, CheckNextIncludeOrder will raise an error. _INITIAL_SECTION = 0 _MY_H_SECTION = 1 _C_SECTION = 2 _CPP_SECTION = 3 _OTHER_H_SECTION = 4 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', _CPP_SYS_HEADER: 'C++ system header', _LIKELY_MY_HEADER: 'header this file implements', _POSSIBLE_MY_HEADER: 'header this file may implement', _OTHER_HEADER: 'other header', } _SECTION_NAMES = { _INITIAL_SECTION: "... nothing. (This can't be an error.)", _MY_H_SECTION: 'a header this file implements', _C_SECTION: 'C system header', _CPP_SECTION: 'C++ system header', _OTHER_H_SECTION: 'other header', } def __init__(self): dict.__init__(self) # The name of the current section. self._section = self._INITIAL_SECTION # The path of last found header. self._last_header = '' def CanonicalizeAlphabeticalOrder(self, header_path): """Returns a path canonicalized for alphabetical comparison. - replaces "-" with "_" so they both cmp the same. - removes '-inl' since we don't require them to be after the main header. - lowercase everything, just in case. Args: header_path: Path to be canonicalized. Returns: Canonicalized path. """ return header_path.replace('-inl.h', '.h').replace('-', '_').lower() def IsInAlphabeticalOrder(self, header_path): """Check if a header is in alphabetical order with the previous header. Args: header_path: Header to be checked. Returns: Returns true if the header is in alphabetical order. """ canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) if self._last_header > canonical_header: return False self._last_header = canonical_header return True def CheckNextIncludeOrder(self, header_type): """Returns a non-empty error message if the next header is out of order. This function also updates the internal state to be ready to check the next include. Args: header_type: One of the _XXX_HEADER constants defined above. Returns: The empty string if the header is in the right order, or an error message describing what's wrong. """ error_message = ('Found %s after %s' % (self._TYPE_NAMES[header_type], self._SECTION_NAMES[self._section])) last_section = self._section if header_type == _C_SYS_HEADER: if self._section <= self._C_SECTION: self._section = self._C_SECTION else: self._last_header = '' return error_message elif header_type == _CPP_SYS_HEADER: if self._section <= self._CPP_SECTION: self._section = self._CPP_SECTION else: self._last_header = '' return error_message elif header_type == _LIKELY_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: self._section = self._OTHER_H_SECTION elif header_type == _POSSIBLE_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: # This will always be the fallback because we're not sure # enough that the header is associated with this file. self._section = self._OTHER_H_SECTION else: assert header_type == _OTHER_HEADER self._section = self._OTHER_H_SECTION if last_section != self._section: self._last_header = '' return '' class _CppLintState(object): """Maintains module-wide state..""" def __init__(self): self.verbose_level = 1 # global setting. self.error_count = 0 # global count of reported errors # filters to apply when emitting error messages self.filters = _DEFAULT_FILTERS[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts # output format: # "emacs" - format that emacs can parse (default) # "vs7" - format that Microsoft Visual Studio 7 can parse self.output_format = 'emacs' def SetOutputFormat(self, output_format): """Sets the output format for errors.""" self.output_format = output_format def SetVerboseLevel(self, level): """Sets the module's verbosity, and returns the previous setting.""" last_verbose_level = self.verbose_level self.verbose_level = level return last_verbose_level def SetCountingStyle(self, counting_style): """Sets the module's counting options.""" self.counting = counting_style def SetFilters(self, filters): """Sets the error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "+whitespace/indent"). Each filter should start with + or -; else we die. Raises: ValueError: The comma-separated filters did not all start with '+' or '-'. E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" """ # Default filters always have less priority than the flag ones. self.filters = _DEFAULT_FILTERS[:] for filt in filters.split(','): clean_filt = filt.strip() if clean_filt: self.filters.append(clean_filt) for filt in self.filters: if not (filt.startswith('+') or filt.startswith('-')): raise ValueError('Every filter in --filters must start with + or -' ' (%s does not)' % filt) def ResetErrorCounts(self): """Sets the module's error statistic back to zero.""" self.error_count = 0 self.errors_by_category = {} def IncrementErrorCount(self, category): """Bumps the module's error statistic.""" self.error_count += 1 if self.counting in ('toplevel', 'detailed'): if self.counting != 'detailed': category = category.split('/')[0] if category not in self.errors_by_category: self.errors_by_category[category] = 0 self.errors_by_category[category] += 1 def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" for category, count in self.errors_by_category.iteritems(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) sys.stderr.write('Total errors found: %d\n' % self.error_count) _cpplint_state = _CppLintState() def _OutputFormat(): """Gets the module's output format.""" return _cpplint_state.output_format def _SetOutputFormat(output_format): """Sets the module's output format.""" _cpplint_state.SetOutputFormat(output_format) def _VerboseLevel(): """Returns the module's verbosity setting.""" return _cpplint_state.verbose_level def _SetVerboseLevel(level): """Sets the module's verbosity, and returns the previous setting.""" return _cpplint_state.SetVerboseLevel(level) def _SetCountingStyle(level): """Sets the module's counting options.""" _cpplint_state.SetCountingStyle(level) def _Filters(): """Returns the module's list of output filters, as a list.""" return _cpplint_state.filters def _SetFilters(filters): """Sets the module's error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "whitespace/indent"). Each filter should start with + or -; else we die. """ _cpplint_state.SetFilters(filters) class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. def __init__(self): self.in_a_function = False self.lines_in_function = 0 self.current_function = '' def Begin(self, function_name): """Start analyzing function body. Args: function_name: The name of the function being tracked. """ self.in_a_function = True self.lines_in_function = 0 self.current_function = function_name def Count(self): """Count line in current function body.""" if self.in_a_function: self.lines_in_function += 1 def Check(self, error, filename, linenum): """Report if too many lines in function body. Args: error: The function to call with any errors found. filename: The name of the current file. linenum: The number of the line to check. """ if Match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: base_trigger = self._NORMAL_TRIGGER trigger = base_trigger * 2**_VerboseLevel() if self.lines_in_function > trigger: error_level = int(math.log(self.lines_in_function / base_trigger, 2)) # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... if error_level > 5: error_level = 5 error(filename, linenum, 'readability/fn_size', error_level, 'Small and focused functions are preferred:' ' %s has %d non-comment lines' ' (error triggered by exceeding %d lines).' % ( self.current_function, self.lines_in_function, trigger)) def End(self): """Stop analyzing function body.""" self.in_a_function = False class _IncludeError(Exception): """Indicates a problem with the include order in a file.""" pass class FileInfo: """Provides utility functions for filenames. FileInfo provides easy access to the components of a file's path relative to the project root. """ def __init__(self, filename): self._filename = filename def FullName(self): """Make Windows paths like Unix.""" return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): """FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from the name so that we get header guards that don't include things like "C:\Documents and Settings\..." or "/home/username/..." in them and thus people on different computers who have checked the source out to different locations won't see bogus errors. """ fullname = self.FullName() if os.path.exists(fullname): project_dir = os.path.dirname(fullname) if os.path.exists(os.path.join(project_dir, ".svn")): # If there's a .svn file in the current directory, we recursively look # up the directory tree for the top of the SVN checkout root_dir = project_dir one_up_dir = os.path.dirname(root_dir) while os.path.exists(os.path.join(one_up_dir, ".svn")): root_dir = os.path.dirname(root_dir) one_up_dir = os.path.dirname(one_up_dir) prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by # searching up from the current path. root_dir = os.path.dirname(fullname) while (root_dir != os.path.dirname(root_dir) and not os.path.exists(os.path.join(root_dir, ".git")) and not os.path.exists(os.path.join(root_dir, ".hg")) and not os.path.exists(os.path.join(root_dir, ".svn"))): root_dir = os.path.dirname(root_dir) if (os.path.exists(os.path.join(root_dir, ".git")) or os.path.exists(os.path.join(root_dir, ".hg")) or os.path.exists(os.path.join(root_dir, ".svn"))): prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Don't know what to do; header guard warnings may be wrong... return fullname def Split(self): """Splits the file into the directory, basename, and extension. For 'chrome/browser/browser.cc', Split() would return ('chrome/browser', 'browser', '.cc') Returns: A tuple of (directory, basename, extension). """ googlename = self.RepositoryName() project, rest = os.path.split(googlename) return (project,) + os.path.splitext(rest) def BaseName(self): """File base name - text after the final slash, before the final period.""" return self.Split()[1] def Extension(self): """File extension - text following the final period.""" return self.Split()[2] def NoExtension(self): """File has no source file extension.""" return '/'.join(self.Split()[0:2]) def IsSource(self): """File has a source file extension.""" return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') def _ShouldPrintError(category, confidence, linenum): """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: # a "NOLINT(category)" comment appears in the source, # the verbosity level isn't high enough, or the filters filter it out. if IsErrorSuppressedByNolint(category, linenum): return False if confidence < _cpplint_state.verbose_level: return False is_filtered = False for one_filter in _Filters(): if one_filter.startswith('-'): if category.startswith(one_filter[1:]): is_filtered = True elif one_filter.startswith('+'): if category.startswith(one_filter[1:]): is_filtered = False else: assert False # should have been checked for in SetFilter. if is_filtered: return False return True def Error(filename, linenum, category, confidence, message): """Logs the fact we've found a lint error. We log where the error was found, and also our confidence in the error, that is, how certain we are this is a legitimate style regression, and not a misidentification or a use that's sometimes justified. False positives can be suppressed by the use of "cpplint(category)" comments on the offending line. These are parsed into _error_suppressions. Args: filename: The name of the file containing the error. linenum: The number of the line containing the error. category: A string used to describe the "category" this bug falls under: "whitespace", say, or "runtime". Categories may have a hierarchy separated by slashes: "whitespace/indent". confidence: A number from 1-5 representing a confidence score for the error, with 5 meaning that we are certain of the problem, and 1 meaning that it could be a legitimate construct. message: The error message. """ if _ShouldPrintError(category, confidence, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) elif _cpplint_state.output_format == 'eclipse': sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) else: sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) # Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') # Matches strings. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') # Matches characters. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") # Matches multi-line C++ comments. # This RE is a little bit more complicated than one might expect, because we # have to take care of space removals tools so we can handle comments inside # statements better. # The current rule is: We only clear spaces from both sides when we're at the # end of the line. Otherwise, we try to remove spaces from the right side, # if this doesn't work we try on left side but only if there's a non-character # on the right. _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( r"""(\s*/\*.*\*/\s*$| /\*.*\*/\s+| \s+/\*.*\*/(?=\W)| /\*.*\*/)""", re.VERBOSE) def IsCppString(line): """Does line terminate so, that the next symbol is in string constant. This function does not consider single-line nor multi-line comments. Args: line: is a partial line of code starting from the 0..n. Returns: True, if next character appended to 'line' is inside a string constant. """ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 def FindNextMultiLineCommentStart(lines, lineix): """Find the beginning marker for a multiline comment.""" while lineix < len(lines): if lines[lineix].strip().startswith('/*'): # Only return this marker if the comment goes beyond this line if lines[lineix].strip().find('*/', 2) < 0: return lineix lineix += 1 return len(lines) def FindNextMultiLineCommentEnd(lines, lineix): """We are inside a comment, find the end marker.""" while lineix < len(lines): if lines[lineix].strip().endswith('*/'): return lineix lineix += 1 return len(lines) def RemoveMultiLineCommentsFromRange(lines, begin, end): """Clears a range of lines for multi-line comments.""" # Having // dummy comments makes the lines non-empty, so we will not get # unnecessary blank line warnings later in the code. for i in range(begin, end): lines[i] = '// dummy' def RemoveMultiLineComments(filename, lines, error): """Removes multiline (c-style) comments from lines.""" lineix = 0 while lineix < len(lines): lineix_begin = FindNextMultiLineCommentStart(lines, lineix) if lineix_begin >= len(lines): return lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) if lineix_end >= len(lines): error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, 'Could not find end of multi-line comment') return RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) lineix = lineix_end + 1 def CleanseComments(line): """Removes //-comments and single-line C-style /* */ comments. Args: line: A line of C++ source. Returns: The line with single-line comments removed. """ commentpos = line.find('//') if commentpos != -1 and not IsCppString(line[:commentpos]): line = line[:commentpos].rstrip() # get rid of /* ... */ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) class CleansedLines(object): """Holds 3 copies of all lines with different preprocessing applied to them. 1) elided member contains lines without strings and comments, 2) lines member contains lines without comments, and 3) raw_lines member contains all the lines without processing. All these three members are of , and of the same length. """ def __init__(self, lines): self.elided = [] self.lines = [] self.raw_lines = lines self.num_lines = len(lines) for linenum in range(len(lines)): self.lines.append(CleanseComments(lines[linenum])) elided = self._CollapseStrings(lines[linenum]) self.elided.append(CleanseComments(elided)) def NumLines(self): """Returns the number of lines represented.""" return self.num_lines @staticmethod def _CollapseStrings(elided): """Collapses strings and chars on a line to simple "" or '' blocks. We nix strings first so we're not fooled by text like '"http://"' Args: elided: The line being processed. Returns: The line with collapsed strings. """ if not _RE_PATTERN_INCLUDE.match(elided): # Remove escaped characters first to make quote/single quote collapsing # basic. Things that look like escaped characters shouldn't occur # outside of strings and chars. elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) return elided def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): """Find the position just after the matching endchar. Args: line: a CleansedLines line. startpos: start searching at this position. depth: nesting level at startpos. startchar: expression opening character. endchar: expression closing character. Returns: Index just after endchar. """ for i in xrange(startpos, len(line)): if line[i] == startchar: depth += 1 elif line[i] == endchar: depth -= 1 if depth == 0: return i + 1 return -1 def CloseExpression(clean_lines, linenum, pos): """If input points to ( or { or [, finds the position that closes it. If lines[linenum][pos] points to a '(' or '{' or '[', finds the linenum/pos that correspond to the closing of the expression. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: A position on the line. Returns: A tuple (line, linenum, pos) pointer *past* the closing brace, or (line, len(lines), -1) if we never find a close. Note we ignore strings and comments when matching; and the line we return is the 'cleansed' line at linenum. """ line = clean_lines.elided[linenum] startchar = line[pos] if startchar not in '({[': return (line, clean_lines.NumLines(), -1) if startchar == '(': endchar = ')' if startchar == '[': endchar = ']' if startchar == '{': endchar = '}' # Check first line end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) if end_pos > -1: return (line, linenum, end_pos) tail = line[pos:] num_open = tail.count(startchar) - tail.count(endchar) while linenum < clean_lines.NumLines() - 1: linenum += 1 line = clean_lines.elided[linenum] delta = line.count(startchar) - line.count(endchar) if num_open + delta <= 0: return (line, linenum, FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) num_open += delta # Did not find endchar before end of file, give up return (line, clean_lines.NumLines(), -1) def CheckForCopyright(filename, lines, error): """Logs an error if no Copyright message appears at the top of the file.""" # We'll say it should occur by line 10. Don't forget there's a # dummy line at the front. for line in xrange(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, 'No copyright message found. ' 'You should have a line: "Copyright [year] "') def GetHeaderGuardCPPVariable(filename): """Returns the CPP variable that should be used as a header guard. Args: filename: The name of a C++ header file. Returns: The CPP variable that should be used as a header guard in the named file. """ # Restores original filename in case that cpplint is invoked from Emacs's # flymake. filename = re.sub(r'_flymake\.h$', '.h', filename) filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) fileinfo = FileInfo(filename) file_path_from_root = fileinfo.RepositoryName() if _root: file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' def CheckForHeaderGuard(filename, lines, error): """Checks that the file contains a header guard. Logs an error if no #ifndef header guard is present. For other headers, checks that the full pathname is used. Args: filename: The name of the C++ header file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ cppvar = GetHeaderGuardCPPVariable(filename) ifndef = None ifndef_linenum = 0 define = None endif = None endif_linenum = 0 for linenum, line in enumerate(lines): linesplit = line.split() if len(linesplit) >= 2: # find the first occurrence of #ifndef and #define, save arg if not ifndef and linesplit[0] == '#ifndef': # set ifndef to the header guard presented on the #ifndef line. ifndef = linesplit[1] ifndef_linenum = linenum if not define and linesplit[0] == '#define': define = linesplit[1] # find the last occurrence of #endif, save entire line if line.startswith('#endif'): endif = line endif_linenum = linenum if not ifndef: error(filename, 0, 'build/header_guard', 5, 'No #ifndef header guard found, suggested CPP variable is: %s' % cppvar) return if not define: error(filename, 0, 'build/header_guard', 5, 'No #define header guard found, suggested CPP variable is: %s' % cppvar) return # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ # for backward compatibility. if ifndef != cppvar: error_level = 0 if ifndef != cppvar + '_': error_level = 5 ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, '#ifndef header guard has wrong style, please use: %s' % cppvar) if define != ifndef: error(filename, 0, 'build/header_guard', 5, '#ifndef and #define don\'t match, suggested CPP variable is: %s' % cppvar) return if endif != ('#endif // %s' % cppvar): error_level = 0 if endif != ('#endif // %s' % (cppvar + '_')): error_level = 5 ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, error) error(filename, endif_linenum, 'build/header_guard', error_level, '#endif line should be "#endif // %s"' % cppvar) def CheckForUnicodeReplacementCharacters(filename, lines, error): """Logs an error for each line containing Unicode replacement characters. These indicate that either the file contained invalid UTF-8 (likely) or Unicode replacement characters (which it shouldn't). Note that it's possible for this to throw off line numbering if the invalid UTF-8 occurred adjacent to a newline. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ for linenum, line in enumerate(lines): if u'\ufffd' in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') def CheckForNewlineAtEOF(filename, lines, error): """Logs an error if there is no newline char at the end of the file. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ # The array lines() was created by adding two newlines to the # original file (go figure), then splitting on \n. # To verify that the file ends in \n, we just have to make sure the # last-but-two element of lines() exists and is empty. if len(lines) < 3 or lines[-2]: error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, 'Could not find a newline character at the end of the file.') def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): """Logs an error if we see /* ... */ or "..." that extend past one line. /* ... */ comments are legit inside macros, for one line. Otherwise, we prefer // comments, so it's ok to warn about the other. Likewise, it's ok for strings to extend across multiple lines, as long as a line continuation character (backslash) terminates each line. Although not currently prohibited by the C++ style guide, it's ugly and unnecessary. We don't do well with either in this lint program, so we warn about both. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Remove all \\ (escaped backslashes) from the line. They are OK, and the # second (escaped) slash may trigger later \" detection erroneously. line = line.replace('\\\\', '') if line.count('/*') > line.count('*/'): error(filename, linenum, 'readability/multiline_comment', 5, 'Complex multi-line /*...*/-style comment found. ' 'Lint may give bogus warnings. ' 'Consider replacing these with //-style comments, ' 'with #if 0...#endif, ' 'or with more clearly structured multi-line comments.') if (line.count('"') - line.count('\\"')) % 2: error(filename, linenum, 'readability/multiline_string', 5, 'Multi-line string ("...") found. This lint script doesn\'t ' 'do well with such strings, and may give bogus warnings. They\'re ' 'ugly and unnecessary, and you should use concatenation instead".') threading_list = ( ('asctime(', 'asctime_r('), ('ctime(', 'ctime_r('), ('getgrgid(', 'getgrgid_r('), ('getgrnam(', 'getgrnam_r('), ('getlogin(', 'getlogin_r('), ('getpwnam(', 'getpwnam_r('), ('getpwuid(', 'getpwuid_r('), ('gmtime(', 'gmtime_r('), ('localtime(', 'localtime_r('), ('rand(', 'rand_r('), ('readdir(', 'readdir_r('), ('strtok(', 'strtok_r('), ('ttyname(', 'ttyname_r('), ) def CheckPosixThreading(filename, clean_lines, linenum, error): """Checks for calls to thread-unsafe functions. Much code has been originally written without consideration of multi-threading. Also, engineers are relying on their old experience; they have learned posix before threading extensions were added. These tests guide the engineers to use thread-safe functions (when using posix directly). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] for single_thread_function, multithread_safe_function in threading_list: ix = line.find(single_thread_function) # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and line[ix - 1] not in ('_', '.', '>'))): error(filename, linenum, 'runtime/threadsafe_fn', 2, 'Consider using ' + multithread_safe_function + '...) instead of ' + single_thread_function + '...) for improved thread safety.') # Matches invalid increment: *count++, which moves pointer instead of # incrementing a value. _RE_PATTERN_INVALID_INCREMENT = re.compile( r'^\s*\*\w+(\+\+|--);') def CheckInvalidIncrement(filename, clean_lines, linenum, error): """Checks for invalid increment *count++. For example following function: void increment_counter(int* count) { *count++; } is invalid, because it effectively does count++, moving pointer, and should be replaced with ++*count, (*count)++ or *count += 1. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] if _RE_PATTERN_INVALID_INCREMENT.match(line): error(filename, linenum, 'runtime/invalid_increment', 5, 'Changing pointer instead of value (or unused value of operator*).') class _BlockInfo(object): """Stores information about a generic block of code.""" def __init__(self, seen_open_brace): self.seen_open_brace = seen_open_brace self.open_parentheses = 0 self.inline_asm = _NO_ASM def CheckBegin(self, filename, clean_lines, linenum, error): """Run checks that applies to text up to the opening brace. This is mostly for checking the text after the class identifier and the "{", usually where the base class is specified. For other blocks, there isn't much to check, so we always pass. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass def CheckEnd(self, filename, clean_lines, linenum, error): """Run checks that applies to text after the closing brace. This is mostly used for checking end of namespace comments. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass class _ClassInfo(_BlockInfo): """Stores information about a class.""" def __init__(self, name, class_or_struct, clean_lines, linenum): _BlockInfo.__init__(self, False) self.name = name self.starting_linenum = linenum self.is_derived = False if class_or_struct == 'struct': self.access = 'public' else: self.access = 'private' # Try to find the end of the class. This will be confused by things like: # class A { # } *x = { ... # # But it's still good enough for CheckSectionSpacing. self.last_line = 0 depth = 0 for i in range(linenum, clean_lines.NumLines()): line = clean_lines.elided[i] depth += line.count('{') - line.count('}') if not depth: self.last_line = i break def CheckBegin(self, filename, clean_lines, linenum, error): # Look for a bare ':' if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): self.is_derived = True class _NamespaceInfo(_BlockInfo): """Stores information about a namespace.""" def __init__(self, name, linenum): _BlockInfo.__init__(self, False) self.name = name or '' self.starting_linenum = linenum def CheckEnd(self, filename, clean_lines, linenum, error): """Check end of namespace comments.""" line = clean_lines.raw_lines[linenum] # Check how many lines is enclosed in this namespace. Don't issue # warning for missing namespace comments if there aren't enough # lines. However, do apply checks if there is already an end of # namespace comment and it's incorrect. # # TODO(unknown): We always want to check end of namespace comments # if a namespace is large, but sometimes we also want to apply the # check if a short namespace contained nontrivial things (something # other than forward declarations). There is currently no logic on # deciding what these nontrivial things are, so this check is # triggered by namespace size only, which works most of the time. if (linenum - self.starting_linenum < 10 and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): return # Look for matching comment at end of namespace. # # Note that we accept C style "/* */" comments for terminating # namespaces, so that code that terminate namespaces inside # preprocessor macros can be cpplint clean. Example: http://go/nxpiz # # We also accept stuff like "// end of namespace ." with the # period at the end. # # Besides these, we don't accept anything else, otherwise we might # get false negatives when existing comment is a substring of the # expected namespace. Example: http://go/ldkdc, http://cl/23548205 if self.name: # Named namespace if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + r'[\*/\.\\\s]*$'), line): error(filename, linenum, 'readability/namespace', 5, 'Namespace should be terminated with "// namespace %s"' % self.name) else: # Anonymous namespace if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): error(filename, linenum, 'readability/namespace', 5, 'Namespace should be terminated with "// namespace"') class _PreprocessorInfo(object): """Stores checkpoints of nesting stacks when #if/#else is seen.""" def __init__(self, stack_before_if): # The entire nesting stack before #if self.stack_before_if = stack_before_if # The entire nesting stack up to #else self.stack_before_else = [] # Whether we have already seen #else or #elif self.seen_else = False class _NestingState(object): """Holds states related to parsing braces.""" def __init__(self): # Stack for tracking all braces. An object is pushed whenever we # see a "{", and popped when we see a "}". Only 3 types of # objects are possible: # - _ClassInfo: a class or struct. # - _NamespaceInfo: a namespace. # - _BlockInfo: some other type of block. self.stack = [] # Stack of _PreprocessorInfo objects. self.pp_stack = [] def SeenOpenBrace(self): """Check if we have seen the opening brace for the innermost block. Returns: True if we have seen the opening brace, False if the innermost block is still expecting an opening brace. """ return (not self.stack) or self.stack[-1].seen_open_brace def InNamespaceBody(self): """Check if we are currently one level inside a namespace body. Returns: True if top of the stack is a namespace block, False otherwise. """ return self.stack and isinstance(self.stack[-1], _NamespaceInfo) def UpdatePreprocessor(self, line): """Update preprocessor stack. We need to handle preprocessors due to classes like this: #ifdef SWIG struct ResultDetailsPageElementExtensionPoint { #else struct ResultDetailsPageElementExtensionPoint : public Extension { #endif (see http://go/qwddn for original example) We make the following assumptions (good enough for most files): - Preprocessor condition evaluates to true from #if up to first #else/#elif/#endif. - Preprocessor condition evaluates to false from #else/#elif up to #endif. We still perform lint checks on these lines, but these do not affect nesting stack. Args: line: current line to check. """ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): # Beginning of #if block, save the nesting stack here. The saved # stack will allow us to restore the parsing state in the #else case. self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) elif Match(r'^\s*#\s*(else|elif)\b', line): # Beginning of #else block if self.pp_stack: if not self.pp_stack[-1].seen_else: # This is the first #else or #elif block. Remember the # whole nesting stack up to this point. This is what we # keep after the #endif. self.pp_stack[-1].seen_else = True self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) # Restore the stack to how it was before the #if self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) else: # TODO(unknown): unexpected #else, issue warning? pass elif Match(r'^\s*#\s*endif\b', line): # End of #if or #else blocks. if self.pp_stack: # If we saw an #else, we will need to restore the nesting # stack to its former state before the #else, otherwise we # will just continue from where we left off. if self.pp_stack[-1].seen_else: # Here we can just use a shallow copy since we are the last # reference to it. self.stack = self.pp_stack[-1].stack_before_else # Drop the corresponding #if self.pp_stack.pop() else: # TODO(unknown): unexpected #endif, issue warning? pass def Update(self, filename, clean_lines, linenum, error): """Update nesting state with current line. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Update pp_stack first self.UpdatePreprocessor(line) # Count parentheses. This is to avoid adding struct arguments to # the nesting stack. if self.stack: inner_block = self.stack[-1] depth_change = line.count('(') - line.count(')') inner_block.open_parentheses += depth_change # Also check if we are starting or ending an inline assembly block. if inner_block.inline_asm in (_NO_ASM, _END_ASM): if (depth_change != 0 and inner_block.open_parentheses == 1 and _MATCH_ASM.match(line)): # Enter assembly block inner_block.inline_asm = _INSIDE_ASM else: # Not entering assembly block. If previous line was _END_ASM, # we will now shift to _NO_ASM state. inner_block.inline_asm = _NO_ASM elif (inner_block.inline_asm == _INSIDE_ASM and inner_block.open_parentheses == 0): # Exit assembly block inner_block.inline_asm = _END_ASM # Consume namespace declaration at the beginning of the line. Do # this in a loop so that we catch same line declarations like this: # namespace proto2 { namespace bridge { class MessageSet; } } while True: # Match start of namespace. The "\b\s*" below catches namespace # declarations even if it weren't followed by a whitespace, this # is so that we don't confuse our namespace checker. The # missing spaces will be flagged by CheckSpacing. namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) if not namespace_decl_match: break new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) self.stack.append(new_namespace) line = namespace_decl_match.group(2) if line.find('{') != -1: new_namespace.seen_open_brace = True line = line[line.find('{') + 1:] # Look for a class declaration in whatever is left of the line # after parsing namespaces. The regexp accounts for decorated classes # such as in: # class LOCKABLE API Object { # }; # # Templates with class arguments may confuse the parser, for example: # template , # class Vector = vector > # class HeapQueue { # # Because this parser has no nesting state about templates, by the # time it saw "class Comparator", it may think that it's a new class. # Nested templates have a similar problem: # template < # typename ExportedType, # typename TupleType, # template class ImplTemplate> # # To avoid these cases, we ignore classes that are followed by '=' or '>' class_decl_match = Match( r'\s*(template\s*<[\w\s<>,:]*>\s*)?' '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' '(([^=>]|<[^<>]*>)*)$', line) if (class_decl_match and (not self.stack or self.stack[-1].open_parentheses == 0)): self.stack.append(_ClassInfo( class_decl_match.group(4), class_decl_match.group(2), clean_lines, linenum)) line = class_decl_match.group(5) # If we have not yet seen the opening brace for the innermost block, # run checks here. if not self.SeenOpenBrace(): self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) # Update access control if we are inside a class/struct if self.stack and isinstance(self.stack[-1], _ClassInfo): access_match = Match(r'\s*(public|private|protected)\s*:', line) if access_match: self.stack[-1].access = access_match.group(1) # Consume braces or semicolons from what's left of the line while True: # Match first brace, semicolon, or closed parenthesis. matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) if not matched: break token = matched.group(1) if token == '{': # If namespace or class hasn't seen a opening brace yet, mark # namespace/class head as complete. Push a new block onto the # stack otherwise. if not self.SeenOpenBrace(): self.stack[-1].seen_open_brace = True else: self.stack.append(_BlockInfo(True)) if _MATCH_ASM.match(line): self.stack[-1].inline_asm = _BLOCK_ASM elif token == ';' or token == ')': # If we haven't seen an opening brace yet, but we already saw # a semicolon, this is probably a forward declaration. Pop # the stack for these. # # Similarly, if we haven't seen an opening brace yet, but we # already saw a closing parenthesis, then these are probably # function arguments with extra "class" or "struct" keywords. # Also pop these stack for these. if not self.SeenOpenBrace(): self.stack.pop() else: # token == '}' # Perform end of block checks and pop the stack. if self.stack: self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) self.stack.pop() line = matched.group(2) def InnermostClass(self): """Get class info on the top of the stack. Returns: A _ClassInfo object if we are inside a class, or None otherwise. """ for i in range(len(self.stack), 0, -1): classinfo = self.stack[i - 1] if isinstance(classinfo, _ClassInfo): return classinfo return None def CheckClassFinished(self, filename, error): """Checks that all classes have been completely parsed. Call this when all lines in a file have been processed. Args: filename: The name of the current file. error: The function to call with any errors found. """ # Note: This test can result in false positives if #ifdef constructs # get in the way of brace matching. See the testBuildClass test in # cpplint_unittest.py for an example of this. for obj in self.stack: if isinstance(obj, _ClassInfo): error(filename, obj.starting_linenum, 'build/class', 5, 'Failed to find complete declaration of class %s' % obj.name) def CheckForNonStandardConstructs(filename, clean_lines, linenum, nesting_state, error): """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. Complain about several constructs which gcc-2 accepts, but which are not standard C++. Warning about these in lint is one way to ease the transition to new compilers. - put storage class first (e.g. "static const" instead of "const static"). - "%lld" instead of %qd" in printf-type functions. - "%1$d" is non-standard in printf-type functions. - "\%" is an undefined character escape sequence. - text after #endif is not allowed. - invalid inner-style forward declaration. - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line): error(filename, linenum, 'build/deprecated', 3, '>? and ))?' # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' error(filename, linenum, 'runtime/member_string_references', 2, 'const string& members are dangerous. It is much better to use ' 'alternatives, such as pointers or simple constants.') # Everything else in this function operates on class declarations. # Return early if the top of the nesting stack is not a class, or if # the class head is not completed yet. classinfo = nesting_state.InnermostClass() if not classinfo or not classinfo.seen_open_brace: return # The class may have been declared with namespace or classname qualifiers. # The constructor and destructor will not have those qualifiers. base_classname = classinfo.name.split('::')[-1] # Look for single-argument constructors that aren't marked explicit. # Technically a valid construct, but against style. args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' % re.escape(base_classname), line) if (args and args.group(1) != 'void' and not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), args.group(1).strip())): error(filename, linenum, 'runtime/explicit', 5, 'Single-argument constructors should be marked explicit.') def CheckSpacingForFunctionCall(filename, line, linenum, error): """Checks for the correctness of various spacing around function calls. Args: filename: The name of the current file. line: The text of the line to check. linenum: The number of the line to check. error: The function to call with any errors found. """ # Since function calls often occur inside if/for/while/switch # expressions - which have their own, more liberal conventions - we # first see if we should be looking inside such an expression for a # function call, to which we can apply more strict standards. fncall = line # if there's no control flow construct, look at whole line for pattern in (r'\bif\s*\((.*)\)\s*{', r'\bfor\s*\((.*)\)\s*{', r'\bwhile\s*\((.*)\)\s*[{;]', r'\bswitch\s*\((.*)\)\s*{'): match = Search(pattern, line) if match: fncall = match.group(1) # look inside the parens for function calls break # Except in if/for/while/switch, there should never be space # immediately inside parens (eg "f( 3, 4 )"). We make an exception # for nested parens ( (a+b) + c ). Likewise, there should never be # a space before a ( when it's a function argument. I assume it's a # function argument when the char before the whitespace is legal in # a function name (alnum + _) and we're not starting a macro. Also ignore # pointers and references to arrays and functions coz they're too tricky: # we use a very simple way to recognize these: # " (something)(maybe-something)" or # " (something)(maybe-something," or # " (something)[something]" # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and # Ignore pointers/references to functions. not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call error(filename, linenum, 'whitespace/parens', 4, 'Extra space after ( in function call') elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') if (Search(r'\w\s+\(', fncall) and not Search(r'#\s*define|typedef', fncall) and not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): error(filename, linenum, 'whitespace/parens', 4, 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain if Search(r'[^)]\s+\)\s*[^{\s]', fncall): # If the closing parenthesis is preceded by only whitespaces, # try to give a more descriptive error message. if Search(r'^\s+\)', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Closing ) should be moved to the previous line') else: error(filename, linenum, 'whitespace/parens', 2, 'Extra space before )') def IsBlankLine(line): """Returns true if the given line is blank. We consider a line to be blank if the line is empty or consists of only white spaces. Args: line: A line of a string. Returns: True, if the given line is blank. """ return not line or line.isspace() def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error): """Reports for long function bodies. For an overview why this is done, see: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions Uses a simplistic algorithm assuming other style guidelines (especially spacing) are followed. Only checks unindented functions, so class members are unchecked. Trivial bodies are unchecked, so constructors with huge initializer lists may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal of vertical space and comments just to get through a lint check. NOLINT *on the last line of a function* disables this check. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. function_state: Current function name and lines in body so far. error: The function to call with any errors found. """ lines = clean_lines.lines line = lines[linenum] raw = clean_lines.raw_lines raw_line = raw[linenum] joined_line = '' starting_func = False regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... match_result = Match(regexp, line) if match_result: # If the name is all caps and underscores, figure it's a macro and # ignore it, unless it's TEST or TEST_F. function_name = match_result.group(1).split()[-1] if function_name == 'TEST' or function_name == 'TEST_F' or ( not Match(r'[A-Z_]+$', function_name)): starting_func = True if starting_func: body_found = False for start_linenum in xrange(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() if Search(r'(;|})', start_line): # Declarations and trivial functions body_found = True break # ... ignore elif Search(r'{', start_line): body_found = True function = Search(r'((\w|:)*)\(', line).group(1) if Match(r'TEST', function): # Handle TEST... macros parameter_regexp = Search(r'(\(.*\))', joined_line) if parameter_regexp: # Ignore bad syntax function += parameter_regexp.group(1) else: function += '()' function_state.Begin(function) break if not body_found: # No body for the function (or evidence of a non-function) was found. error(filename, linenum, 'readability/fn_size', 5, 'Lint failed to find start of function body.') elif Match(r'^\}\s*$', line): # function end function_state.Check(error, filename, linenum) function_state.End() elif not Match(r'^\s*$', line): function_state.Count() # Count non-blank/non-comment lines. _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') def CheckComment(comment, filename, linenum, error): """Checks for common mistakes in TODO comments. Args: comment: The text of the comment from the line in question. filename: The name of the current file. linenum: The number of the line to check. error: The function to call with any errors found. """ match = _RE_PATTERN_TODO.match(comment) if match: # One whitespace is correct; zero whitespace is handled elsewhere. leading_whitespace = match.group(1) if len(leading_whitespace) > 1: error(filename, linenum, 'whitespace/todo', 2, 'Too many spaces before TODO') username = match.group(2) if not username: error(filename, linenum, 'readability/todo', 2, 'Missing username in TODO; it should look like ' '"// TODO(my_username): Stuff."') middle_whitespace = match.group(3) # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 if middle_whitespace != ' ' and middle_whitespace != '': error(filename, linenum, 'whitespace/todo', 2, 'TODO(my_username) should be followed by a space') def CheckAccess(filename, clean_lines, linenum, nesting_state, error): """Checks for improper use of DISALLOW* macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' r'DISALLOW_EVIL_CONSTRUCTORS|' r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) if not matched: return if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): if nesting_state.stack[-1].access != 'private': error(filename, linenum, 'readability/constructors', 3, '%s must be in the private: section' % matched.group(1)) else: # Found DISALLOW* macro outside a class declaration, or perhaps it # was used inside a function when it should have been part of the # class declaration. We could issue a warning here, but it # probably resulted in a compiler error already. pass def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): """Find the corresponding > to close a template. Args: clean_lines: A CleansedLines instance containing the file. linenum: Current line number. init_suffix: Remainder of the current line after the initial <. Returns: True if a matching bracket exists. """ line = init_suffix nesting_stack = ['<'] while True: # Find the next operator that can tell us whether < is used as an # opening bracket or as a less-than operator. We only want to # warn on the latter case. # # We could also check all other operators and terminate the search # early, e.g. if we got something like this "a(),;\[\]]*([<>(),;\[\]])(.*)$', line) if match: # Found an operator, update nesting stack operator = match.group(1) line = match.group(2) if nesting_stack[-1] == '<': # Expecting closing angle bracket if operator in ('<', '(', '['): nesting_stack.append(operator) elif operator == '>': nesting_stack.pop() if not nesting_stack: # Found matching angle bracket return True elif operator == ',': # Got a comma after a bracket, this is most likely a template # argument. We have not seen a closing angle bracket yet, but # it's probably a few lines later if we look for it, so just # return early here. return True else: # Got some other operator. return False else: # Expecting closing parenthesis or closing bracket if operator in ('<', '(', '['): nesting_stack.append(operator) elif operator in (')', ']'): # We don't bother checking for matching () or []. If we got # something like (] or [), it would have been a syntax error. nesting_stack.pop() else: # Scan the next line linenum += 1 if linenum >= len(clean_lines.elided): break line = clean_lines.elided[linenum] # Exhausted all remaining lines and still no matching angle bracket. # Most likely the input was incomplete, otherwise we should have # seen a semicolon and returned early. return True def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): """Find the corresponding < that started a template. Args: clean_lines: A CleansedLines instance containing the file. linenum: Current line number. init_prefix: Part of the current line before the initial >. Returns: True if a matching bracket exists. """ line = init_prefix nesting_stack = ['>'] while True: # Find the previous operator match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) if match: # Found an operator, update nesting stack operator = match.group(2) line = match.group(1) if nesting_stack[-1] == '>': # Expecting opening angle bracket if operator in ('>', ')', ']'): nesting_stack.append(operator) elif operator == '<': nesting_stack.pop() if not nesting_stack: # Found matching angle bracket return True elif operator == ',': # Got a comma before a bracket, this is most likely a # template argument. The opening angle bracket is probably # there if we look for it, so just return early here. return True else: # Got some other operator. return False else: # Expecting opening parenthesis or opening bracket if operator in ('>', ')', ']'): nesting_stack.append(operator) elif operator in ('(', '['): nesting_stack.pop() else: # Scan the previous line linenum -= 1 if linenum < 0: break line = clean_lines.elided[linenum] # Exhausted all earlier lines and still no matching angle bracket. return False def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): """Checks for the correctness of various spacing issues in the code. Things we check for: spaces around operators, spaces after if/for/while/switch, no spaces around parens in function calls, two spaces between code and comment, don't start a block with a blank line, don't end a function with a blank line, don't add a blank line after public/protected/private, don't have too many blank lines in a row. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] # Before nixing comments, check if the line is blank for no good # reason. This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' # # Skip all the blank line checks if we are immediately inside a # namespace body. In other words, don't issue blank line warnings # for this block: # namespace { # # } # # A warning about missing end of namespace comments will be issued instead. if IsBlankLine(line) and not nesting_state.InNamespaceBody(): elided = clean_lines.elided prev_line = elided[linenum - 1] prevbrace = prev_line.rfind('{') # TODO(unknown): Don't complain if line before blank line, and line after, # both start with alnums and are indented the same amount. # This ignores whitespace at the start of a namespace block # because those are not usually indented. if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: # OK, we have a blank line at the start of a code block. Before we # complain, we check if it is an exception to the rule: The previous # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name). We also check for the case where # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False if Match(r' {6}\w', prev_line): # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum-2 while (search_position >= 0 and Match(r' {6}\w', elided[search_position])): search_position -= 1 exception = (search_position >= 0 and elided[search_position][:5] == ' :') else: # Search for the function arguments or an initializer list. We use a # simple heuristic here: If the line is indented 4 spaces; and we have a # closing paren, without the opening paren, followed by an opening brace # or colon (for initializer lists) we assume that it is the last line of # a function header. If we have a colon indented 4 spaces, it is an # initializer list. exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', prev_line) or Match(r' {4}:', prev_line)) if not exception: error(filename, linenum, 'whitespace/blank_line', 2, 'Blank line at the start of a code block. Is this needed?') # Ignore blank lines at the end of a block in a long if-else # chain, like this: # if (condition1) { # // Something followed by a blank line # # } else if (condition2) { # // Something else # } if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] if (next_line and Match(r'\s*}', next_line) and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, 'Blank line at the end of a code block. Is this needed?') matched = Match(r'\s*(public|protected|private):', prev_line) if matched: error(filename, linenum, 'whitespace/blank_line', 3, 'Do not leave a blank line after "%s:"' % matched.group(1)) # Next, we complain if there's a comment too near the text commentpos = line.find('//') if commentpos != -1: # Check if the // may be in quotes. If so, ignore it # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if (line.count('"', 0, commentpos) - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes # Allow one space for new scopes, two spaces otherwise: if (not Match(r'^\s*{ //', line) and ((commentpos >= 1 and line[commentpos-1] not in string.whitespace) or (commentpos >= 2 and line[commentpos-2] not in string.whitespace))): error(filename, linenum, 'whitespace/comments', 2, 'At least two spaces is best between code and comments') # There should always be a space between the // and the comment commentend = commentpos + 2 if commentend < len(line) and not line[commentend] == ' ': # but some lines are exceptions -- e.g. if they're big # comment delimiters like: # //---------------------------------------------------------- # or are an empty C++ style Doxygen comment, like: # /// # or they begin with multiple slashes followed by a space: # //////// Header comment match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^/+ ', line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') CheckComment(line[commentpos:], filename, linenum, error) line = clean_lines.elided[linenum] # get rid of comments and strings # Don't try to do spacing checks for operator methods line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. # You should always have whitespace around binary operators. # # Check <= and >= first to avoid false positives with < and >, then # check non-include lines for spacing around < and >. match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) if match and not (match.group(1).isdigit() and match.group(2).isdigit()): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <<') elif not Match(r'#.*include', line): # Avoid false positives on -> reduced_line = line.replace('->', '') # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though # technically should should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <') # Look for > that is not surrounded by spaces. Similar to the # above, we only trigger if both sides are missing spaces to avoid # false positives with shifts. match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) if (match and not FindPreviousMatchingAngleBracket(clean_lines, linenum, match.group(1))): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >') # We allow no-spaces around >> for almost anything. This is because # C++11 allows ">>" to close nested templates, which accounts for # most cases when ">>" is not followed by a space. # # We still warn on ">>" followed by alpha character, because that is # likely due to ">>" being used for right shifts, e.g.: # value >> alpha # # When ">>" is used to close templates, the alphanumeric letter that # follows would be part of an identifier, and there should still be # a space separating the template type and the identifier. # type> alpha match = Search(r'>>[a-zA-Z_]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >>') # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: error(filename, linenum, 'whitespace/operators', 4, 'Extra space for operator %s' % match.group(1)) # A pet peeve of mine: no spaces after an if, while, switch, or for match = Search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, 'Missing space before ( in %s' % match.group(1)) # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. match = Search(r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', line) if match: if len(match.group(2)) != len(match.group(4)): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, 'Mismatching spaces inside () in %s' % match.group(1)) if not len(match.group(2)) in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, 'Should have zero or one spaces inside ( and ) in %s' % match.group(1)) # You should always have a space after a comma (either as fn arg or operator) if Search(r',[^\s]', line): error(filename, linenum, 'whitespace/comma', 3, 'Missing space after ,') # You should always have a space after a semicolon # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; if Search(r';[^\s};\\)/]', line): error(filename, linenum, 'whitespace/semicolon', 3, 'Missing space after ;') # Next we will look for issues with function calls. CheckSpacingForFunctionCall(filename, line, linenum, error) # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. if Search(r'[^ ({]{', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before {') # Make sure '} else {' has spaces. if Search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'new char * []'. if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): error(filename, linenum, 'whitespace/braces', 5, 'Extra space before [') # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Semicolon defining empty statement. Use {} instead.') elif Search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' 'use {} instead.') elif (Search(r'\s+;\s*$', line) and not Search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' 'statement, use {} instead.') # In range-based for, we wanted spaces before and after the colon, but # not around "::" tokens that might appear. if (Search('for *\(.*[^:]:[^: ]', line) or Search('for *\(.*[^: ]:[^:]', line)): error(filename, linenum, 'whitespace/forcolon', 2, 'Missing space around colon in range-based for loop') def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): """Checks for additional blank line issues related to sections. Currently the only thing checked here is blank line before protected/private. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. class_info: A _ClassInfo objects. linenum: The number of the line to check. error: The function to call with any errors found. """ # Skip checks if the class is small, where small means 25 lines or less. # 25 lines seems like a good cutoff since that's the usual height of # terminals, and any class that can't fit in one screen can't really # be considered "small". # # Also skip checks if we are on the first line. This accounts for # classes that look like # class Foo { public: ... }; # # If we didn't find the end of the class, last_line would be zero, # and the check will be skipped by the first condition. if (class_info.last_line - class_info.starting_linenum <= 24 or linenum <= class_info.starting_linenum): return matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) if matched: # Issue warning if the line before public/protected/private was # not a blank line, but don't do this if the previous line contains # "class" or "struct". This can happen two ways: # - We are at the beginning of the class. # - We are forward-declaring an inner class that is semantically # private, but needed to be public for implementation reasons. # Also ignores cases where the previous line ends with a backslash as can be # common when defining classes in C macros. prev_line = clean_lines.lines[linenum - 1] if (not IsBlankLine(prev_line) and not Search(r'\b(class|struct)\b', prev_line) and not Search(r'\\$', prev_line)): # Try a bit harder to find the beginning of the class. This is to # account for multi-line base-specifier lists, e.g.: # class Derived # : public Base { end_class_head = class_info.starting_linenum for i in range(class_info.starting_linenum, linenum): if Search(r'\{\s*$', clean_lines.lines[i]): end_class_head = i break if end_class_head < linenum - 1: error(filename, linenum, 'whitespace/blank_line', 3, '"%s:" should be preceded by a blank line' % matched.group(1)) def GetPreviousNonBlankLine(clean_lines, linenum): """Return the most recent non-blank line and its line number. Args: clean_lines: A CleansedLines instance containing the file contents. linenum: The number of the line to check. Returns: A tuple with two elements. The first element is the contents of the last non-blank line before the current line, or the empty string if this is the first non-blank line. The second is the line number of that line, or -1 if this is the first non-blank line. """ prevlinenum = linenum - 1 while prevlinenum >= 0: prevline = clean_lines.elided[prevlinenum] if not IsBlankLine(prevline): # if not a blank line... return (prevline, prevlinenum) prevlinenum -= 1 return ('', -1) def CheckBraces(filename, clean_lines, linenum, error): """Looks for misplaced braces (e.g. at the end of line). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings if Match(r'\s*{\s*$', line): # We allow an open brace to start a line in the case where someone # is using braces in a block to explicitly create a new scope, # which is commonly used to control the lifetime of # stack-allocated variables. We don't detect this perfectly: we # just don't complain if the last non-whitespace character on the # previous non-blank line is ';', ':', '{', or '}', or if the previous # line starts a preprocessor block. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if (not Search(r'[;:}{]\s*$', prevline) and not Match(r'\s*#', prevline)): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. if Match(r'\s*else\s*', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if Match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, 'An else should appear on the same line as the preceding }') # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if # find the ( after the if pos = line.find('else if') pos = line.find('(', pos) if pos > 0: (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) if endline[endpos:].find('{') == -1: # must be brace after if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') else: # common case: else not followed by a multi-line if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): error(filename, linenum, 'whitespace/newline', 4, 'Else clause should never be on same line as else (use 2 lines)') # In the same way, a do/while should never be on one line if Match(r'\s*do [^\s{]', line): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') # Braces shouldn't be followed by a ; unless they're defining a struct # or initializing an array. # We can't tell in general, but we can for some common cases. prevlinenum = linenum while True: (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): line = prevline + line else: break if (Search(r'{.*}\s*;', line) and line.count('{') == line.count('}') and not Search(r'struct|class|enum|\s*=\s*{', line)): error(filename, linenum, 'readability/braces', 4, "You don't need a ; after a }") def CheckEmptyLoopBody(filename, clean_lines, linenum, error): """Loop for empty loop body with only a single semicolon. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Search for loop keywords at the beginning of the line. Because only # whitespaces are allowed before the keywords, this will also ignore most # do-while-loops, since those lines should start with closing brace. line = clean_lines.elided[linenum] if Match(r'\s*(for|while)\s*\(', line): # Find the end of the conditional expression (end_line, end_linenum, end_pos) = CloseExpression( clean_lines, linenum, line.find('(')) # Output warning if what follows the condition expression is a semicolon. # No warning for all other cases, including whitespace or newline, since we # have a separate check for semicolons preceded by whitespace. if end_pos >= 0 and Match(r';', end_line[end_pos:]): error(filename, end_linenum, 'whitespace/empty_loop_body', 5, 'Empty loop bodies should use {} or continue') def ReplaceableCheck(operator, macro, line): """Determine whether a basic CHECK can be replaced with a more specific one. For example suggest using CHECK_EQ instead of CHECK(a == b) and similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. Args: operator: The C++ operator used in the CHECK. macro: The CHECK or EXPECT macro being called. line: The current source line. Returns: True if the CHECK can be replaced with a more specific one. """ # This matches decimal and hex integers, strings, and chars (in that order). match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' # Expression to match two sides of the operator with something that # looks like a literal, since CHECK(x == iterator) won't compile. # This means we can't catch all the cases where a more specific # CHECK is possible, but it's less annoying than dealing with # extraneous warnings. match_this = (r'\s*' + macro + r'\((\s*' + match_constant + r'\s*' + operator + r'[^<>].*|' r'.*[^<>]' + operator + r'\s*' + match_constant + r'\s*\))') # Don't complain about CHECK(x == NULL) or similar because # CHECK_EQ(x, NULL) won't compile (requires a cast). # Also, don't complain about more complex boolean expressions # involving && or || such as CHECK(a == b || c == d). return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) def CheckCheck(filename, clean_lines, linenum, error): """Checks the use of CHECK and EXPECT macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Decide the set of replacement macros that should be suggested raw_lines = clean_lines.raw_lines current_macro = '' for macro in _CHECK_MACROS: if raw_lines[linenum].find(macro) >= 0: current_macro = macro break if not current_macro: # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' return line = clean_lines.elided[linenum] # get rid of comments and strings # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. for operator in ['==', '!=', '>=', '>', '<=', '<']: if ReplaceableCheck(operator, current_macro, line): error(filename, linenum, 'readability/check', 2, 'Consider using %s instead of %s(a %s b)' % ( _CHECK_REPLACEMENT[current_macro][operator], current_macro, operator)) break def CheckAltTokens(filename, clean_lines, linenum, error): """Check alternative keywords being used in boolean expressions. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Avoid preprocessor lines if Match(r'^\s*#', line): return # Last ditch effort to avoid multi-line comments. This will not help # if the comment started before the current line or ended after the # current line, but it catches most of the false positives. At least, # it provides a way to workaround this warning for people who use # multi-line comments in preprocessor macros. # # TODO(unknown): remove this once cpplint has better support for # multi-line comments. if line.find('/*') >= 0 or line.find('*/') >= 0: return for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): error(filename, linenum, 'readability/alt_tokens', 2, 'Use operator %s instead of %s' % ( _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) def GetLineWidth(line): """Determines the width of the line in column positions. Args: line: A string, which may be a Unicode string. Returns: The width of the line in column positions, accounting for Unicode combining characters and wide characters. """ if isinstance(line, unicode): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 elif not unicodedata.combining(uc): width += 1 return width else: return len(line) def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we do what we can. In particular we check for 2-space indents, line lengths, tab usage, spaces inside code, etc. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ raw_lines = clean_lines.raw_lines line = raw_lines[linenum] if line.find('\t') != -1: error(filename, linenum, 'whitespace/tab', 1, 'Tab found; better to use spaces') # One or three blank spaces at the beginning of the line is weird; it's # hard to reconcile that with 2-space indents. # NOTE: here are the conditions rob pike used for his tests. Mine aren't # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces # if(RLENGTH > 20) complain = 0; # if(match($0, " +(error|private|public|protected):")) complain = 0; # if(match(prev, "&& *$")) complain = 0; # if(match(prev, "\\|\\| *$")) complain = 0; # if(match(prev, "[\",=><] *$")) complain = 0; # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 if line and line[-1].isspace(): error(filename, linenum, 'whitespace/end_of_line', 4, 'Line ends in whitespace. Consider deleting these extra spaces.') # There are certain situations we allow one space, notably for labels elif ((initial_spaces == 1 or initial_spaces == 3) and not Match(r'\s*\w+\s*:\s*$', cleansed_line)): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') # Labels should always be indented at least one space. elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', line): error(filename, linenum, 'whitespace/labels', 4, 'Labels should always be indented at least one space. ' 'If this is a member-initializer list in a constructor or ' 'the base class list in a class definition, the colon should ' 'be on the following line.') # Check if the line is a header guard. is_header_guard = False if file_extension == 'h': cppvar = GetHeaderGuardCPPVariable(filename) if (line.startswith('#ifndef %s' % cppvar) or line.startswith('#define %s' % cppvar) or line.startswith('#endif // %s' % cppvar)): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. # # URLs can be long too. It's possible to split these, but it makes them # harder to cut&paste. # # The "$Id:...$" comment may also get very long without it being the # developers fault. if (not line.startswith('#include') and not is_header_guard and not Match(r'^\s*//.*http(s?)://\S*$', line) and not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) if line_width > 100: error(filename, linenum, 'whitespace/line_length', 4, 'Lines should very rarely be longer than 100 characters') elif line_width > 80: error(filename, linenum, 'whitespace/line_length', 2, 'Lines should be <= 80 characters long') if (cleansed_line.count(';') > 1 and # for loops are allowed two ;'s (and may run over two lines). cleansed_line.find('for') == -1 and (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and # It's ok to have many commands in a switch case that fits in 1 line not ((cleansed_line.find('case ') != -1 or cleansed_line.find('default:') != -1) and cleansed_line.find('break;') != -1)): error(filename, linenum, 'whitespace/newline', 0, 'More than one command on the same line') # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckEmptyLoopBody(filename, clean_lines, linenum, error) CheckAccess(filename, clean_lines, linenum, nesting_state, error) CheckSpacing(filename, clean_lines, linenum, nesting_state, error) CheckCheck(filename, clean_lines, linenum, error) CheckAltTokens(filename, clean_lines, linenum, error) classinfo = nesting_state.InnermostClass() if classinfo: CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) _RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') def _DropCommonSuffixes(filename): """Drops common suffixes like _test.cc or -inl.h from filename. For example: >>> _DropCommonSuffixes('foo/foo-inl.h') 'foo/foo' >>> _DropCommonSuffixes('foo/bar/foo.cc') 'foo/bar/foo' >>> _DropCommonSuffixes('foo/foo_internal.h') 'foo/foo' >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') 'foo/foo_unusualinternal' Args: filename: The input filename. Returns: The filename with the common suffix removed. """ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', 'inl.h', 'impl.h', 'internal.h'): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] return os.path.splitext(filename)[0] def _IsTestFilename(filename): """Determines if the given filename has a suffix that identifies it as a test. Args: filename: The input filename. Returns: True if 'filename' looks like a test, False otherwise. """ if (filename.endswith('_test.cc') or filename.endswith('_unittest.cc') or filename.endswith('_regtest.cc')): return True else: return False def _ClassifyInclude(fileinfo, include, is_system): """Figures out what kind of header 'include' is. Args: fileinfo: The current file cpplint is running over. A FileInfo instance. include: The path to a #included file. is_system: True if the #include used <> rather than "". Returns: One of the _XXX_HEADER constants. For example: >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) _C_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) _CPP_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) _LIKELY_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), ... 'bar/foo_other_ext.h', False) _POSSIBLE_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) _OTHER_HEADER """ # This is a list of all standard c++ header files, except # those already checked for above. is_stl_h = include in _STL_HEADERS is_cpp_h = is_stl_h or include in _CPP_HEADERS if is_system: if is_cpp_h: return _CPP_SYS_HEADER else: return _C_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include # lives in . , then it's likely to be owned by the target file. target_dir, target_base = ( os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) if target_base == include_base and ( include_dir == target_dir or include_dir == os.path.normpath(target_dir + '/../public')): return _LIKELY_MY_HEADER # If the target and include share some initial basename # component, it's possible the target is implementing the # include, so it's allowed to be first, but we'll never # complain if it's not there. target_first_component = _RE_FIRST_COMPONENT.match(target_base) include_first_component = _RE_FIRST_COMPONENT.match(include_base) if (target_first_component and include_first_component and target_first_component.group(0) == include_first_component.group(0)): return _POSSIBLE_MY_HEADER return _OTHER_HEADER def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): """Check rules that are applicable to #include lines. Strings on #include lines are NOT removed from elided line, to make certain tasks easier. However, to prevent false positives, checks applicable to #include lines in CheckLanguage must be put here. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ fileinfo = FileInfo(filename) line = clean_lines.lines[linenum] # "include" should use the new style "foo/bar.h" instead of just "bar.h" if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): error(filename, linenum, 'build/include', 4, 'Include the directory when naming .h files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's # not. match = _RE_PATTERN_INCLUDE.search(line) if match: include = match.group(2) is_system = (match.group(1) == '<') if include in include_state: error(filename, linenum, 'build/include', 4, '"%s" already included at %s:%s' % (include, filename, include_state[include])) else: include_state[include] = linenum # We want to ensure that headers appear in the right order: # 1) for foo.cc, foo.h (preferred location) # 2) c system files # 3) cpp system files # 4) for foo.cc, foo.h (deprecated location) # 5) other google headers # # We classify each include statement as one of those 5 types # using a number of techniques. The include_state object keeps # track of the highest type seen, and complains if we see a # lower type after that. error_message = include_state.CheckNextIncludeOrder( _ClassifyInclude(fileinfo, include, is_system)) if error_message: error(filename, linenum, 'build/include_order', 4, '%s. Should be: %s.h, c system, c++ system, other.' % (error_message, fileinfo.BaseName())) if not include_state.IsInAlphabeticalOrder(include): error(filename, linenum, 'build/include_alpha', 4, 'Include "%s" not in alphabetical order' % include) # Look for any of the stream classes that are part of standard C++. match = _RE_PATTERN_INCLUDE.match(line) if match: include = match.group(2) if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): # Many unit tests use cout, so we exempt them. if not _IsTestFilename(filename): error(filename, linenum, 'readability/streams', 3, 'Streams are highly discouraged.') def _GetTextInside(text, start_pattern): """Retrieves all the text between matching open and close parentheses. Given a string of lines and a regular expression string, retrieve all the text following the expression and between opening punctuation symbols like (, [, or {, and the matching close-punctuation symbol. This properly nested occurrences of the punctuations, so for the text like printf(a(), b(c())); a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. start_pattern must match string having an open punctuation symbol at the end. Args: text: The lines to extract text. Its comments and strings must be elided. It can be single line and can span multiple lines. start_pattern: The regexp string indicating where to start extracting the text. Returns: The extracted text. None if either the opening string or ending punctuation could not be found. """ # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably # rewritten to use _GetTextInside (and use inferior regexp matching today). # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} closing_punctuation = set(matching_punctuation.itervalues()) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) if not match: # start_pattern not found in text. return None start_position = match.end(0) assert start_position > 0, ( 'start_pattern must ends with an opening punctuation.') assert text[start_position - 1] in matching_punctuation, ( 'start_pattern must ends with an opening punctuation.') # Stack of closing punctuations we expect to have in text after position. punctuation_stack = [matching_punctuation[text[start_position - 1]]] position = start_position while punctuation_stack and position < len(text): if text[position] == punctuation_stack[-1]: punctuation_stack.pop() elif text[position] in closing_punctuation: # A closing punctuation without matching opening punctuations. return None elif text[position] in matching_punctuation: punctuation_stack.append(matching_punctuation[text[position]]) position += 1 if punctuation_stack: # Opening punctuations left without matching close-punctuations. return None # punctuations match. return text[start_position:position - 1] def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, error): """Checks rules from the 'C++ language rules' section of cppguide.html. Some of these rules are hard to test (function overloading, using uint32 inappropriately), but we do the best we can. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ # If the line is empty or consists of entirely a comment, no need to # check it. line = clean_lines.elided[linenum] if not line: return match = _RE_PATTERN_INCLUDE.search(line) if match: CheckIncludeLine(filename, clean_lines, linenum, include_state, error) return # Create an extended_line, which is the concatenation of the current and # next lines, for more effective checking of code that may span more than one # line. if linenum + 1 < clean_lines.NumLines(): extended_line = line + clean_lines.elided[linenum + 1] else: extended_line = line # Make Windows paths like Unix. fullname = os.path.abspath(filename).replace('\\', '/') # TODO(unknown): figure out if they're using default arguments in fn proto. # Check for non-const references in functions. This is tricky because & # is also used to take the address of something. We allow <> for templates, # (ignoring whatever is between the braces) and : for classes. # These are complicated re's. They try to capture the following: # paren (for fn-prototype start), typename, &, varname. For the const # version, we're willing for const to be before typename or after # Don't check the implementation on same line. fnline = line.split('{', 1)[0] if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', fnline))): # We allow non-const references in a few standard places, like functions # called "swap()" or iostream operators like "<<" or ">>". We also filter # out for loops, which lint otherwise mistakenly thinks are functions. if not Search( r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', fnline): error(filename, linenum, 'runtime/references', 2, 'Is this a non-const reference? ' 'If so, make const or use a pointer.') # Check to see if they're using an conversion function cast. # I just try to capture the most common basic types, though there are more. # Parameterless conversion functions, such as bool(), are allowed as they are # probably a member operator declaration or default constructor. match = Search( r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) if match: # gMock methods are defined using some variant of MOCK_METHODx(name, type) # where type may be float(), int(string), etc. Without context they are # virtually indistinguishable from int(x) casts. Likewise, gMock's # MockCallback takes a template parameter of the form return_type(arg_type), # which looks much like the cast we're trying to detect. if (match.group(1) is None and # If new operator, then this isn't a cast not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or Match(r'^\s*MockCallback<.*>', line))): # Try a bit harder to catch gmock lines: the only place where # something looks like an old-style cast is where we declare the # return type of the mocked method, and the only time when we # are missing context is if MOCK_METHOD was split across # multiple lines (for example http://go/hrfhr ), so we only need # to check the previous line for MOCK_METHOD. if (linenum == 0 or not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', clean_lines.elided[linenum - 1])): error(filename, linenum, 'readability/casting', 4, 'Using deprecated casting style. ' 'Use static_cast<%s>(...) instead' % match.group(2)) CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'static_cast', r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) # This doesn't catch all cases. Consider (const char * const)"hello". # # (char *) "foo" should always be a const_cast (reinterpret_cast won't # compile). if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): pass else: # Check pointer casts for other than string constants CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) # In addition, we look for people taking the address of a cast. This # is dangerous -- casts can assign to temporaries, so the pointer doesn't # point where you think. if Search( r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): error(filename, linenum, 'runtime/casting', 4, ('Are you taking an address of a cast? ' 'This is dangerous: could be a temp var. ' 'Take the address before doing the cast, rather than after')) # Check for people declaring static/global STL strings at the top level. # This is dangerous because the C++ language does not guarantee that # globals with constructors are initialized before the first access. match = Match( r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', line) # Make sure it's not a function. # Function template specialization looks like: "string foo(...". # Class template definitions look like: "string Foo::Method(...". if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3)): error(filename, linenum, 'runtime/string', 4, 'For a static/global string constant, use a C style string instead: ' '"%schar %s[]".' % (match.group(1), match.group(2))) # Check that we're not using RTTI outside of testing code. if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): error(filename, linenum, 'runtime/rtti', 5, 'Do not use dynamic_cast<>. If you need to cast within a class ' "hierarchy, use static_cast<> to upcast. Google doesn't support " 'RTTI.') if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): error(filename, linenum, 'runtime/init', 4, 'You seem to be initializing a member variable with itself.') if file_extension == 'h': # TODO(unknown): check that 1-arg constructors are explicit. # How to tell it's a constructor? # (handled in CheckForNonStandardConstructs for now) # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS # (level 1 error) pass # Check if people are using the verboten C basic types. The only exception # we regularly allow is "unsigned short port" for port. if Search(r'\bshort port\b', line): if not Search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') else: match = Search(r'\b(short|long(?! +double)|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) # When snprintf is used, the second argument shouldn't be a literal. match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) if match and match.group(2) != '0': # If 2nd arg is zero, snprintf is used to calculate size. error(filename, linenum, 'runtime/printf', 3, 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' 'to snprintf.' % (match.group(1), match.group(2))) # Check if some verboten C functions are being used. if Search(r'\bsprintf\b', line): error(filename, linenum, 'runtime/printf', 5, 'Never use sprintf. Use snprintf instead.') match = Search(r'\b(strcpy|strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Almost always, snprintf is better than %s' % match.group(1)) if Search(r'\bsscanf\b', line): error(filename, linenum, 'runtime/printf', 1, 'sscanf can be ok, but is slow and can overflow buffers.') # Check if some verboten operator overloading is going on # TODO(unknown): catch out-of-line unary operator&: # class X {}; # int operator&(const X& x) { return 42; } // unary operator& # The trick is it's hard to tell apart from binary operator&: # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& if Search(r'\boperator\s*&\s*\(\s*\)', line): error(filename, linenum, 'runtime/operator', 4, 'Unary operator& is dangerous. Do not use it.') # Check for suspicious usage of "if" like # } if (a == b) { if Search(r'\}\s*if\s*\(', line): error(filename, linenum, 'readability/braces', 4, 'Did you mean "else if"? If not, start a new line for "if".') # Check for potential format string bugs like printf(foo). # We constrain the pattern not to pick things like DocidForPrintf(foo). # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) # TODO(sugawarayu): Catch the following case. Need to change the calling # convention of the whole function to process multiple line to handle it. # printf( # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') if printf_args: match = Match(r'([\w.\->()]+)$', printf_args) if match and match.group(1) != '__VA_ARGS__': function_name = re.search(r'\b((?:string)?printf)\s*\(', line, re.I).group(1) error(filename, linenum, 'runtime/printf', 4, 'Potential format string bug. Do %s("%%s", %s) instead.' % (function_name, match.group(1))) # Check for potential memset bugs like memset(buf, sizeof(buf), 0). match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): error(filename, linenum, 'runtime/memset', 4, 'Did you mean "memset(%s, 0, %s)"?' % (match.group(1), match.group(2))) if Search(r'\busing namespace\b', line): error(filename, linenum, 'build/namespaces', 5, 'Do not use namespace using-directives. ' 'Use using-declarations instead.') # Detect variable-length arrays. match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) if (match and match.group(2) != 'return' and match.group(2) != 'delete' and match.group(3).find(']') == -1): # Split the size using space and arithmetic operators as delimiters. # If any of the resulting tokens are not compile time constants then # report the error. tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) is_const = True skip_next = False for tok in tokens: if skip_next: skip_next = False continue if Search(r'sizeof\(.+\)', tok): continue if Search(r'arraysize\(\w+\)', tok): continue tok = tok.lstrip('(') tok = tok.rstrip(')') if not tok: continue if Match(r'\d+', tok): continue if Match(r'0[xX][0-9a-fA-F]+', tok): continue if Match(r'k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. if tok.startswith('sizeof'): skip_next = True continue is_const = False break if not is_const: error(filename, linenum, 'runtime/arrays', 1, 'Do not use variable-length arrays. Use an appropriately named ' "('k' followed by CamelCase) compile-time constant for the size.") # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing # in the class declaration. match = Match( (r'\s*' r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' r'\(.*\);$'), line) if match and linenum + 1 < clean_lines.NumLines(): next_line = clean_lines.elided[linenum + 1] # We allow some, but not all, declarations of variables to be present # in the statement that defines the class. The [\w\*,\s]* fragment of # the regular expression below allows users to declare instances of # the class or pointers to instances, but not less common types such # as function pointers or arrays. It's a tradeoff between allowing # reasonable code and avoiding trying to parse more C++ using regexps. if not Search(r'^\s*}[\w\*,\s]*;', next_line): error(filename, linenum, 'readability/constructors', 3, match.group(1) + ' should be the last thing in the class') # Check for use of unnamed namespaces in header files. Registration # macros are typically OK, so we allow use of "namespace {" on lines # that end with backslashes. if (file_extension == 'h' and Search(r'\bnamespace\s*{', line) and line[-1] != '\\'): error(filename, linenum, 'build/namespaces', 4, 'Do not use unnamed namespaces in header files. See ' 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, error): """Checks for a C-style cast by looking for the pattern. This also handles sizeof(type) warnings, due to similarity of content. Args: filename: The name of the current file. linenum: The number of the line to check. line: The line of code to check. raw_line: The raw line of code to check, with comments. cast_type: The string for the C++ cast to recommend. This is either reinterpret_cast, static_cast, or const_cast, depending. pattern: The regular expression used to find C-style casts. error: The function to call with any errors found. Returns: True if an error was emitted. False otherwise. """ match = Search(pattern, line) if not match: return False # e.g., sizeof(int) sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) if sizeof_match: error(filename, linenum, 'runtime/sizeof', 1, 'Using sizeof(type). Use sizeof(varname) instead if possible') return True # operator++(int) and operator--(int) if (line[0:match.start(1) - 1].endswith(' operator++') or line[0:match.start(1) - 1].endswith(' operator--')): return False remainder = line[match.end(0):] # The close paren is for function pointers as arguments to a function. # eg, void foo(void (*bar)(int)); # The semicolon check is a more basic function check; also possibly a # function pointer typedef. # eg, void foo(int); or void foo(int) const; # The equals check is for function pointer assignment. # eg, void *(*foo)(int) = ... # The > is for MockCallback<...> ... # # Right now, this will only catch cases where there's a single argument, and # it's unnamed. It should probably be expanded to check for multiple # arguments with some unnamed. function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) if function_match: if (not function_match.group(3) or function_match.group(3) == ';' or ('MockCallback<' not in raw_line and '/*' not in raw_line)): error(filename, linenum, 'readability/function', 3, 'All parameters should be named in a function') return True # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, 'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1))) return True _HEADERS_CONTAINING_TEMPLATES = ( ('', ('deque',)), ('', ('unary_function', 'binary_function', 'plus', 'minus', 'multiplies', 'divides', 'modulus', 'negate', 'equal_to', 'not_equal_to', 'greater', 'less', 'greater_equal', 'less_equal', 'logical_and', 'logical_or', 'logical_not', 'unary_negate', 'not1', 'binary_negate', 'not2', 'bind1st', 'bind2nd', 'pointer_to_unary_function', 'pointer_to_binary_function', 'ptr_fun', 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', 'mem_fun_ref_t', 'const_mem_fun_t', 'const_mem_fun1_t', 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', 'mem_fun_ref', )), ('', ('numeric_limits',)), ('', ('list',)), ('', ('map', 'multimap',)), ('', ('allocator',)), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('pair',)), ('', ('vector',)), # gcc extensions. # Note: std::hash is their hash, ::hash is our hash ('', ('hash_map', 'hash_multimap',)), ('', ('hash_set', 'hash_multiset',)), ('', ('slist',)), ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') _re_pattern_algorithm_header = [] for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', 'transform'): # Match max(..., ...), max(..., ...), but not foo->max, foo.max or # type::max(). _re_pattern_algorithm_header.append( (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), _template, '')) _re_pattern_templates = [] for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: for _template in _templates: _re_pattern_templates.append( (re.compile(r'(\<|\b)' + _template + r'\s*\<'), _template + '<>', _header)) def FilesBelongToSameModule(filename_cc, filename_h): """Check if these two filenames belong to the same module. The concept of a 'module' here is a as follows: foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the same 'module' if they are in the same directory. some/path/public/xyzzy and some/path/internal/xyzzy are also considered to belong to the same module here. If the filename_cc contains a longer path than the filename_h, for example, '/absolute/path/to/base/sysinfo.cc', and this file would include 'base/sysinfo.h', this function also produces the prefix needed to open the header. This is used by the caller of this function to more robustly open the header file. We don't have access to the real include paths in this context, so we need this guesswork here. Known bugs: tools/base/bar.cc and base/bar.h belong to the same module according to this implementation. Because of this, this function gives some false positives. This should be sufficiently rare in practice. Args: filename_cc: is the path for the .cc file filename_h: is the path for the header path Returns: Tuple with a bool and a string: bool: True if filename_cc and filename_h belong to the same module. string: the additional prefix needed to open the header file. """ if not filename_cc.endswith('.cc'): return (False, '') filename_cc = filename_cc[:-len('.cc')] if filename_cc.endswith('_unittest'): filename_cc = filename_cc[:-len('_unittest')] elif filename_cc.endswith('_test'): filename_cc = filename_cc[:-len('_test')] filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') if not filename_h.endswith('.h'): return (False, '') filename_h = filename_h[:-len('.h')] if filename_h.endswith('-inl'): filename_h = filename_h[:-len('-inl')] filename_h = filename_h.replace('/public/', '/') filename_h = filename_h.replace('/internal/', '/') files_belong_to_same_module = filename_cc.endswith(filename_h) common_path = '' if files_belong_to_same_module: common_path = filename_cc[:-len(filename_h)] return files_belong_to_same_module, common_path def UpdateIncludeState(filename, include_state, io=codecs): """Fill up the include_state with new includes found from the file. Args: filename: the name of the header to read. include_state: an _IncludeState instance in which the headers are inserted. io: The io factory to use to read the file. Provided for testability. Returns: True if a header was succesfully added. False otherwise. """ headerfile = None try: headerfile = io.open(filename, 'r', 'utf8', 'replace') except IOError: return False linenum = 0 for line in headerfile: linenum += 1 clean_line = CleanseComments(line) match = _RE_PATTERN_INCLUDE.search(clean_line) if match: include = match.group(2) # The value formatting is cute, but not really used right now. # What matters here is that the key is in include_state. include_state.setdefault(include, '%s:%d' % (filename, linenum)) return True def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs): """Reports for missing stl includes. This function will output warnings to make sure you are including the headers necessary for the stl containers and functions that you use. We only give one reason to include a header. For example, if you use both equal_to<> and less<> in a .h file, only one (the latter in the file) of these will be reported as a reason to include the . Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. include_state: An _IncludeState instance. error: The function to call with any errors found. io: The IO factory to use to read the header file. Provided for unittest injection. """ required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } for linenum in xrange(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue # String is special -- it is a non-templatized type in STL. matched = _RE_PATTERN_STRING.search(line) if matched: # Don't warn about strings in non-STL namespaces: # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): required[''] = (linenum, 'string') for pattern, template, header in _re_pattern_algorithm_header: if pattern.search(line): required[header] = (linenum, template) # The following function is just a speed up, no semantics are changed. if not '<' in line: # Reduces the cpu time usage by skipping lines. continue for pattern, template, header in _re_pattern_templates: if pattern.search(line): required[header] = (linenum, template) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. # Let's copy the include_state so it is only messed up within this function. include_state = include_state.copy() # Did we find the header for this file (if any) and succesfully load it? header_found = False # Use the absolute path so that matching works properly. abs_filename = FileInfo(filename).FullName() # For Emacs's flymake. # If cpplint is invoked from Emacs's flymake, a temporary file is generated # by flymake and that file name might end with '_flymake.cc'. In that case, # restore original file name here so that the corresponding header file can be # found. # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' # instead of 'foo_flymake.h' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) # include_state is modified during iteration, so we iterate over a copy of # the keys. header_keys = include_state.keys() for header in header_keys: (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) fullpath = common_path + header if same_module and UpdateIncludeState(fullpath, include_state, io): header_found = True # If we can't find the header file for a .cc, assume it's because we don't # know where to look. In that case we'll give up as we're not sure they # didn't include it in the .h file. # TODO(unknown): Do a better job of finding .h files so we are confident that # not having the .h file means there isn't one. if filename.endswith('.cc') and not header_found: return # All the lines have been processed, report the errors found. for required_header_unstripped in required: template = required[required_header_unstripped][1] if required_header_unstripped.strip('<>"') not in include_state: error(filename, required[required_header_unstripped][0], 'build/include_what_you_use', 4, 'Add #include ' + required_header_unstripped + ' for ' + template) _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): """Check that make_pair's template arguments are deduced. G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are specified explicitly, and such use isn't intended in any case. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) if match: error(filename, linenum, 'build/explicit_make_pair', 4, # 4 = high confidence 'For C++11-compatibility, omit template arguments from make_pair' ' OR use pair directly OR if appropriate, construct a pair directly') def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions=[]): """Processes a single line in the file. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. clean_lines: An array of strings, each representing a line of the file, with comments stripped. line: Number of line being processed. include_state: An _IncludeState instance in which the headers are inserted. function_state: A _FunctionState instance which counts function lines, etc. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ raw_lines = clean_lines.raw_lines ParseNolintSuppressions(filename, raw_lines[line], line, error) nesting_state.Update(filename, clean_lines, line, error) if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: return CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) CheckLanguage(filename, clean_lines, line, file_extension, include_state, error) CheckForNonStandardConstructs(filename, clean_lines, line, nesting_state, error) CheckPosixThreading(filename, clean_lines, line, error) CheckInvalidIncrement(filename, clean_lines, line, error) CheckMakePairUsesDeduction(filename, clean_lines, line, error) for check_fn in extra_check_functions: check_fn(filename, clean_lines, line, error) def ProcessFileData(filename, file_extension, lines, error, extra_check_functions=[]): """Performs lint checks and reports any errors to the given error function. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. lines: An array of strings, each representing a line of the file, with the last element being empty if the file is terminated with a newline. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) include_state = _IncludeState() function_state = _FunctionState() nesting_state = _NestingState() ResetNolintSuppressions() CheckForCopyright(filename, lines, error) if file_extension == 'h': CheckForHeaderGuard(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines) for line in xrange(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions) nesting_state.CheckClassFinished(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. CheckForUnicodeReplacementCharacters(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error) def ProcessFile(filename, vlevel, extra_check_functions=[]): """Does google-lint on a single file. Args: filename: The name of the file to parse. vlevel: The level of errors to report. Every error of confidence >= verbose_level will be reported. 0 is a good default. extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ _SetVerboseLevel(vlevel) try: # Support the UNIX convention of using "-" for stdin. Note that # we are not opening the file with universal newline support # (which codecs doesn't support anyway), so the resulting lines do # contain trailing '\r' characters if we are reading a file that # has CRLF endings. # If after the split a trailing '\r' is present, it is removed # below. If it is not expected to be present (i.e. os.linesep != # '\r\n' as in Windows), a warning is issued below if this file # is processed. if filename == '-': lines = codecs.StreamReaderWriter(sys.stdin, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace').read().split('\n') else: lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') carriage_return_found = False # Remove trailing '\r'. for linenum in range(len(lines)): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') carriage_return_found = True except IOError: sys.stderr.write( "Skipping input '%s': Can't open for reading\n" % filename) return # Note, if no dot is found, this will give the entire filename as the ext. file_extension = filename[filename.rfind('.') + 1:] # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in ('cc', 'h', 'cpp', 'hpp'): sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) if carriage_return_found and os.linesep != '\r\n': # Use 0 for linenum since outputting only one error for potentially # several lines. Error(filename, 0, 'whitespace/newline', 1, 'One or more unexpected \\r (^M) found;' 'better to use only a \\n') sys.stderr.write('Done processing %s\n' % filename) def PrintUsage(message): """Prints a brief usage string and exits, optionally with an error message. Args: message: The optional error message. """ sys.stderr.write(_USAGE) if message: sys.exit('\nFATAL ERROR: ' + message) else: sys.exit(1) def PrintCategories(): """Prints a list of all the error-categories used by error messages. These are the categories used to filter messages via --filter. """ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) sys.exit(0) def ParseArguments(args): """Parses the command line arguments. This may set the output format and verbosity level as side-effects. Args: args: The command line arguments: Returns: The list of filenames to lint. """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', 'root=']) except getopt.GetoptError: PrintUsage('Invalid arguments.') verbosity = _VerboseLevel() output_format = _OutputFormat() filters = '' counting_style = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) elif opt == '--output': if not val in ('emacs', 'vs7', 'eclipse'): PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') output_format = val elif opt == '--verbose': verbosity = int(val) elif opt == '--filter': filters = val if not filters: PrintCategories() elif opt == '--counting': if val not in ('total', 'toplevel', 'detailed'): PrintUsage('Valid counting options are total, toplevel, and detailed') counting_style = val elif opt == '--root': global _root _root = val if not filenames: PrintUsage('No files were specified.') _SetOutputFormat(output_format) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) return filenames def main(): filenames = ParseArguments(sys.argv[1:]) # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. sys.stderr = codecs.StreamReaderWriter(sys.stderr, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) _cpplint_state.PrintErrorCounts() sys.exit(_cpplint_state.error_count > 0) if __name__ == '__main__': main() sipp-3.7.2/docker/0000775000000000000000000000000014525516253010645 5ustar sipp-3.7.2/docker/Dockerfile0000664000000000000000000000033314525516253012636 0ustar FROM alpine:3.10 RUN apk add --no-cache binutils make cmake gcc g++ ncurses-static libpcap-dev ncurses-dev gsl-dev git CMD cd /src && rm -f CMakeCache.txt && cmake . -DBUILD_STATIC=1 -DUSE_PCAP=1 -DUSE_GSL=1 && make sipp-3.7.2/docker/Dockerfile.full0000664000000000000000000000111114525516253013572 0ustar FROM alpine:3.13 RUN apk add --no-cache \ binutils \ make \ cmake \ gcc \ g++ \ git \ ncurses-dev \ ncurses-static \ libpcap-dev \ gsl-dev \ gsl-static \ openssl-dev \ openssl-libs-static \ linux-headers \ lksctp-tools-dev \ lksctp-tools-static CMD cd /src \ && rm -f CMakeCache.txt \ && cmake . -DBUILD_STATIC=1 -DUSE_PCAP=1 -DUSE_GSL=1 -DUSE_SSL=1 -DUSE_SCTP=1 \ && make # Usage (from within the git repo): # git submodule update --init # docker build -t sipp-build -f docker/Dockerfile.full docker # docker run -v $PWD:/src sipp-build sipp-3.7.2/docs/0000775000000000000000000000000014525516253010326 5ustar sipp-3.7.2/docs/3PCC_extended.rst0000664000000000000000000000364714525516253013442 0ustar 3PCC Extended ============= An extension of the 3pcc mode is implemented in SIPp. This feature allows any number of SIPp instances to communicate with each other, each one of them being connected to a remote host. The SIPp instance which initiates the call is launched in "master" mode. The others are launched in "slave" mode. Slave SIPp instances have names, given in the command line (for example, s1, s2...sN for the slaves and m for the master) Correspondances between instances names and their addresses must be stored in a file (provided by ``-slave_cfg`` command line argument), in the following format:: s1;127.0.0.1:8080 s2;127.0.0.1:7080 m;127.0.0.1:6080 Each SIPp instance must access a different copy of this file. sendCmd and recvCmd have additional attributes:: Will send a command to the "s1" peer instance, which can be either master or slave, depending on the command line argument, which must be consistent with the scenario: a slave instance cannot have a sendCmd action before having any recvCmd. Note that the message must contain a "From" field, filled with the name of the sender. :: Indicates that the twin command is expected to be received from the "m" peer instance. Note that the master must be the launched at last. There is no integrated scenarios for the 3pcc extended mode, but you can easily adapt those from 3pcc. **Example:** the following drawing illustrate the entire procedure. The arrows that are shown between SIPp master and slaves depict only the synchronization commands exchanged between the different SIPp instances. The SIP message exchange takes place as usual. .. image:: master_slave.png sipp-3.7.2/docs/3pcc-A.xml0000664000000000000000000000731414525516253012063 0ustar Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.7.2/docs/3pcc-B.xml0000664000000000000000000000727614525516253012073 0ustar Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.7.2/docs/3pcc-C-A.xml0000664000000000000000000001144614525516253012244 0ustar ;tag=[pid]SIPpTag03[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag03[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$2] ]]> ;tag=[pid]SIPpTag03[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.7.2/docs/3pcc-C-B.xml0000664000000000000000000001144414525516253012243 0ustar ;tag=[pid]SIPpTag04[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$1] ]]> ;tag=[pid]SIPpTag04[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag04[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.7.2/docs/_static/0000775000000000000000000000000014525516253011754 5ustar sipp-3.7.2/docs/_static/theme_overrides.css0000664000000000000000000000344614525516253015661 0ustar /* -*- coding: utf-8; mode: css -*- * * Sphinx HTML theme customization: read the doc * */ @media screen { /* content column * * RTD theme's default is 800px as max width for the content, but we have * tables with tons of columns, which need the full width of the view-port. */ .wy-nav-content{max-width: none; } /* table: * * - Sequences of whitespace should collapse into a single whitespace. * - make the overflow auto (scrollbar if needed) * - align caption "left" ("center" is unsuitable on vast tables) */ .wy-table-responsive table td { white-space: normal; } .wy-table-responsive { overflow: auto; } .rst-content table.docutils caption { text-align: left; font-size: 100%; } /* captions: * * - captions should have 100% (not 85%) font size * - hide the permalink symbol as long as link is not hovered */ .toc-title { font-size: 150%; font-weight: bold; } caption, .wy-table caption, .rst-content table.field-list caption { font-size: 100%; } caption a.headerlink { opacity: 0; } caption a.headerlink:hover { opacity: 1; } /* Menu selection and keystrokes */ span.menuselection { color: blue; font-family: monospace, "Courier New", Courier } code.kbd, code.kbd span { color: white; background-color: darkblue; font-weight: bold; font-family: monospace, "Courier New", Courier } /* inline literal: drop the borderbox, padding and red color */ code, .rst-content tt, .rst-content code { color: inherit; border: none; padding: unset; background: inherit; font-size: 85%; } .rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal { color: inherit; } } sipp-3.7.2/docs/beep_1sec_50x160b.g7220000664000000000000000000001750014525516253013647 0ustar ޛ7$ f2SKMjio]АzkoySRunw\TٛnntQtnn7SSZzpwSВ[x򰮳uw^RՓmv{ؐ޷mTnu.wۿUY[/qXW]qr7\QVtq>W}~0zUqzW=roj~SږykrtTR|Xo[]SVp1Y~XWYzSۚ4vry3Ζ_?^wzO{ښޮ20X{quZZ3yW~4{tqu[9]\/ r~s\{Tt]{}}}|{{޹{߽zyw}~wz^zݟ|v_t]{ttwxruxtysty׸tv߷yz{{xwyx{vvۻܻw|ߵtwwuyxs{{wyvv|vtuvtwxux޻߻v߸x߾rzzz{tx\t|wx|tyt{{yt{ڸuwrߙt[w޸w{ߵx_xy~z~p.oӠ `nQMT\}vn-u}\ԕTtsw<ՔV[|.xVUԓYoq|XYorqxXUTUtm|zXWosy[X՗rn}]Rܘsr[ؗvpm4xWӗ]{n1ؓT[,}UWZos^֝~jku~ҙؓ޴qnp\[13ںTY]pnzTuPvsoxr\muY~]sZ{WxVyvsVnovWx|p{Tr^\R]|tx]\Sxq__TuzrwuT*X90w~Y]yZS>tzY]:ӱssrV]l{xWZ֖zm8}V|ٻnwwXS~Ytlt}^tro|TT{tn~_/lw9YR]1Xt}_}8\rp]zS~wrt]Wܝpw4X1ε6`wrZW_p[t^z||޻~޼~z_}}|~޺|z||xsu~v߳vxߝvz{zy[\wxuxu{X۳{x{x{_tv[xw~xwy|~{uyy״x߶yuxu~vy~vsyZvߘsw7:|xv{{s{{ysvvz\vpxڴpxxxttyss{ܙ|5rwxwvwxvt_z_t~{y{ytx{{{ּvܸvwvzzwyxw|w\yuvsy{xwW޴sݛzz~|zvzߵ_wzvyqyuzywwy{ٹu\ִ7Xzv|{yyZvwu{v{yxXߴttضwXxxszwvzzxxyu|\yz{yݚݵxx|x|xy]ܳuޜtyz6ttzuZ|;ھx~{ߵ;wyWv۹{yst{xuzy|qvvuuuu[|wxyxwwxt{صxڵ6ߜ\ܲquyyxywxzٸzzx|\w~~wx|ywwx||[u|{uyxx~ruuܵx|w{\\|ܷxuyyܺ~]|uxyۻu{x״psuuYxڶ{tzvyyyut{sy\Xy]~w{w۴qz\wuzxutZys^tw|[yٶx|ںxYsxv[wuyuuu~ztzxv\twt|{{w|\x~xxt|~9{xtxruڹuvvxxxvv~yx~wxxyqx{^z|~߸]{[x{X~xyw|7ݵwWuwv|~Ywyxxt|ڶ_w~]x{]zwy]^ޘys^vu{^zzs:tuxx{tu[y߸xwuwuywu|v~vֹvXwyZu{ܻuxxwܼ_yxv~x~]r5y{w~tywyxڞxzvv|V{ssx۟7tws\xXw[sx\ry]ܾwm8@gEUfSZʥV95?ojVG5XU[\g"/0A{^Y $qqXU[\rE@#UIkW@L.*>LH_a$_?  4X3>D GBtbAZXh1te V ~ٿl 'Yv34b@:xJh`*նi.&bQEeUUUU VEYֳL{Jڨ(6b!s}~c1U:fOE՝u[qڦR"9Mw;XY[lr"Aړ]뗡QT'Jw4ɦw';|Y[Lr"?@'P`]QP'KݳRid QtY*3334"5|!qSP^72Np(J 7Ar!ZWoqD)>BJhp6+ZﱚŃnbAMe2E ,;0pPzh`V$j}R$D`Xb1BI:+:{>8FOzh7`lsզJbE`XRjD)C<-ߐԽ٦h7pWԫXhi@)(66Aʖa.4槞Jh:&`o8mmo$r fȚ"QL*?ހL7`lFϵ[|Д|N#PjI'd6B[.Ozdp|Zۛrp4ܷ g2)a ϧgdöЃx`>K_@;%L@ZBGUU} zdp(=f* 8B1@$ BAr;k #F@h&`x]1|P,AfbA+E. ع~;rXNOzh72s]lq@JJha D8[L7`>z)^g1gf+Mp\G%d(@h7pKZ\[m![m pp!;63gPJh7`Ck~\FyE$HeRPeZZ16Bȿ{RGRh7pu1;?r(BXRj&UC0[Br .1zh7`2cNqw]ʷI ,C)@(l;o;~Ozh7p@ ֺaj$hD! >f1:?xzh7`Kw*mR( MY%)i1h$S,n"W  h&pm{ͩ-UQUm\Y\ @q=յer% )܀Jh7pvZ]71eyURQj)T*w^B$UGh7pB[L9)A_X6e+Jʱ;5` {)k_r, wUEeUAdPD'e=?@R3zc0lqWٳe)F`')iMcbavsipp-3.7.2/docs/branchc.xml0000664000000000000000000001104514525516253012451 0ustar ;tag=[pid]SIPpTag07[call_number] To: ua1 Call-ID: [call_id] CSeq: 1 REGISTER Contact: sip:ua1@[local_ip]:[local_port] Content-Length: 0 Expires: 300 ]]> sipp-3.7.2/docs/branchs.xml0000664000000000000000000001055114525516253012472 0ustar Content-Length: 0 Expires: 300 ]]> Content-Length: 0 ]]> Content-Length: 0 ]]> sipp-3.7.2/docs/conf.py0000664000000000000000000001145314525516253011631 0ustar # -*- coding: utf-8 -*- # # SIPp documentation build configuration file, created by # sphinx-quickstart on Tue Dec 27 16:20:26 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['rstFlatTable'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'SIPp' copyright = u'2019, SIPp community' author = u'SIPp developer community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'3.6' # The full version, including alpha/beta/rc tags. release = u'3.6' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # #html_theme = 'alabaster' html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] def setup(app): app.add_stylesheet('theme_overrides.css') #html_context = { # 'css_files': [ # '_static/theme_overrides.css', # ], #} # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'SIPpdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'SIPp.tex', u'SIPp Documentation', u'SIPp community', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'sipp', u'SIPp Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'SIPp', u'SIPp Documentation', author, 'SIPp', 'One line description of project.', 'Miscellaneous'), ] highlight_language = 'XML' sipp-3.7.2/docs/controlling.rst0000664000000000000000000001270114525516253013413 0ustar Controlling SIPp ================ SIPp can be controlled interactively through the keyboard or via a UDP socket. SIPp supports both 'hot' keys that can be entered at any time and also a simple command mode. The hot keys are: ===== ====== Key Action ===== ====== \+ Increase the call rate by 1 * rate_scale \* Increase the call rate by 10 * rate_scale \- Decrease the call rate by 1 * rate_scale / Decrease the call rate by 10 * rate_scale c Enter command mode q Quit SIPp (after all calls complete, enter a second time to quit immediately) Q Quit SIPp immediately s Dump screens to the log file (if -trace_screen is passed) p Pause traffic 1 Display the scenario screen 2 Display the statistics screen 3 Display the repartition screen 4 Display the variable screen 5 Display the TDM screen 6-9 Display the second through fifth repartition screen. ===== ====== In command mode, you can type a single line command that instructs SIPp to take some action. Command mode is more versatile than the hot keys, but takes more time to input some common actions. The following commands are available: List of Interactive Commands ```````````````````````````` - ``dump tasks`` Prints a list of active tasks (most tasks are calls) to the error log. dump tasks - ``set rate X`` Sets the call rate. set rate 10 - ``set rate-scale X`` Sets the rate scale, which adjusts the speed of '+', '-', '*', and '/'. set rate-scale 10 - ``set users X`` Sets the number of users (only valid when -users is specified). set rate 10 - ``set limit X`` Sets the open call limit (equivalent to -l option) set limit 100 - ``set hide `` Should the hide XML attribute be respected? set hide false - ``set display `` Changes the scenario that is displayed to either the main or the out-of-call scenario. set display main set display ooc - ``trace `` Turns log on or off at run time. Valid values for log are "error", "logs", "messages", and "shortmessages". trace error on Traffic control ``````````````` SIPp generates SIP traffic according to the scenario specified. You can control the number of calls (scenario) that are started per second. If you pass the -users option, then you need to control the number of instantiated users. You can control the rate through: + Interactive hot keys (described in the previous section) + Interactive Commands + Startup Parameters There are two commands that control rates: set rate X sets the current call rate to X. Additionally, set rate-scale X sets the rate_scale parameter to X. This enables you to use the '+', '-', '*', and '/' keys to set the rate more quickly. For example, if you do set rate- scale 100, then each time you press '+', the call rate is increased by 100 calls and each time you press '*', the call rate is increased by 1000 calls. Similarly, for a user based benchmark you can run set users X. At starting time, you can control the rate by specifying parameters on the command line: + "-r" to specify the call rate in number of calls per seconds + "-rp" to specify the " r ate p eriod" in milliseconds for the call rate (default is 1000ms/1sec). This allows you to have n calls every m milliseconds (by using -r n -rp m). .. note:: Example: run SIPp at 7 calls every 2 seconds (3.5 calls per second) :: ./sipp -sn uac -r 7 -rp 2000 127.0.0.1 You can also pause the traffic by pressing the 'p' key. SIPp will stop placing new calls and wait until all current calls go to their end. You can resume the traffic by pressing 'p' again. To quit SIPp, press the 'q' key. SIPp will stop placing new calls and wait until all current calls go to their end. SIPp will then exit. You can also force SIPp to quit immediatly by pressing the 'Q' key. Current calls will be terminated by sending a BYE or CANCEL message (depending if the calls have been established or not). The same behaviour is obtained by pressing 'q' twice. .. tip:: You can place a defined number of calls and have SIPp exit when this is done. Use the -m option on the command line. Remote control `````````````` SIPp can be "remote-controlled" through a UDP socket. This allows for example + To automate a series of actions, like increasing the call rate smoothly, wait for 10 seconds, increase more, wait for 1 minute and loop + Have a feedback loop so that an application under test can remote control SIPp to lower the load, pause the traffic, ... Each SIPp instance is listening to a UDP socket. It starts to listen to port 8888 and each following SIPp instance (up to 60) will listen to base_port + 1 (8889, 8890, ...). It is then possible to control SIPp like this: :: echo p >/dev/udp/x.y.z.t/8888 -> put SIPp in pause state (p key) echo q >/dev/udp/x.y.z.t/8888 -> quit SIPp (q key) .. note:: All keys available through keyboard are also available in the remote control interface You could also have a small shell script to automate a serie of action. For example, this script will increase the call rate by 10 more new calls/s every 5 seconds, wait at this call rate for one minute and exit SIPp: :: #!/bin/sh echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 60 echo "q" >/dev/udp/127.0.0.1/8889 To send a command to SIPp, preface it with 'c'. For example: ``echo "cset rate 100" >/dev/udp/127.0.0.1/8888 sets the call rate to 100.`` sipp-3.7.2/docs/error.rst0000664000000000000000000001057214525516253012216 0ustar Error handling ============== SIPp has advanced feature to handle errors and unexpected events. They are detailed in the following sections. Unexpected messages ``````````````````` + When a SIP message that can be correlated to an existing call (with the Call-ID: header) but is not expected in the scenario is received, SIPp will send a CANCEL message if no 200 OK message has been received or a BYE message if a 200 OK message has been received. The call will be marked as failed. If the unexpected message is a 4XX or 5XX, SIPp will send an ACK to this message, close the call and mark the call as failed. + When a SIP message that can't be correlated to an existing call (with the Call-ID: header) is received, SIPp will send a BYE message. The call will not be counted at all. + When a SIP "PING" message is received, SIPp will send an ACK message in response. This message is not counted as being an unexpected message. But it is counted in the "AutoAnswered" statistic counter. + An unexpected message that is not a SIP message will be simply dropped. Retransmissions (UDP only) `````````````````````````` A retransmission mechanism exists in UDP transport mode. To activate the retransmission mechanism, the "send" command must include the "retrans" attribute. When it is activated and a SIP message is sent and no ACK or response is received in answer to this message, the message is re-sent. .. note:: The retransmission mechanism follows :RFC:`3261`, section 17.1.1.2. Retransmissions are differentiated between INVITE and non-INVITE methods. : will initiate the T1 timer to 500 milliseconds. Even if retrans is specified in your scenarios, you can override this by using the -nr command line option to globally disable the retransmission mechanism. Log files ````````` There are several ways to trace what is going on during your SIPp runs. + You can log sent and received SIP messages in __messages.log by using the command line parameter -trace_msg. The messages are time-stamped so that you can track them back. + You also can trace it using the -trace_shortmsg parameter. This logs the most important values of a message as CSV into one line of the __shortmessages.log + You can trace all unexpected messages or events in __errors.log by using the command line parameter -trace_err. + You can trace the SIP response codes of unexpected messages in __error_codes.log by using the command line parameter -trace_error_codes. + You can trace the counts from the main scenario screen in __counts.csv by using the command line parameter -trace_counts. + You can trace the messages and state transitions of failed calls in __calldebug.log using the -trace_calldebug command line parameter. This is useful, because it has less overhead than -trace_msg yet allows you to debug call flows that were not completed successfully. + You can save in a file the statistics screens, as displayed in the interface. This is especially useful when running SIPp in background mode. This can be done in different ways: + When SIPp exits to get a final status report (-trace_screen option) + On demand by using USR2 signal (example: kill -SIGUSR2 738) + By pressing 's' key (if -trace_screen option is set) + If the -trace_logs option is set, you can use the action to print some scenario traces in the __logs.log file. See the Log action section SIPp can treat the messages, short messages, logs, and error logs as ring buffers. This allows you to limit the total amount of space used by these log files and keep only the most recent messages. To set the maximum file size use the -ringbuffer_size option. Once the file exceeds this size (the file size can be exceeded up to the size of a single log message), it is rotated. SIPp can keep several of the most recent files, to specify the number of files to keep use the -ringbuffer_files option. The rotated files have a name of the form ___.log, where is the number of seconds since the epoch. If more than one log file is rotated during a one second period, then the date is expressed as , where serial is an increasing integer identifier. sipp-3.7.2/docs/foreword.rst0000664000000000000000000000211114525516253012702 0ustar Foreword ~~~~~~~~ Warning This version of the documentation is for SIPp |version| and describes some features not present in earlier versions. See the sidebar to access documentation for previous versions. SIPp is a performance testing tool for the SIP protocol. It includes a few basic SipStone user agent scenarios (UAC and UAS) and establishes and releases multiple calls with the INVITE and BYE methods. It can also reads XML scenario files describing any performance testing configuration. It features the dynamic display of statistics about running tests (call rate, round trip delay, and message statistics), periodic CSV statistics dumps, TCP and UDP over multiple sockets or multiplexed with retransmission management, regular expressions and variables in scenario files, and dynamically adjustable call rates. SIPp can be used to test many real SIP equipements like SIP proxies, B2BUAs, SIP media servers, SIP/x gateways, and SIP PBXes. It is also very useful to emulate thousands of user agents calling your SIP system. Want to see it? Here is a screenshot .. figure:: sipp-01.jpg sipp-3.7.2/docs/index.rst0000664000000000000000000000114714525516253012172 0ustar .. SIPp documentation master file, created by sphinx-quickstart on Tue Dec 27 16:20:26 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to SIPp reference documentation! ======================================== .. toctree:: :maxdepth: 3 :caption: Contents: foreword installation sipp int_scenarios scenarios/index 3PCC_extended controlling transport media statistics error perftest tools Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sipp-3.7.2/docs/installation.rst0000664000000000000000000000765514525516253013576 0ustar Installation ~~~~~~~~~~~~ Getting SIPp ```````````` SIPp is released under the `GNU GPL license`_. All the terms of the license apply. It was originally created and provided to the SIP community by `Hewlett-Packard`_ engineers in hope it can be useful, but HP does not provide any support nor warranty concerning SIPp. SIPp releases ````````````` Like many other "open source" projects, there are two versions of SIPp: a stable and unstable release. Stable release: before being labelled as "stable", a SIPp release is thoroughly tested. So you can be confident that all mentioned features will work :) .. note:: Use the stable release for your everyday use and if you are not blocked by a specific feature present in the "unstable release" (see below). `SIPp stable download page `_ Unstable release ```````````````` Unstable release: all new features and bug fixes are checked in `SIPp's master tree`_ repository as soon as they are available. .. note:: Use the unstable release if you absolutely need a bug fix or a feature that is not in the stable release. Available platforms ``````````````````` SIPp is available on Linux and Cygwin. Other Unix distributions are likely to work, but are not tested every release cycle. .. note:: SIPp on Cygwin works only on Windows XP and later versions and will not work on Win2000. This is because of IPv6 support. Installing SIPp ``````````````` + On Linux, SIPp is provided in the form of source code. You will need to compile SIPp to actually use it. + Pre-requisites to compile SIPp are: + C++ Compiler + curses or ncurses library + For TLS support: OpenSSL >= 0.9.8 or WolfSSL >= 3.15.0 + For pcap play support: libpcap and libnet + For SCTP support: lksctp-tools + For distributed pauses: `Gnu Scientific Libraries`_ + You have four options to compile SIPp: + Without TLS (Transport Layer Security), SCTP or PCAP support -- this is the recommended setup if you don't need to handle SCTP, TLS or PCAP:: tar -xvzf sipp-xxx.tar cd sipp cmake . make + With TLS support, you must have installed `OpenSSL library`_ (>=0.9.8) (which may come with your system) or `WolfSSL library`_ (>=3.15.0). Building SIPp consists only of adding the ``-DUSE_SSL=1`` option to the cmake command:: tar -xvzf sipp-xxx.tar.gz cd sipp cmake . -DUSE_SSL=1 make + With PCAP play support:: tar -xvzf sipp-xxx.tar.gz cd sipp cmake . -DUSE_PCAP=1 make + With SCTP support:: tar -xvzf sipp-xxx.tar.gz cd sipp cmake . -DUSE_SCTP=1 make + With support for statistically distributed pauses:: tar -xvzf sipp-xxx.tar.gz cd sipp cmake . -DUSE_GSL=1 make + You can also combine these various options, e.g.:: tar -xvzf sipp-xxx.tar.gz cd sipp cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL=1 -DUSE_SCTP=1 make .. warning:: SIPp compiles under CYGWIN on Windows, provided that you installed IPv6 extension for `CYGWIN `_, as well as libncurses and (optionally OpenSSL and WinPcap). SCTP is not currently supported. + To compile SIPp on Windows with pcap (media support), you must: + Copy the `WinPcap developer package`_ to "C:\cygwin\lib\WpdPack" + Remove or rename "pthread.h" in "C:\cygwin\lib\WpdPack\Include", as it interfers with pthread.h from cygwin + Compile according to the instructions above. .. _GNU GPL license: https://www.gnu.org/copyleft/gpl.html .. _Gnu Scientific Libraries: https://www.gnu.org/software/gsl/ .. _WinPcap developer package: https://www.winpcap.org/devel.htm .. _hewlett-packard: https://www.hp.com/ .. _SIPp's master tree: https://github.com/SIPp/sipp/tree/master .. _OpenSSL library: https://www.openssl.org/ .. _WolfSSL library: https://www.wolfssl.com/ sipp-3.7.2/docs/int_scenarios.rst0000664000000000000000000002150314525516253013721 0ustar Integrated scenarios ==================== Integrated scenarios? Yes, there are scenarios that are embedded in SIPp executable. While you can create your own custom SIP scenarios (see how to create your own XML scenarios), a few basic (yet useful) scenarios are available in SIPp executable. UAC ``` Scenario file: :download:`uac.xml ` :: SIPp UAC Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) PAUSE | | | |(7) BYE | |------------------>| |(8) 200 | |<------------------| UAC with media `````````````` Scenario file: :download:`uac_pcap.xml ` :: SIPp UAC Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) RTP send (8s) | |==================>| | | |(7) RFC2833 DIGIT 1| |==================>| | | |(8) BYE | |------------------>| |(9) 200 | |<------------------| UAS ``` Scenario file: :download:`uas.xml ` :: Remote SIPp UAS |(1) INVITE | |------------------>| |(2) 180 | |<------------------| |(3) 200 | |<------------------| |(4) ACK | |------------------>| | | |(5) PAUSE | | | |(6) BYE | |------------------>| |(7) 200 | |<------------------| regexp `````` Scenario file: :download:`regexp.xml ` This scenario, which behaves as an UAC is explained in greater details in this section. :: SIPp regexp Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) PAUSE | | | |(7) BYE | |------------------>| |(8) 200 | |<------------------| branch `````` Scenario files: :download:`branchc.xml ` and :download:`branchs.xml ` Those scenarios, which work against each other (branchc for client side and branchs for server side) are explained in greater details in this section. :: REGISTER ----------> 200 <---------- 200 <---------- INVITE ----------> 100 <---------- 180 <---------- 403 <---------- 200 <---------- ACK ----------> [ 5000 ms] BYE ----------> 200 <---------- UAC Out-of-call Messages ```````````````````````` Scenario file: :download:`ooc_default.xml ` When a SIPp UAC receives an out-of-call request, it instantiates an out-of-call scenario. By default this scenario simply replies with a 200 OK response. This scenario can be overridden by passing the -oocsf or -oocsn command line options. :: SIPp UAC Remote |(1) .* | |<------------------| |(2) 200 | |------------------>| 3PCC ```` 3PCC stands for 3rd Party Call Control. 3PCC is described in :RFC:`3725`. While this feature was first developed to allow 3PCC like scenarios, it can also be used for every case where you would need one SIPp to talk to several remotes. In order to keep SIPp simple (remember, it's a test tool!), one SIPp instance can only talk to one remote. Which is an issue in 3PCC call flows, like call flow I (SIPp being a controller):: A Controller B |(1) INVITE no SDP | | |<------------------| | |(2) 200 offer1 | | |------------------>| | | |(3) INVITE offer1 | | |------------------>| | |(4) 200 OK answer1 | | |<------------------| | |(5) ACK | | |------------------>| |(6) ACK answer1 | | |<------------------| | |(7) RTP | | |.......................................| Scenario file: :download:`3pcc-A.xml <3pcc-A.xml>` Scenario file: :download:`3pcc-B.xml <3pcc-B.xml>` Scenario file: :download:`3pcc-C-A.xml <3pcc-C-A.xml>` Scenario file: :download:`3pcc-C-B.xml <3pcc-C-B.xml>` The 3PCC feature in SIPp allows to have two SIPp instances launched and synchronised together. If we take the example of call flow I, one SIPp instance will take care of the dialog with remote A (this instance is called 3PCC-C-A for 3PCC-Controller-A-Side) and another SIPp instance will take care of the dialog with remote B (this instance is called 3PCC-C-B for 3PCC-Controller-B-Side). The 3PCC call flow I will, in reality, look like this (Controller has been divided in two SIPp instances):: A Controller A Controller B B |(1) INVITE no SDP | | | |<------------------| | | |(2) 200 offer1 | | | |------------------>| | | | sendCmd (offer1) | | | |----------------->| | | | recvCmd | | | |(3) INVITE offer1 | | | |------------------>| | | |(4) 200 OK answer1 | | | |<------------------| | | sendCmd | | | (answer1) | | | |<-----------------| | | recvCmd |(5) ACK | | | |------------------>| |(6) ACK answer1 | | | |<------------------| | | |(7) RTP | | | |..........................................................| As you can see, we need to pass information between both sides of the controller. SDP "offer1" is provided by A in message (2) and needs to be sent to B side in message (3). This mechanism is implemented in the scenarios through the command. This:: Will send a "command" to the twin SIPp instance. Note that including the Call-ID is mandatory in order to correlate the commands to actual calls. In the same manner, this:: Will receive a "command" from the twin SIPp instance. Using the regular expression mechanism, the content is retrieved and stored in a call variable ($2 in this case), ready to be reinjected:: ;tag=[call_number] To: sut [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$2] ]]> In other words, sendCmd and recvCmd can be seen as synchronization points between two SIPp instances, with the ability to pass parameters between each other. Another scenario that has been reported to be do-able with the 3PCC feature is the following: + A calls B. B answers. B and A converse + B calls C. C answers. C and B converse + B "REFER"s A to C and asks to replace A-B call with B-C call. + A accepts. A and C talk. B drops out of the calls. sipp-3.7.2/docs/master_slave.png0000664000000000000000000014217014525516253013526 0ustar PNG  IHDRfșmsBIT|d pHYs ptEXtSoftwarewww.inkscape.org< IDATxwXW3"HQE,{キ؍QOcb4&4M4@QP7A"*=>ݕ3 ADT~ϙYMDe 3G1pcWrAD-|#߀ ̼%_\`3s˒ίP4T/{¦TkŒd!ė=3D6Gz!@ Psc׶001 @@s_Q]f|MCA/Eh ;I/: 0g S .tlu8a|EסzY;=u,g%֬d_HTR&TK%Q'޽OJ{]ADq1`̃r @c7!3x?ykZ~kNcQq9*+ְ^iELHR4Ɉ H+%"y `#3%f2s{hXy0>"j<3ѾG0fN&Z0: <Qg. ױʂU'Q s F嬬MlutE\ۊZ-eE$G%'œz^<÷U'9~&ϼg,  0rxf83ADh`1.Q=f(`-u>ِȀk5p @E KIl*3Q'j >|c|9C'm!̼;QS&-o>u+J;2rsgYC.Sk mQt1?TlT!j?3LNi0N 5 @p2SAޙDmyfޫ]sq59sX_jQws3g][!IvU*WKPPQva@veCDDtKSfsٍ_b~AVeff՚vtD~ P -TFv:8v)=t.䶐ݵe!!qA6`9Y&3pNH <r8@,xkfvdfGȞ -MQgskBYu#0@wG!k"v]lp33OQ/K! &FQ ߡ+^s7}>™Տ?^7ix/s џz%Y&F"$H&bW,ɘCvB/YJJe:մq%7egBְ1nbdgz}0s(3> TA6ķi@1D"m/r\Ȼc@A_0lxoa> zDT=HDӴ e3_c\)Z6CD rb(9\y13pK1$ ßWr[#t?8{ᤫ#ɢjΖlhW?z%n6o"|oyK8S5l -D`deg\yg5``RA#Iowg#C|@h yr&K!c@hf.A_`3@)_Νyw]+||Pffڧ$k}Uy׾wdϐ߾J`ozkJiXώ܍ތ`_ֹ* |s; 44jPG ˂@L򵗅Ȩdַcr(~>oS<5PRrzwmZ:2lKU*Ŷ]~ԷG n>7?|A[ 2pb0ulT%+ ZOrhpx}1ľ194l7جMPIv 17TƼ ƦnUR 00ҁ34t.7#``s"|kGU f-9Z1sWkIozHwe_wEHY=р[JfS~u-bRrڶ+?!pS}) f 9F~Sܺӗ`+¨w @rJظT\ F 3\{OGf9amz1-]'I2-ݨAyq3 ̉-*to i$\8zVCx:\{y>/q@jŔ7Tv,-va?5Fja%""s#VQ"Zǜ̡Ŷ""" {C-i W'O?$||5aea}>iĞ335n9Aݐt-kS2BySTj&ƺWǖ!gj;[r˟xVҳAs˔ zIp3o$a`*.>f "Tr0[MK YBN`.]Z{kn03F6!&>!v]-o_MA4yJD,n0}"-Ц3{1Q鈰d_&"~α? 3:[0+6CܼIEN{Au}0V70BF*feՃ4g>꠪}$ N`4]NxQ 1%,w:wD65C:rՀ T%[kܽCYnIfFG[7(ߥf+g^sw[A$ _UaZOclR7+Ǜf*}dkɺ!$~ԞCE/@=Wcӡ)I*Zh7)Ԯ'2emTyQt#-Hu?M:mi|{~!T |I0s&Vw_B 7Qxym0wMmӸ.>x6PckZܾ" Ci)T˞/N~|IIJn/A#[sjrvw\ޠw/rw}DkIèEbG҇3y}?5RO}^fij٣?ocuBf뷮e,sb^!֐Z.01Ν$̰4d+KC\6]RDxnU(k eǖ1h HAu [ RtL ~G\jq)vpeKp7׵U.R,G!y'Eמկwx m~J$J̌߷qU.<,kQ,M`acFUxo{I*}Pr;9YHqQ(\98T+߫yT5(((WnN-k&y{?p/'I-ŧQK]e( ذly5nL(VM-sВR0 TA%.ca,HtҜ-mx?zi++_4p\{@ƦhԮ&#PM>+u|ab%`&TVPxrtǶNܱSh.ux͒~ y~Wg_6}9Y{=fx۩^]:T.dims=kT H3l1-A`@bMK$F_51:{>n_8s:tQ^/l`3 ÔnYERD9B.ft'k"-e+("UlR ;D\sD!VxsZ˗r qf}>,ތ>IJU^B.+џoDeI_&~IaHBov>݊C7M/pXKhs8]KjжjK }_a[n6d?-qo;Ww CD4sy-82*FmCOۡ~б@ZԽsUBB\9|m!"|p"ZJDMJ:›k1-:ַcG}nJN<-)gSgPHhֳ5cƗ$3)6.5ܿyZز}4ax-ׄmx\B,qCyiY69wPt'4vr/ϛKukc*uohTx\)8q>c.aadfZc"b=6wY:X\B*VlPsJ$>I>o}Z #4Ok+,- suTKM+E}?#wfyKƍj(yqϝw+U,xB^mAuȉ@ZŴC 7g0NO VcN}։-7lVⲰ02P6wȽQ"JDJDJR.<,L3O<|qhsňЧ?a4wb+㑪}扷)Dտ>8Gn|?!wib;UkL7oҴŶfcT{Se _.9 q鷉+!w7~E;{/?3Ը~+OҡJ`>?uŬyEv5|2Ws#b^<<#Gƒuصj~X Mܰ|&g̢`bv+]-y7oL0vD޹'аZ'wQܐ<(>IJqu04Zp#H7^y-30LR8NDD4SUPL4ʛ 9+ܤSHk8X3sMö5yЧ] /=GѴv_6iԾ7\nLM76Ssk[9 5PRUX3K\>A6ԤSnޭ=xxI|: ֘<%XN\lZ~.s{PZ9i=ݼN)nN) uˡ-Z5js׊pk!Mtl .^ p%=>feaF² >{6._rfQ{7@D<)wl^/}gN5, tfzjGz*Xb8v +]ŀ5}VG|bnߍY#;v6 S-A-+tuD\ g/=,sF{ш!u'G{w*}hҴ-4͛TɜC%f? @.l霮sHXެʎ{?wPy^rAhߺשekkN)A} IDAT<_N@=QZuuۗ#AVw$ #t6k^'} 2٨ 1wTYU;K,}o_ S{b kUnnjx#(XbXE[?2m]xXzj1 >1 6\`Ԡ8x"5GU I 3ӢuѴ0BNU1]hTzH6lD] i?nuh|\\ $1Fo\a|O4ZVF%{snꀯȻrJ! SaĥV۵( &.4RRu;8cp3wM8T0s#2fp=ֹ]loP$ؖuLaB6iX3FjիX㢞rag^R8@?3quԠS;' KĘo&224<Dg(9%){!55ǎl 9/xаDп k/p;6!|4RtY@Yo8mKbHZ ֳʞ$1ea!Q"Oݧۮ .[̜̫- 9z eЧ]_ڔ: KeCLnĽ%B跭5UmyABUm<* 49B2Qox:_/9*|OqؔRڿӆT7$I]VCiJjXώwE?!d="")-M)=#b2\*fسy?<7$4Բ=Q\d~OM-Yw ]xĄ¶~º,Q/0#=OԽsU8j\Oy=4%n3P @,"r,彵Wm~5+N2$jʡow?jX#/?5[c\h""! N6fLjiG=Xށ@+aj ơc|rB gK`e! t$)kK#tiVhwΖi/waKrJFiHPc`O8V4C=&ؼ[^ϖ=pjmP#дaTB6N-g0lg"84q 8Kɮٽͺ2wڙWw .]ۥϼ̹rv&2ƭ(P+Ff5,Ftl `b3S}y|dϴt5CZ~ɩ`hgvĄ G&QXkw+7_.DQ3ù.y0vJ(\M`Xٯt'IR:T02ԅ۲Ah~;*!Q"]UAL\ _a 07@xdvfϽzU|^\; dw;z"i|ЧVL ʺuܞٯV IcF?~_"=_9%k7 Og?ގ=GnRE3}&Ə_܏A+`h$^'~=ƹ OagmSdߜ C'#fU+!Dd{Lj5 Oa1NJjK!&A#Q)^0sdû*`DC{XMjTQ=xS8db tlywiӧ:CMjc>qS‘kK¿7N*c0HIJ#רr isWMԬW3ł4Ϻ.ݥؙ_7E. =+~;MdhZhXF#%5[WFy#b!1F@Yqq= g6Bfّ A ԫ)0+ #l]:ФAyp-0\s羜׫eM[*^2YhX6[Gg݉DvU`^FNT07ס8ubSѺCr.`d\rji].<@\F:;ܼd9Yzyx*>'#]Ѹ^\F7/ ~ ԶuΪ-)<frhPw~Q:P7~6)ڮJ0vč T^gB6e?նXƤ1&<Klj [uҩU<6f .yhcܥ*&Kp*rEM#ex"z?H|4< qz 0. LD[F_,hf&"Wxiw^sD~t"ykU*((((t: UD"O;Sl* `*hd|Bq]+(((Q3g2>ݣmiYZ eߘ:!oDTVL,I7Rx@zj&q24 cqA^Pd &"* ܮa5_DW3\nrܸkp"3?"HDQ.4G̬Ro*ħAfD%j@K{=0rn8+E`.|<FYYATx07 Ǎ{̜ۘ{] FD݈Tstgh iQjSP4;"(qJ>'?lj8?ǎ1Xw[rH)2P 'ǧ91̱yW6 B̑:@.7덋 3?by\>F!D4j<ë޽O|3x_BlZӡHN(|BHOUPD$g\9pno}d+ؚ".S.CW'ヲ"X|D;kkQ Nbιo)2 3g.޸hwD%^5 ^[xhB&dTxNs~1)(SV:\ GT$eDD }r(pi&#Bؼ;CA, b[@Ec by-D)fN"0'~F3 Je1.Ǔ30h֑hҠB9\ZU bՂ>X ЦB%A6|#/]q?1L8Vo-+AtT#'ܱ毾ΙSfrŖA GJj&؄Y<>ײEZޟSqzq'fv{N,D׺vϕAo=C߱[q.[k\4EC1z(zOD & Cn_޷/=\̀% ~oP\:ÿW)8R1ibnNCTL ؄'i8#TsDjZ& ss1Rad ֵà 1d7Bx˲HKWC_/%=6>5ۭZ*B&3KgǪ fIes3 l}2HNFJN%̐ dmQȖA˥@DJe $Caj8qxQb7"gJEѮ'd ]ff~o]yu>/T\`c}ʪ%!S hCׇuS ;bP {6n1qAD tl캾\8puXG2$JXCF&&:ƅSxcS2IP]tpk@i+_Cnq@6ʽ܅Kyfd dQéE[}GAEzV(̐QKIQ)7oDNYy򞥆b0Cn,0^:Dd`(ѐG!}̜޲OZgx6}vtZ6Ma`Pt234HJL8?| [Vqvj C$kS7Oˀy 37PrMђ$M :6MvlN5-adDGW!6+lĚl[kz}iUH89;ӷPd8y~(2 o<̬f/|`:JYU la M:V+ 2JKBLx<<q{s?w,#- 1O3`*T6CY+#]X*$o}>܊]tu/8Eh>,U Pd2yduxC_̬a#<XZ-+4w^o-C\9g|ԾXU z"C1KE֯ijjl>NīgngۿS,1#Bf01ճa AZ%#?Dtt#]'FaXhzĊ bᤥ1c6y{Dwmv+*%r@?CW˳/&\8z~ZkL7'eܽ9j\8,(V)}`LuǁMy۲NCIao ]'""N';8#3S5^l be%H8QDw f$T%wE8cdki!eC5zy\Qb1{17&% obrb*,m۶)=SafiEGgjn_ +Nك>[j߿I 5;%|`3ܿwphis9V/ͻ I6-x噟4FҶ_|'(LN.]QzbȻۏ(5) [^gld~dЦo#cb\@ Dd]O"L}& Z(-)hYs~4Z4ʝֲ=<>š1w\I@AIb%ZW id[] /]q_}p~G<)Vo9eo u]lc9eKq_VK8-{|q/8zOZdkr ]1Cao'g x Mp`0~wq&xPH|b|yip3teu|jk5KUTHHy峷 #BbG "tٚ;i!H&vi;/ 5'}C=pW{r]%lClne ׶.hfC}jᇹ풙['\0J>U ;r`f<~N_2/OAu1b.h49n9T*)cܮanb7Kqڧ-}Fw7#дJqX6ߤnPTVh>Ι% >m\<&nݵ@% MNH/?Ĉd@m zx%{ ztt.w[kZ0em7qwY#]go"_YzS~ euҏ/[bᴍnv}7@\{B(sh u!^u.}}U"Ǒc9Q4triq_%|gq5L ~DtEv~i۷QsE?w *Xwቜݎļ%q\2(?0 /KU+f^p+*-]IƁr MW A1I#w%'"I4_2W?˖A,c_Er @NujK1'ϓA3=Z d $3S A7y| ~#s͝ byS U;/%%WԶh}7X-'\1`ȖAIzl׀KQ5>d|Q VxGT@Ӯv^kjN,m&rdgpUY2hճK~4oTM yßkg)!ԙjѠ2WoX(p5P.ҍ PՎ/'5j_`ƐnhȤn ?hhiVܠ '%SЭPـ'0HIaɉ`9V/45cAaᴍBMl2y MO AwJ5g1ywH> :w *WKAdx] CdL2WD3N bQ1ɸA ԯe c {oN~!k=':Xr!-r gЭrjԶd\Zq!*Va3K$@ IDAT%ӠfX<@p:4dcBHc263J5ʣ撅MѫfG ƪss4\[EFZ&v-{4`#S\=sN9fˆ?#-*}24~ qIQd B1 B oy i7 hj4\¿7 Q^,!VP(JXXh !~oo U2ٚJwIhѣȉ_+((%kZ0s>o3u[Tc}Ò)[XJm{eQ 7dv %E1 %h zcĈJ 3(qcSQ2W KmS=Z=WiNXx"w0sݠ#'Oq}À.&SR3?DQ@.`_'p3 ZWFZ\~CjѶ14s,XIb B ᒧIQϻv3ZVk]\ǂC4WMVuaXvrFӥ}_+9:wOurb*R`ikXpUl|ưHdjPYDCWOKJJHAG\ׁu^$c#N++9Prc_ۏ̐Sb1tlw erbFB> _COW1hw5L`dgBI2.|;6νSIboW쭨ҴՖR5զK}FR:TU1Z{S Y"{u=hjE/ssyL{ TZ }E|!,/PMv*fс"tXJuGK ̜301X~}VMlGz"wWh *s!)ԪaO5ܛ+߹bI6-3g6--K^'8ڔ+}nsi7Ң%"Yx= ӴQVma'Y^oF3S}_/W\X!CLUjz|ԙKa&9N4 )TR1ߪ{ٿf0g&PG/Srl0t樋׺pi`7 ^zdk ;%=Wgڳ,@ZD|_PِDz[*މ)w"ukrT,W}#S),ҲlB綵?6oݙ2ǿQ$M<_ub:xot`tܡɄy #C5v\,?Īy9 <6e~:U3'O veӟMx43rpcVqi?w[#ǖxϟh~tjgul.SQ S=o'ԯ~235糧U+~R+t `tlgݡԟ&fFl\و7f=֟;N᪉z3~a KKt&]Kcε/ȵe>5C%I5.ttِě4X`,zr;WwdpF;s3 Y{gFTW=GO>xû!LӂG?Oe'|˓/Rdhl{&JSVNKL!rvn-MM_.us7ݢ cS9~/uJ뾻,@1I4Yb-'Hgfa°{gHQﮏ;o)wRNK',(R*.*A[rkӁؑ-X)f s&3]xo;7)iw,bjbȘ9[ZR$ $£0z=5xgRT,4{nmtaCU !/ Z6Rs&aldPZei[{Ѭ]բ7s7Q'fv,۽.S:@TJYOj[/Vw$'c#Əj1:ޝ OJɣM󿓃%iĎXȸo܇JoܼS}٦K7?iׯd,qm\SiuﵕGB׮7xN F9׺|~{$zmmauV1 '#O9=3%>]*-nSg29dQv2/xu6=<wF07Bhc2t[BysO}[ְˮlaa~Sк&}(F+ʨ8Vz5xD$*:XSt \j>7$Iyˉ߫kqmo2c[N@wC=we'#513.5?{%QHcz}4mfa"dJy@y!T{..-)SgycuNm*,/z#r -{B5WFcanDqɭ7a&*lfѤ\6Fۖ5ZiTLӓ]x ,YZ{=Ku JN=ײ-'agcѱ6q\sWڈ#)5=pe簦tr.k{Bac%л뽭3*+E=ʿR)d.=9KЯ c.' ]n.ѵQ TjM74Er '¥&mԿRi2ͪb7 _OfOJïIg84_@@P,F 5w?~1~/%lFf a'{̽|IrdZg,c՚-pv_DJME3;"eJ#[p*8 qlLh75Z/*U7XY?fq7c?]yt'ѬwTi5F xs|֣Smj:Y|u;.Uǘ92g5Z/b˞0ۦ7:0m-:}ˀnm+_5fF8/_8ƽ֣B4}T($UEnM6.߫:q|4Td+תCMۻ"{ sFjյޏtjf (5P_fNNgyڼѥ3~aҼ)?X?Mf eS\u혶`Yt.SC|1FCq6-#INVeѱDfmlM).ѱ yxtMEƓW62ۡy<dԐ&v.^J.㫶0*Mj?}<W䝒ϱh xtt) bcCu'P6\|Q@T!˲)IRJqkIoa M;UoSNC#uSsCLL @hB[#?mKO;s>0ޥà%I"@p{ĎX0mp2/u{XZM6٪LK4QHTbE}Xp `{]H_<Ȳ|_Sm(۶VáU=]b-CƄV]jRƼmZ/%]=^>w6E,xe9 l|ce[ͬK}[MaSy5LL mzD"<Ȳey&0"Iko9zZthv6Վ_|a^q|XB fo8DmBI#PkKmѭf{M0U̼c?~_끞FjL- wtPyʊPĂJ,˿7nb._cIeFEJ;/C~8 CKyO"^:S޻PvZR&{Sy.lτ?]@k$ IBG [R!iIڠ$?d?v3S:D]հ䟿HǸdUK񒓋kDUFr\T}RIMڹqmciI_uD԰”~z3UE$JKvR4nSg-kvUߍZ;Yb1`tW9ٸ|  *?7wjfZD3zf]{Ć'zMUQ֖6HJϥH˥3{UyDDZ= TyR0id$ZՇI?Sug017mf?>H-Ԩ g۩2ŊPugҸCKPMAA>!OԮ_ lE,\>7yvNJwD=ODN ~/u'ǥK X˹c 634Dv]U̓^x~~u wlhU665}frhPL`ThT˭˂E$ĤJf&L?FhY4Nb<jw"rP#DYKo|R{%fq>_OEsjҠw9{uH`ϵ% bΨK'uzKϵH213 gNUu֝>">*yG!ٞo9X˟]^ƹ\W(6 sor/V*=)a ~RFr##P}3 js[vix<KMU+Zwk$_9SDKvf-?CYzr-!2Ж谰6㟥b ꓇.Hp8=)K &M3R_jmN.UJf] r)'3cSΩRI߱^JkTԕ]VG[eĎX |~not֕ki֡>ټꋧ"չwoP;nj?rq*뎨FG?o;5_jn [!rx5K-{']WDҹsSW.\V|*;#EĎX 6H)q,t IDAT) 742 *tb.K~U@[On6u?qg)i{^nTC6mf_m5*I%|w%E%l\OzkVeKI!`_Q\*XXsQUOA^gQҢXs!~RY >6;b3u@Qi:I?@p"<۸@3r cӓr*%kk^#<E,xbrc.){9 VϹde=5 O""jZL5(:~.^πu%ZV[al*nE['?HOͻz|_&YfÇA xZgNGjխrnQŤ@e"咢’ka1P۱^@"@5J}5teYeajahbQRxobmIvrA^W\6V۹.i(SUFk\b!i K 3}f" @ x@ .#?JxVӁj8PѢl^;L&Uٸi8c G&h<3IK{AEF'+U xڡqF!0/o#]׳Ƴ>JdmFd/9/PRg% ~ʱ~wأYg8w/^ޱ @ xh< Qv2I:BUx6By ǀ}MxFQ1z/^ޫo!B;q/RDN@ ]3(Tixv@yʭPb?u=hϞ~ yWz7xy?uU6kw$ʵ!xXyxZ\  E,|h|hkhhk<=JP/qZm豫xCZ!!+_yc_3<^wB ^4(&W;2E>Y}!UyPb6ݐb IYԱ!>H *oT wFDIN(]L;acZ./M:_E,xx@y[1zHJA; #,%2.W%ㇳJ6#˵k+KcRX65ko/?FQWsDg|7. p9x(BNCdqÑI8Z}^މw A fP3DP"@@yaZk$8!kC,Lr{~$]r- -$2e[&t5[CxyC'| d{7{C RP"oi~{.'p.1c(ѕа/$F)8!>&;Q o<bμT"?Jnk݆@6xvBRF؛1kw0Cۻ)U͍I)%^o[3\H̒?'tEԣDrӰl'lm533"*#)L#"=S#6܉:>rZD?b%aexyOzʹ -2+O\`Dn|ܳ 'cxsk KDz.Nl'[I.˻\7L 6WYv2F*SJW9XSnؘ3s}^uwdl:/eLvų/]˦ j挙]),,q5kPzk/V+PĂ'-t6sܥ6qP\5l }<lj45!U)ҒUXB+(~ɽ#H9MG$#,UxݕSi\ Wq#Q] dּЎAkPݨ$b=@E*X9Q"qt^foxEZ=7԰2%H˕&m Hc+]imFA%jM^jQ=00N(bEY\z2>y^QXg Jt|3oE$z>=xMcƕsq0"NUڟ'1PޤFsjw` o{@/ˌx":3s͍i`lJtz **;VAʾW=z/3.SmHC*'Jd #boG1PV*&h|C2 N|r Km{'fzȖ1][K<% E,xg2 K>ܰ}T4Q^@gxTF҉=#!nB!_@E|vK/1au=_sB *g{厭ܴzOghr|ݿ9ۻk&glw6@; *,PZŹL 8ZwTDtW3Y</ol͌Y7Hߺ۞RA=yh<[qbGS}9Vk]SCwǕnNôzq6&ٞb-ŨյN5 ב,,aȯ~$y@}[ẉC(bAŠrtЅ~a\I%PŤ\#|z{]R2u{#53R/udjhM6, !b؅5AQURޤg: L vR}gDl/4ufX9͑\zxG3sCĞ9GS6ieY,dWhO-JdhZ 2WC߂}CyV<玃 ʘ.T57H߂O{6IgoNE,xt\WRj -172 >sC8b2b[t=#IuKI+jqxS||ӌ l4F@ÍSsPLw05TS2>Z.+?_ŞnDe䑚_DvBhˁQjٻ$|xPXF/:?ךtMPx4b. yEju1s=gCyĂO9ͥ+O\AJSCY?Y1crԱ5) 3ރ5KR_xy_~<hAcϊ,Dz~1ڸxP+|B)bڡZkaV"dN&KG6u솥A(e`.6 %5YI4>eSܘBuLȜ^Mt>;3#V kC`\:1Rfwk4&:!+%PĂG9[i8G8>75LYz2PBfԈb-Fju^u#;^&XTA=>4uQve/sp53ۃM@%FO[bb_$6܉{΢e6 ~8!%)M^ͮN@mY߮((45P,+s|!q en2PƜ"t͌mcN`\:lShr?AiR}-nk`jz'dJ"&3NU:X3{YL[}hXЇmF?p7iSVn YE%\Lb μݏt[RPNqj*S5Ƈ?&A gװ / |}:Ҁcr&>vHȶ8NN郉Z%'rJRq:~^|-!v=]{V'!ֆ띐jۘccjD|vƆ칔@xZyԶ1gN&D]X$Lw6viEi!ta 9'dҮvfDWO!5]mcTY9ΰ͗]J5`^E*a;bnKo0;" 8p%cbadGoon yY-6TҚl.&eEU0YZu3x:<|&VճB,Ѥ&mk^M) O!7%);ϰzD[G&[Xf;5UL ?8K_,0W%cGi3 gڎӘW-̙NHgc,x{i ՙV&ִ "]eՉ+zTĬĞ4[Y<zYfoȔnTlzbnGR}-7((b)yxΝ,:z kCZ_c[Hѳo(qŶFfum87R+L'Ņ2 Ŋcy4Z]^Æ} ]h\ͪ}"0W?^jQ-]_wES)P;Gakd>{I0RJ%]cj&"=Ns*i-0ÍiAQZ]eq("uָiT57)M_s?T=;2tQvSncN2*T p7{.'C۠fQPLzN/cl"(.qx4ZzSFtvCrGMk3<",%J)=˟1gS,MH+[lL h4 / iF~%?9x,MGclM*L%"- c6ܙ|pvF%?h@%w\EuG+b-ϭ;! -Y{YXMǹ8cFʻ_{p1gnE$H+?agE[(Y@ 2?doxI&qǍC-Ie__t ߨ:-kCg P^"4,6Z:ɧ=;q1nѶ.3{"w#>@k%{vm4QmgIbRƆ󱜟ޟkMåu,D&,ҚWsfSl" vĂsn\vdp("O^`.xVioT -r|)%gA=lt)RxuI2 Kx " gG\I˥9-M .U4js%-wEΪT(>=((fܦH%hwW:Բ D7~7ۻaV5\muOխLOYR=h<4DAtIhjYk;"@Eg;tLzA15l9~wNE,(@"c+bBZ*fteO`\:<ɤvuywY$dfD;lpn9|գdYJXiMp(B]%{t:.搒WL ]rJ"͑v_J`0kn[~Bbc[ۃH/fX|3%=!=0hمZfՊ|tӐ%"{ha^Yp$McܙsR:=8jm\@ê#F0M `WO׻ -i>^uwg]D0[2 -؛{|7’oL 7In/Ȳ|1wcW>$&so=GVBHr6g1.*evFg{zҫRf/G<mp:Qh<ۢ(wdq-[NŞ_O3L{os,L0jBmsVȦ1:՟l !iYE1 G15Ӱb(:Y&H5gc:ԲeN-Lkg|N^K&5ya?ǮWսc6l;HT.ɲ\\!V$Jmчp-i;NӶ-=˂-1TxKs("%[1췣|2 ,iYEq ˲ilK{C4Vo,p^Fٳؐ< .$fwkW>  ^ޗlO+J.+9EZIFBeKY{:uuujW1/Zi0k IDAT$>ވ|Bڗï`y@81)mk}3my+ԣE(g 7VHj1@ z$!Iٱԟ 5?A{;"!up +-e;!TuO(B8VZ`kjD5ZŬ 'Cug׸p2RN/lmA+X'g_`mA {ԩfμ׭+ODF"3UٴSw."sckhugQZj A̐e>5e9J-ni2kVD<"wbKO̢5[0'ү.Vֺ4, |]vFN{g;:Kq6fUNHyװeKysk Zand"K^MҪt+N'ħJZyGBMciTsYܙC۔~)5Ƌ:s-I+]֏6k`'uմ"ۓcJZ_QݏB;Y9 5lXp$1|[7};53>ZqTſL |шak?<%aؚ-7q) |;5J\Jx#)fi ^ޕVY E,솲va<ZUa{hSڻn׆ ":8[1~I hAs* P A1N>ySV]ƤvuyKs>17 Fq|GI EZ= U2:v|.`}꺨!*z+U+2N, c0Jtz&j~ Θ?[/z:Y^_`il'.d2ê/NES;#si>"1tXyZ~[W=i2J?k豫N~8[…IkU3(goSٲCݗyx8HZ3C5Uk^:AUBbNul{cz>072ݮ %:Z5dQD2nҖe͜I+d:+6pbr{7 ev<]u€OM ՛+n둠D`b-?ohlJ ~aؙZ:CۀpnQr_$i%^ޕ/E\QjAc_aGXy6:O֍<FN91a/ޡw}=;bß&?1VۮyJt9O3}w),dĔ|5nm5aU;ЬQCzS>3ږɳʧymٲ"xSU]uu~˟! .sͮ7\g[W}>f-oy݄79o8a5ܲ)CNe[^d{j\7^R: ,[vϜ;OFOs˦jߒ& 0wB1[  l|*NYAXF[ߌo] [;ۣ&۝3s++/G/mI>iR{?LoҪْ-#{>~˻'_O}7gI,,sp~ ӹ2W|=oZVܰW>3_ܓnm,=uBc6g<{.-7iO0tDu^h2|,{m~]ΙLj3j4گ'ڼ/AͨC릍jp2W}8_. /b! nߏ??hv;ؽoWdu^v,/ ,iiAv\WtkӜumYC+vj͏fM[Ctj~IɀF)c&&0|<!wkjZg>_jZ'ge91e>+Gv2oBe4mLv-xy}ii[y+l@G0B> R27o=r'Y4ZVthޔsjV76ށ_Bٹ<2f[=;^Ҧ=۷p0Ke xyd@оy}2U.c6m@F>- R.#^X>I3{W?3m%X:nf'7KpѴoޘ;b.9w>g?)/-wx-{v⒝ii۬VM .e`Z兒 =?s3lr;ߋӶKi.s)KgxWTJ;:|^ nїGßo9gR^Ku_{p6WM`ةDYNd䙬־%Goڴzu 3jV$%V}r}9ea<|,_;pipFE+עVS^{Ս9ЬѢ''t)* E/Cn|9n:/DqyTeJDc6M_qrVkUϓi޸!%oo٩eSNjM;uj[дQ׿Ix7u7yrvۯbi=h3vەǾ 7i[L#z\ U~_ѡfpC)geO?N}77twWGY^ X:ZU(p0p/4/~⃟&۱]C55DɀO}ߏx݃n{ٕLOF[ҴQ/xy<~GFNIk n b@>J yݬx0p2Kx6%uE6T=d@'+,| ձKX$~9n;U'-,w^6d* 8#:6)p޺L-pV4$5Zb ,XH͏B\[ǹ)Iuq&*pݣ9qܵfT z@^fvâ8W~V# ExWE\2)>Os)끋O-B HZ8ϵ 4P2ox3ߏo~Lݙb| H < 3}驹XUk<^{WT+^Ɨ= afW.:_`)!ˀ q}X=>]v*r \K't$5v!5 in;@2{bz۠Hi),қ5f@3&0]ozW>V#rٙ\nf %L0F5mpcɲ kͷ>`~&0Hf?#p` ,D jQx@,*k?݀OHM|ĥ!E xmf _~@l' %*/8fJڏ >PY02-ep {\zٗ鸇~!5$`= zu?oq<^qs/(Tvw䄡@e+n-/71-CTk~x.3̞؛wQxĻ?RZ:33 8oyBk_4P7~ ,=++`]U%U7^@^w/ ^7jI;#l7 2$+@,C/f+t3 g`xDP^8nЅ#iIc:} N ?[?ո"𩂽pa!pCOp?$+|%70$mg'i{G|^pO:6%Ɨ:5{~XAAՐ$nTs p/R}Kǧj !.%~ b`iI=p#}I-s}AAA1b  ("!AAPDB AA ! Xt-=lB V$%b#X@b"(jDJ{I$(81MևA@qr4$(.{APB,ԨIHzZ q < I;TUvL&^;U  @ҪV!YLRi_WI-^@d<~1p KR+Im$5#HCR'I $[㾵e".@t|f{k6WRy:Tpղ͒͵ AAi$FHhfI$=v F 3K]@CWf6̾M}1;$ݏwk^&xA e ^Q;N`$3%\<_60v ;I3Cs.p ޷fv-¾  (FxE񾡙Fggq-mgf$`fJ$g$tY3OKxhoZ0 ifc%M6IH*gXAAP ?Ijtģӏ l< 0:QI4ul9 >7t"W)s5Vik3]H.f'0 (  VCũU<.?֓c$.i]I#QYtIk)񼘷z8Q^i׽xD*~h ܍/#ofok%GHj\z@X#@VgXb%O,RZj iz<-ޝRz|D3+kjaCR*ϪxVkIwCp}1ݞ끛+$}hf[36axA#xf<} xd2/8ͽx/]$gnnOYmnY>-p.;'U f !."i{rz +$/tQ/Tigo3s56NP>5+%Ǘ;b73{[k|<}Wff|4—퍑tpML i52|&_G Ǥm SD=}(u|Kg;&u~5%/O/kXR/<*=̦IlafSt>1y-0HMYz$;6];Lp rBq)e>))䜔2D~^/϶P@Zf[{whW"3{}WgtIBS4NZ9w,pӘvYK^k][Gkz84( <*$`]|Nf6[xfp߽Y,ddUHjr CX3LIY7|oՀ̀ -wxӑm$uī»/Mt>__|,nj$XIOvt#fvwe NQXx6cVuT^1L* I+L !wyS1%n;M뻳g"<5qA.)j88p]%/9EUÛ|G!2 j=A}d^_5K<~JJ`3쓴mn6rSWwF 9%xf(ihxWJv$ewgGhux3L[N5Ҹ7}<_5HAB,iug:eSAPhD afSWqyIG&yxsI@ی_L%'r,>o7 -m!UIHꁋj_7{I Ζ E[HѪ\Hc@Y>/YZT'f3Q;,gqSw\/xČӾϧ.@&|>K›J|?,dmy%o3|m}Q3H7f7sMzoN9dD&O~ if=嵸7 8n+6U[ʷ}5ʊVzb t$aqY4nugRm)SxAC[=.(Oxp>^lr?˒~GIQ@WFpSs Q.˹ x'%~Y6Ttߕ"95\\T|:gղ ALB<"ǴlkS$|A 44%Wf6d3NjLҳ7ãsEx'cLe5k <]X.A<:okcQ9Q/g)+2g۔VAGt_F2i|f agGzzEKG_BҀhlb&KA_lf>O4!Afmqד+ ^B\Øf98NAZgf;Uu QU˘Y>G qA  (".ĒKё^ҩtfZuTf#I%QkN(*;%],#; #xG,fVtOJڪ^58Q vNL } IZS+2bW 4HO}03${7 ON篚ݛ~-i4(YtZ{+m [ۿ-Ù݊6Oھޠ =xb'"pQxQx_ђdq3\fKڤg~f_}m$eoiAa2C@)Fx;}I]7SjIO}5703@RG` $]$5.g܇slj䴽̦I7I>uIߛِ])x|~}}3WZ#,-,1oB.oDA5KF^㶌;M$7tX{Rw`TVLղ?L6xrtvxӶ7YZfq[·% XKjG<  IMH(3"}p_*mXZs{m~JGB^GtAP6!>N4O?RX왜3GG©x ަyIO٧y.:|6~ES`OIeIÁ l`v3;UF\qr/RퟙE>-guԥirzUI)Lצl1o[|FtACFN)kp}xd&J:3Ikt~7qFX F5p.Ofq /i 5o%=y[C>̤sx (>{pfS=>y3wPI Z]lN/0^j":NL?b3;cYԔż Ge~}%m6+8.!q',.3)Wq3#W$+T=߶0: AAP8ό efC  w牟4Ob;1}$Y9IO&C0I\:fnیif_Hj*/x=.A* Cj$ =#'«9FVwf6*_m¬m2ߘو FMlhJãr$ǻ̍4$92~xә2Slrz?̦JZ mfS: CODA] ~|[%_mf6 /6s{%M&1-eSуH:2ܠ[AA-BAEڅX6i="H/ :Hڴ-Y@e۬c-9A2S_p۹F}xULzܻE<~ e>Lcq3{6EkFn.p 6pnM:] / $)U3n[ |/8 ,>OFiR$ϳtNUq/'c|`6_:㽖L=+++, DWgFu&[޸S5'p px)Tk`?!d"nv1pi*,s!afoI'xT}}Y@@Mo~𐤻pXlk1Ļw\^C-O&ҽ_?,$P_|2'엊n< _e5WQx{!=>'?H+68d%eP<5 ݞte sxJ<߾v)5^`/-okAP!E,oϕ/%Z S鳫xAA/'$Q) &H|e|}d*2">wAP!^ISkDeMv.c_{IXjQA="8(NHJKB(MZ^U BU)$J ͷ/|xCIPgk 5B:Gycu7d$O(]B*,{iªm`!(i,0^(V^*}24*M_[` 8z6:*֔>||$*]ԖRA2"%jeU*߾,RK),!AP$P~*}24XZOq,*1U)ZyekT$]%Tzk[9A=f3NkRm)[-Ueq*K wEmmR`"8HXIK.Te4RK$gfC<5̀&_f6BAڱU)$l6S5X{=*]H: xV6UB "ɺt&qL,//ޙ2ҺTMV.7N4Wtc Sp!xfK 7OY@)p0L%5Sn#2pMOwQiU-ftlW4v9 cf7T}!A )U)R*=eGXҖJTWv>|J2 m@+<:>8)绪B rHqU)QKyug\*#m>%Jf(ƴ}MQ3Ogȭ| wgi-܎O.ǣ̸z Og7Ȟ.s ^B 9ÿ} w:Iw/#\@7Jx'H:CD#3?qlFWg4t?9,ݺNmF9ՇsvfvNRt$YK-_ IOY xqכ\inYZ7ޠKTIͪ.Iy˝$5:e,DDA|kUյ*aW IEklvkBA| 8^,`BA|؃@APyb%MB V0RU Z&UM8ZT35 A-}ldfٗm0WJ}IfMҍ f65ƽ8j ,rucl|,G肎/kO=6N3;]2#Uw`\%ejk=WtkxI8+=;ُɄM=LQ; %3~J'z'Kz<5}tt4޹zi{QC,zD&33lQwd{ u3lrĬtcA ѫI%Ǘ=YaqY>7f[L3#i_7,txvq}IS :5->vǤVtӀ/v>;F6 OEO?ǛW~L7#Zcxj`'\}7ƨɤo~x:bM 얩~K\4Lgi"(,- [l;TfW3OoaoIZWx%e7x{I:^^|qI \?84ąL|8̢hrDe3?H l V|`_3)x~~F<GG3/ <'!n[͢*&`?3[@Ұ~<0OH:Q)]q1"{gGj7\Y4I~Ip>!/ S몸jY,||YT  p!vPKg'+u]gُ.Et)m4x4 ޚ v pN?'e]wTs3`+=p!=̖ppɺKZspgi'se}Yf6 _fXhf) P˗VdZ n[̬9jDVqKuXTHAZœG%'y8AA!"Z&U+_qAu AA ! "BAE$8 HqA  ("!AAPDB AA ! "BAE$8 HqA  ("!AAPDB AA ! "BAE$8 HqA  ("!AAPDB AA ! "BAE$8 HqA  ("!AAPDB AA ! "BAE$8 HqA  ("!*{ACb`EARs7p|AKODP62.XefO{,I+3K,?`~YIENDB`sipp-3.7.2/docs/media.rst0000664000000000000000000000425514525516253012145 0ustar Handling media with SIPp ======================== SIPp is originally a signalling plane traffic generator. There is a limited support of media plane (RTP). RTP echo ```````` The "RTP echo" feature allows SIPp to listen to one or two local IP address and port (specified using -mi and -min_rtp_port command line parameters) for RTP media. Everything that is received on this address/port is echoed back to the sender. RTP/UDP packets coming on this port + 2 are also echoed to their sender (used for sound and video echo). RTP streaming ````````````` SIPp can play a PCMA, PCMU, G722, iLBC or G729-encoded audio file over RTP. More details on how to do this can be found in the action reference section. RTP check functionality ``````````````````````` SIPp has support for for bidirectional RTP or bidirectional SRTP checking. This is not detailed in the RST docs, but in a separate PDF, unfortunately. See: :download:`rtpcheck_xml_syntax_reference.pdf ` PCAP Play ````````` The PCAP play feature makes use of the `PCAP library`_ to replay pre- recorded RTP streams towards a destination. RTP streams can be recorded by tools like Wireshark or ``tcpdump``. This allows you to: + Play any RTP stream (voice, video, voice+video, out of band DTMFs/:RFC:`2833`, T38 fax, ...) + Use any codec as the codec is not handled by SIPp + Emulate precisely the behavior of any SIP equipment as the pcap play will try to replay the RTP stream as it was recorded (limited to the performances of the system). + Reproduce exactly what has been captured using an IP sniffer like Wireshark. A good example is the UAC with media (uac_pcap) embedded scenario. SIPp comes with a G711 alaw pre-recorded pcap file and out of band (:RFC:`2833`) DTMFs in the pcap/ directory. .. warning:: The PCAP play feature uses pthread_setschedparam calls from pthread library. Depending on the system settings, you might need to be root to allow this. Please check "man 3 pthread_setschedparam" man page for details More details on the possible PCAP play actions can be found in the action reference section. .. _PCAP library: https://www.tcpdump.org/manpages/pcap.3pcap.html sipp-3.7.2/docs/ooc_default.xml0000664000000000000000000000423214525516253013335 0ustar Content-Length: 0 ]]> sipp-3.7.2/docs/perftest.rst0000664000000000000000000001054114525516253012715 0ustar Performance testing with SIPp ============================= Advice to run performance tests with SIPp ````````````````````````````````````````` SIPp has been originally designed for SIP performance testing. Reaching high call rates and/or high number of simultaneous SIP calls is possible with SIPp, provided that you follow some guidelines: + Use a Linux system to reach high performances. The Windows port of SIPp (through CYGWIN) cannot handle high performances. + Limit the traces to a minimum (usage of ``-trace_msg``, ``-trace_logs`` should be limited to scenario debugging only) + Understand internal SIPp's scheduling mechanism and use the ``-timer_resol``, ``-max_recv_loops`` and ``-max_sched_loops`` command line parameters to tune SIPp given the system it is running on. Generally, running performance tests also implies measuring response times. You can use SIPp's timers (start_rtd, rtd in scenarios and -trace_rtt command line option) to measure those response times. The precision of those measures are entirely dependent on the timer_resol parameter (as described in `SIPp's internal scheduling`_ section). You might want to use another "objective" method if you want to measure those response times with a high precision (a tool like Wireshark will allow you to do so). SIPp's internal scheduling `````````````````````````` SIPp has a single-threaded event-loop architecture, which allows it to handle high SIP traffic loads. SIPp's event loop tracks various tasks, most of which are the calls that are defined in your scenario. In addition to tasks that represent calls there are several special tasks: a screen update task, a statistics update task, a call opening task, and a watchdog task. SIPp's main execution loop consists of: #. Waking up tasks that have expired timers. #. Running up to max_sched_loop tasks that are in a running state (each call is executed until it is no longer runnable). #. Handling each of the sockets in turn, reading max_recv_loops messages from the various sockets. SIPp executes this loop continuously, until some condition tells it to stop (e.g., the user pressing the 'q' key or the global call limit or timeout being reached). Several parameters can be specified on the command line to fine tune this scheduling. + timer_resol: during the main loop, the management of calls (management of wait, retransmission ...) is done for all calls, every "timer_resol" ms at best. The delay of retransmission must be higher than "timer_resol". The default timer resolution is 1 millisecond, and that is the most precise resolution that SIPp currently supports. If you increase this parameter, SIPp's traffic will be burstier and you are likely to encounter retransmissions at high load. If you have too many calls, or each call takes too long, the timer resolution will not be respected. + max_recv_loops and max_sched_loops: received messages are read and treated in batch. "max_recv_loops" is the maximum number of messages that can be read at one time. "max sched loops" is the maximum number of processing calls loops. These limits prevent SIPp from reading and processing new messages from sockets to the exclusion of processing existing calls, and vice versa. For heavy call rate, increase both values. Be careful, those two parameters have a large influence on the CPU occupation of SIPp. + watchdog_interval, watchdog_minor_threshold, watchdog_major_threshold, watchdog_minor_maxtriggers, and watchdog_major_maxtriggers: The watchdog timer is designed to provide feedback if your call load is causing SIPp's scheduler to be overwhelmed. The watchdog task sets a timer that should fire every watchdog_interval milliseconds (by defualt 400ms). If the timer is not serviced for more than watchdog_minor_threshold milliseconds (by default 500s), then a "minor" trigger is recorded. If the number of minor triggers is more than watchdog_minor_maxtriggers; the watchdog task terminates SIPp. Similarly, if the timer is not serviced for more than watchdog_major_threshold milliseconds (by default 3000ms), then a major trigger is recorded; and if more than watchdog_major_maxtriggers are recorded SIPp is terminated. If you only see occasional messages, your test is likely acceptable, but if these events are frequent you need to consider using a more powerful machine or set of machines to run your scenario.sipp-3.7.2/docs/regexp.xml0000664000000000000000000001473514525516253012354 0ustar ;tag=[pid]SIPpTag02[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> ;tag=[pid]SIPpTag02[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK retrievedIp: [$1] retrievedContact:[$6] retrievedSdpOrigin:[$3] retrievedSdpOrigin-username:[$4] retrievedSdpOrigin-session-id:[$5] retrievedSdpOrigin-version:[$8] Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag02[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.7.2/docs/rstFlatTable.py0000664000000000000000000003243114525516253013272 0ustar #!/usr/bin/env python3 # -*- coding: utf-8; mode: python -*- # pylint: disable=C0330, R0903, R0912 u""" flat-table ~~~~~~~~~~ Implementation of the ``flat-table`` reST-directive. :copyright: Copyright (C) 2016 Markus Heiser :license: GPL Version 2, June 1991 see linux/COPYING for details. The ``flat-table`` (:py:class:`FlatTable`) is a double-stage list similar to the ``list-table`` with some additional features: * *column-span*: with the role ``cspan`` a cell can be extended through additional columns * *row-span*: with the role ``rspan`` a cell can be extended through additional rows * *auto span* rightmost cell of a table row over the missing cells on the right side of that table-row. With Option ``:fill-cells:`` this behavior can changed from *auto span* to *auto fill*, which automaticly inserts (empty) cells instead of spanning the last cell. Options: * header-rows: [int] count of header rows * stub-columns: [int] count of stub columns * widths: [[int] [int] ... ] widths of columns * fill-cells: instead of autospann missing cells, insert missing cells roles: * cspan: [int] additionale columns (*morecols*) * rspan: [int] additionale rows (*morerows*) """ # ============================================================================== # imports # ============================================================================== import sys from docutils import nodes from docutils.parsers.rst import directives, roles from docutils.parsers.rst.directives.tables import Table from docutils.utils import SystemMessagePropagation # ============================================================================== # common globals # ============================================================================== # The version numbering follows numbering of the specification # (Documentation/books/kernel-doc-HOWTO). __version__ = '1.0' PY3 = sys.version_info[0] == 3 PY2 = sys.version_info[0] == 2 if PY3: # pylint: disable=C0103, W0622 unicode = str basestring = str # ============================================================================== def setup(app): # ============================================================================== app.add_directive("flat-table", FlatTable) roles.register_local_role('cspan', c_span) roles.register_local_role('rspan', r_span) return dict( version = __version__, parallel_read_safe = True, parallel_write_safe = True ) # ============================================================================== def c_span(name, rawtext, text, lineno, inliner, options=None, content=None): # ============================================================================== # pylint: disable=W0613 options = options if options is not None else {} content = content if content is not None else [] nodelist = [colSpan(span=int(text))] msglist = [] return nodelist, msglist # ============================================================================== def r_span(name, rawtext, text, lineno, inliner, options=None, content=None): # ============================================================================== # pylint: disable=W0613 options = options if options is not None else {} content = content if content is not None else [] nodelist = [rowSpan(span=int(text))] msglist = [] return nodelist, msglist # ============================================================================== class rowSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 class colSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 # ============================================================================== # ============================================================================== class FlatTable(Table): # ============================================================================== u"""FlatTable (``flat-table``) directive""" option_spec = { 'name': directives.unchanged , 'class': directives.class_option , 'header-rows': directives.nonnegative_int , 'stub-columns': directives.nonnegative_int , 'widths': directives.positive_int_list , 'fill-cells' : directives.flag } def run(self): if not self.content: error = self.state_machine.reporter.error( 'The "%s" directive is empty; content required.' % self.name, nodes.literal_block(self.block_text, self.block_text), line=self.lineno) return [error] title, messages = self.make_title() node = nodes.Element() # anonymous container for parsing self.state.nested_parse(self.content, self.content_offset, node) tableBuilder = ListTableBuilder(self) tableBuilder.parseFlatTableNode(node) tableNode = tableBuilder.buildTableNode() # SDK.CONSOLE() # print --> tableNode.asdom().toprettyxml() if title: tableNode.insert(0, title) return [tableNode] + messages # ============================================================================== class ListTableBuilder(object): # ============================================================================== u"""Builds a table from a double-stage list""" def __init__(self, directive): self.directive = directive self.rows = [] self.max_cols = 0 def buildTableNode(self): colwidths = self.directive.get_column_widths(self.max_cols) if isinstance(colwidths, tuple): # Since docutils 0.13, get_column_widths returns a (widths, # colwidths) tuple, where widths is a string (i.e. 'auto'). # See https://sourceforge.net/p/docutils/patches/120/. colwidths = colwidths[1] stub_columns = self.directive.options.get('stub-columns', 0) header_rows = self.directive.options.get('header-rows', 0) table = nodes.table() tgroup = nodes.tgroup(cols=len(colwidths)) table += tgroup for colwidth in colwidths: colspec = nodes.colspec(colwidth=colwidth) # FIXME: It seems, that the stub method only works well in the # absence of rowspan (observed by the html buidler, the docutils-xml # build seems OK). This is not extraordinary, because there exists # no table directive (except *this* flat-table) which allows to # define coexistent of rowspan and stubs (there was no use-case # before flat-table). This should be reviewed (later). if stub_columns: colspec.attributes['stub'] = 1 stub_columns -= 1 tgroup += colspec stub_columns = self.directive.options.get('stub-columns', 0) if header_rows: thead = nodes.thead() tgroup += thead for row in self.rows[:header_rows]: thead += self.buildTableRowNode(row) tbody = nodes.tbody() tgroup += tbody for row in self.rows[header_rows:]: tbody += self.buildTableRowNode(row) return table def buildTableRowNode(self, row_data, classes=None): classes = [] if classes is None else classes row = nodes.row() for cell in row_data: if cell is None: continue cspan, rspan, cellElements = cell attributes = {"classes" : classes} if rspan: attributes['morerows'] = rspan if cspan: attributes['morecols'] = cspan entry = nodes.entry(**attributes) entry.extend(cellElements) row += entry return row def raiseError(self, msg): error = self.directive.state_machine.reporter.error( msg , nodes.literal_block(self.directive.block_text , self.directive.block_text) , line = self.directive.lineno ) raise SystemMessagePropagation(error) def parseFlatTableNode(self, node): u"""parses the node from a :py:class:`FlatTable` directive's body""" if len(node) != 1 or not isinstance(node[0], nodes.bullet_list): self.raiseError( 'Error parsing content block for the "%s" directive: ' 'exactly one bullet list expected.' % self.directive.name ) for rowNum, rowItem in enumerate(node[0]): row = self.parseRowItem(rowItem, rowNum) self.rows.append(row) self.roundOffTableDefinition() def roundOffTableDefinition(self): u"""Round off the table definition. This method rounds off the table definition in :py:member:`rows`. * This method inserts the needed ``None`` values for the missing cells arising from spanning cells over rows and/or columns. * recount the :py:member:`max_cols` * Autospan or fill (option ``fill-cells``) missing cells on the right side of the table-row """ y = 0 while y < len(self.rows): x = 0 while x < len(self.rows[y]): cell = self.rows[y][x] if cell is None: x += 1 continue cspan, rspan = cell[:2] # handle colspan in current row for c in range(cspan): try: self.rows[y].insert(x+c+1, None) except: # pylint: disable=W0702 # the user sets ambiguous rowspans pass # SDK.CONSOLE() # handle colspan in spanned rows for r in range(rspan): for c in range(cspan + 1): try: self.rows[y+r+1].insert(x+c, None) except: # pylint: disable=W0702 # the user sets ambiguous rowspans pass # SDK.CONSOLE() x += 1 y += 1 # Insert the missing cells on the right side. For this, first # re-calculate the max columns. for row in self.rows: if self.max_cols < len(row): self.max_cols = len(row) # fill with empty cells or cellspan? fill_cells = False if 'fill-cells' in self.directive.options: fill_cells = True for row in self.rows: x = self.max_cols - len(row) if x and not fill_cells: if row[-1] is None: row.append( ( x - 1, 0, []) ) else: cspan, rspan, content = row[-1] row[-1] = (cspan + x, rspan, content) elif x and fill_cells: for i in range(x): row.append( (0, 0, nodes.comment()) ) def pprint(self): # for debugging retVal = "[ " for row in self.rows: retVal += "[ " for col in row: if col is None: retVal += ('%r' % col) retVal += "\n , " else: content = col[2][0].astext() if len (content) > 30: content = content[:30] + "..." retVal += ('(cspan=%s, rspan=%s, %r)' % (col[0], col[1], content)) retVal += "]\n , " retVal = retVal[:-2] retVal += "]\n , " retVal = retVal[:-2] return retVal + "]" def parseRowItem(self, rowItem, rowNum): row = [] childNo = 0 error = False cell = None target = None for child in rowItem: if (isinstance(child , nodes.comment) or isinstance(child, nodes.system_message)): pass elif isinstance(child , nodes.target): target = child elif isinstance(child, nodes.bullet_list): childNo += 1 cell = child else: error = True break if childNo != 1 or error: self.raiseError( 'Error parsing content block for the "%s" directive: ' 'two-level bullet list expected, but row %s does not ' 'contain a second-level bullet list.' % (self.directive.name, rowNum + 1)) for cellItem in cell: cspan, rspan, cellElements = self.parseCellItem(cellItem) if target is not None: cellElements.insert(0, target) row.append( (cspan, rspan, cellElements) ) return row def parseCellItem(self, cellItem): # search and remove cspan, rspan colspec from the first element in # this listItem (field). cspan = rspan = 0 if not len(cellItem): return cspan, rspan, [] for elem in cellItem[0]: if isinstance(elem, colSpan): cspan = elem.get("span") elem.parent.remove(elem) continue if isinstance(elem, rowSpan): rspan = elem.get("span") elem.parent.remove(elem) continue return cspan, rspan, cellItem[:] sipp-3.7.2/docs/rtpcheck_xml_syntax_reference.pdf0000664000000000000000000043140414525516253017136 0ustar %PDF-1.5 % 4 0 obj <>stream xwu., Ҥ* MXPP@ANRD"JD z("Mҋ (n=rq>dMf2wdw'w2;ߋ@Yn]6m{v`V׮]}~=;0ɓMFF#7{l_cٲev꫍7l;wG%!!{`'eW^]&L{SvvvٲeuAfĉ#Gcڵ,@vmƦ-Zf͚ڵ{ꩧ6nܨ(g O27qqq駟q7ӍW*;vO\4io[nb.3sL?.s3gϭ/&n\Q=}^Q'< Xtӽ{~ӹs+z 8=e:h7hݺn58p@MVVfM6k<.s|r6m7̤ٳ *2g27 :wvSN6c>&Ӎ+~ry]GrN:6c>&͎;f_e<27JRj %J[ pرcoVժUf|3  E.s.D)VԩSsrr;27A`&A"E 2{@To}fZwi(uaÆkРA pGO>7|k֬yFreǎ?)75L^رc-[[KLf^Y;; p y-o D9 >}i1,||QjԨ۲XbOen[3};pMjt[XӧҴ594hc?t萯-H7W_}qW_}?~qWT)]`1H7J^t#7@8sLB{V wٹsqQ~g:rH%޽bŊӧO߻woΝ۸qQ*Udn>chcǎ5 k,ZH׮ۇ;.ݦԯ__w5fe„ 5`ѦO>.\u+WVRWξ˴VVx&X/@j׮߷oaM7*;j2qđ#GvܿRR|\&⏪fn{unٲ̰Y\^_|裏lٲL2iii񩩩e2ڶm;pٳgo޼Y_#X"йstRB㻄m\\ܡC~駀I&{Y3O9sF5jTއŭeA)PO?u@EҥK㻄_kUVAfw}y!DWtx_x3_X"ĉuoq%|ǯvΜ9[_xrJJ|~ܳGVVn z8+ضmn0H7,Hӽ{wxw +'Nxn_W ^du3Wvmw^\/ozآE.]PBqqq[ E@i֬ _%|ǯ r :dr_oҹsn:wܻH"1 e ԬYST]tz=lu?$͟?́_ˡԞ~Vu/Y$r5kW wp›o9sLmX,|W;wvSN?C#*U_=t}p:W1o<76lb*Tнo|tGqllnJrN:=ϟ?/gܹܳ߯ȭek (`DX"x?t6^W*UT-Df+{%sκ_P l26l T3g1X" 7ܠ{7~'w8p<4kEHTfwr;3gIjѢg8[PTVٲeo#F]V=X,Cw?ewvS;kժ%7+]ߋ,X>~ڡaرx?䁜)'5 kb_20p W?K/a]|E%J129~mݻAm۶,X!.{=lk֬%@>w\߾}gts h JX u ?t͛{monp_@,Yd:֭/E7vf]fp Xz&TT׷4yf̘afܺ^yspwa Dw4~PHf͚mZ*Uycǎɻ{=lt `rmH+==Zrqڻ.\?03an]/ü>Hhp,oζ2dH@#={vƍWJ'N|y#GiAS1gǎ˗/={U:u:`,ܚiku tu۶m~kT=e $1bY*TU#uo͚59sѣGˑ#G4h G;|g D'NNsj:{p=Isz#..VZݻwW31cƼy&Oܭ[4u5xh@dRol۲TR{RDwJm x~p%WK7YhCX"SvvZWCn4SON֭Oom@XrӍ /ݰX\^+Wl:<`|5"[ؾ}1cZn]P!P?~57}P"޻w?wɓ';vڴi/oTF33ez~Xa2a ,8ebp/Gmٲe2eSSS/:umvg޼ysVVnf6ӊUPh޼?4)))٫UVƎwAԭƃ4yd+۷O:cǎuUIRRҵk)Sڵ+MIʕ+۾x&GKM8:;;˗o͚5&g 2xر7nu|~駹sѣQF+VLOOW ,Yd t2f@.\z+Rзz=zU|m_|y}5_7ٴiTS@~8J7Rʕz-h[﵃ؒn$O zÆ ͌Qjff-Ӎ{NMInoWu'$$q9x`-="Ezҍvʹin޼뀉O۫]mYbE+%nFE-]t#ݨ*?^gsY3~ܒ%K"rJ W?}޽{s7Vo75RJmlO7_'?zAѢEmf M˗/5k񽼦cǎ,Y੧?zP2uTU=[a{M=ܬCf;|pXV9K/s p5k> *m7|s̙^o a4h/KF3Θ1Cn־}{'7‰]4 0@n5.^+WVRtc 7nCn-K*BoN&}Bߟ${i&nǏ7iDޤz~!Й#W\;+Lߺu봷fddTZU7L7@HNNo{l~w^9ܹsۨ>}ԯ_?Ս#T<` {ː_(P /W y9sR-;gY^&}B&M$7Po]ptc^T-[~ nԬY3.r{L}瞓oRJPorݻwnE/]J?n/5;>OD9ecz6lUPa̙e~%6#_VMOΝ;y_/Ӎ{=gΜ<> tr5=^)Sh+ 7t ^]vn>}xnZhs/No`֭D#^<`Pu^8Ar&0;p,5tЎ^Dшb!G32X/#${2v5tҥK{sAnԳjA-&ҟ>&_[ҍ=t&0}zZK7uBB’%KXp7tsɂ zw+VLϖ~M@Bpfɺ-ccc='?ڃ}n| 6Fc 9Zw2^.0O륗^RVE2KE'y *䞍,H7#G4*Xn>ϟ[]/Ek^{]LD=ւ Xp7t_< t@PQK77oZJsr;K{ҥK=z|2h!YzzX/#8*:-]y!۷dpU,XP}r]wu<Ⱥu^Bn<;{ĉ>;hOošn?o"::~-7|p՛D!8_F\5Ӽe &+dee_LJѻw}5:!G β^^Fpq=ZzI5#$6=j͚5^ǚׯ޽{ˡt_*Ӎ{eߢ^#­ӍrM7wͤB.]g>(J852d_ti[̙S@ l23tD ;wN"rW^yxI7o*>>O? bgqxo./s)L>=G[M7|;}m̤BBBE8/nƌ#э0uW}Z^=95k֘|2_X/2|=!a^^i~/]V><Êt#wҥrJ*|!F^9_t;w7''gw,N7; b|~ࢉtބuaJ7t@P]|M/t'x}j۶Wɒ% MTa ^{z|q̙=u#G4h@>,nIFyQJr !uhJ"E.xΝ[hQFbpEwo{~(U}g D梸j*̤sfffʻnQzu}N:ܥ^z~C ˸zzZ/#7xժU{GV͛MN2E2Gj|}s1ҍҳgO9G2ez5i$L:j۶teӍ^4zΝ/=tdj[MB%v:~ٳgK*s=Ŋmfn뮻ƍ]T/dpzsX/zaХ$I7*x뛅 <ҍZn-4ùƖ޽{ZO? Qd1f>@0N7O<;˗7^B̵t@ 2bX/#XX/#tӾ}{&zˍk֬盈J7/@ҷo_9_M7v}*UuXHy8<Ӎk ueh^e!e߿r^_b<3$I7W^#-xXzrp_Tcy]Ʈ>xCLts\;?4VNIO>{k]'ZOzzAz۷o3fL֭ s]z= $,Lpqu4xԩS7rJjVRaizz]wݵrʼqƖufWh@P-ڱc9s/f&ӰaC|%8qDwx]J8-H7 bnTbd;to nE(MpҍD,@h!q, NGH7H7-`;N4H7FlGi(MpҍD,@ !B4I&8 F"X'&>>>--Zj:t3gӧ*W{[l#*/Yd˖-G}AB !BXBJDin$ҍ5 >J._+LM8q}OLh!G!I4YH7&?… 9rs/ c-D a= t(M BH7}ī7[Ν;uԢE4Gѣ=RغV|y햩uuzD}j=bRRҞ={|܎B:&g!Hk>{:k+VhlҤI,[,66VqBB:&g!HkϚ5K{kƍ}B۴t)-(DС4I&8 F"X#~[ǽqBT 2DAV0@ !BXBJDin$ҍ5?uȑ+Sh!G!I4YH7^?srr~WUH ({SZZڱc|B݀-D a= t(M BH70yYO?`Uvcǎ=nRRݻð/Zz"P$Jt#naիѣG 06jԨP1B:&g!HkOHH]`—7\-(DС4I&8 F"XCQF 2aÆO3fB5%JhѢȑ#݅-D a= t(M BH70ZQSSS {ơ4I&8 Fn: %gώ8-ܟoBq(M B͛{m/^|gϞ-D cǎ 0ÁFi(Mpҍ7}\RB_yB2B\{ \RF _%`O>ѣGE&)t{-̛2e(bcc;vm6;BH[Ș8qbNNɚFiK7ڵ+Yd6m-Yd޽F"&;wL2^y}BH[[~~k)RȘ1cN:PY4I5k^/<ڵkX" 0t#n?ܘFBv PO.^xHrI 4effرc…>hV/Zl+H70CÍkh!$ cjsMRf͚SRM69򪫮^{׭[-ҍD̘>}zpƃB"pk(MRKo6o޼-Zy^ڵkϟ?… zD3ҍD:sL͟&BH Q a4I+M{9rdr<L25t5F"~,Ztwի1"0I wip«ZNO)Z3233pp=ҍD,@ !n:tرKi)M999k֬k=VZk׮ #­H7-DlGi,.M˗/T'ifϞ=<.\t#n BHv&t'MKK7oe  HI49roڶpW"HI4͟?#Nɒ%7l`,tXB"4IxjIII3gδkp t]Tƥ ,تU+;N,`B+W.%%KEl t#EBizG=gӇu?a ݌5J} 7we˶k뎓n W^y:NT$@#HS.\&su?~ BqfD,@ !EN D-JQ髯*]O ~7HDH7h!j!I_>\fM5u9r n$ `ҍi-8iM4QZ" F" HBц$Efi:v jbu9qA!HRd@T4I[?^~}5n!F""Itʕ+u);; "n$ `Z)[ JP/M%+T# "F""IҴnݺĸ5k=؏t#n BHBGiQf̘&{nn$ `Z-n&)鮻RlԨ DH7h!$Q$cǎ.]ZMsH7-p1JҴaÆ[=؆t#n BHj!4I*MVmРAVVs=H7-p%Jt)iӧO{.F"BDiW-Z&\HG=؀t#n BHk!4IN,M-[Ts0` H7-pJҴs΄]v=Xt#n BHNl!4I-MS~FH7h!$P$={$%%v"HC[M(MsKSn<H7-p J?(Pɓv!Hs[5(MKS֭LbD`t3q˿Rmڴ{8q Ѱa[neK܁t#94\RMz999v1H7VXR֤I;N,`B.]:111٥\B@]ʕ+=X3$ `Z-&iذaj=z{"F"B.I+M}"E233 @H7H7[8i֕Zjj>c'+n$ `ҍp:J裏]4hH7-p:JҴa {"F"\BNGi\P)b޽vaGH7h!$Q$wvکXhAؑn$ `Z-h&gQ{1`'#H;Z(M;J^4n H7-p4Jtԩg=F"BFi\SWvdvOEH7h!$״sQ$ה[nEțoiD^tXBrM 8IrMi0`ڑ'=F"\BEi\Sy#}{"/ҍD,@ !$4^ZH֭‹t#n BHi!4I)M[lQ;R^='"HkZ(MkJΝ;ՎTT H7-p.Jta#%J{"/ҍD,@ !$4:uJH‹t#n BHi!4I)Mٱa\FtXBrM 8IrSiJHHPr'0"HZ(MkJ 5jGT{"/ҍD,@ !$48qBHZZAx… RժU#6BԫWKt#4qʹ(an^}n6qD;N,`B5t#4OjG*VhD^&jn 5-\5髯R;R^='"H5-\4o+J͊+ƏcM>Ƥѓn^|Ł>75-\44qD#?Ax9s-j'MV|@\`ۓn sO1rȠO.KJ]}55-\ŋy)MW;2yd'2fZb?tRL+VP,ؘ1\L7u.~OܹP~~8ucNT$iʔ)wM 8FrMi[ՎvOnf̘tQIj$O\zn 8Ow[ltܠr17,CwM 8W(frA 8CK'۷wECQE")%?HlU+e|n 8(ݨr&&ĎW\m1x,ECi KSNNj#.|vVp}YtiѢ{/3`@B tX ZrTԟSbʗ7j(_ph(Mrziڲeų twrJ_G, -D:eׯ֟qb۷c9\ J[QvoD`קG}}?e˖z8 `hh!jּSyᅘvnXNo!U'͞>}PBjꕵ{.̙3M.֬)WÑn DC J->YY1 &YرcEQ~7 AH7h!$g+Q$g}6k,''tXBrV IrPiRU]܎; lCH7h!$[Q$}Μ9NtXBrJ IrDiʺk<;wl\`3ҍD,@ !9܍$94=j%K}ɍΝ;^*Yɍ=-Nv8@"Hn֯_AҍD,@H7H7R䤛iӦMMK.Ν{:PtXt#nqҬ '͞?~1+F" Hv&߫搘 H-Jdci7n\rr@z;!HI ki:uޤ 79<}t8&!HI _iڸqcJN8'O<811Q=nѢEWZ򇆋n$ `Zt؎$4L<999z857qqq={|xx^7h![W_}֭[x=(AI 4={ٳgb|5^)ҍPQs֬YʕlFF+P*cǎ ۿ g}.(K2o޼YSY?5pScPnjFh!ԛkQ@?8f͚58gy8jy o߾A8ݺu !(M6muV J"E ._|G}gII*nܸqȐ!+W/?t#L7@5TF~A|(qqqdA[vD !B|]wݯ3^s*O>TDi2!sܱcѣ6l{i贴]vdp"f*T$_|A|)R$/ lڴip_yA|ڭ[ ۷op_&3&ﮟ '={vp_G-P qرaÆ:TNzਸQӦM~ٳg8jG@GA8JpNp?:gAƽm۶~RW ~?s挩n&)LKq8p`ڴiNi\B̟DVJEZٷo_4JVVV`/R-rk%d Rx𽂀+Q0\ܹs7lmH7RWPA^רzwF !B8qbĈw.<\$;E"u5jm-ׯW pZ)-8_~ٳgCFiH7pҍ<-[vWvjоd;BH!i!T9rdnƙ2eJ^2 P$ t#%x֯__U$ BH!l!N<2NFFzt57 (MBn}_`At'xaAZZZj:t0g%.sײe 1E)F}=6=%Q$n,)T&O?܄"BHaj!C;ҥK S!'D)JDnBSh!$Z[, J…9⹗cر>=Q$Q,tchF !BBWT^_~kѢEZZ,=s/Y[Nejjj]aݺuQi߾}Ztg;(Ci(MpҍD ME4h!l+ ˽WC{?QL-4i#*˖-n0nܸ)̠4I&8 F"X"BH0. fڸqc_ㄪ)vv>>v8}G-B$&&6l^t9| 4tn~Q-DJJ*n_ݯ8iV"AT?~ҍJ7.\O>%_~)TPrr2F˓n5kvWdff*шf%NS8p ==[o%tSV}.]ԩSvP oVB˓nTjԨѓO>iD#N8iNm۪tI7fjڴ+tȋ.\#䦛 &hӦMvV@Yfn&MDrӍ꾊) p~r-#ѣFϟ'zIp7t#EaP7InF=vb@Y8i/ҍWtr~*spB<}1/ذaC+W^yɍUAѣɍΝ[N VY_8i/@M]6p~*&t tFQkpҬ/4t.p~*}t#ti58i'"i!x%Ӎ>}t#yM7 Xf p,"?ܐnnTKVhNt#yM7.VjճgI8i_~nH7^yM7w~ڿo_Fy7t3SApYc4%!x+4 XZ p*&4'"b鍊n}bn$t9?>7Ai@xpҬ_4ҍWf֥ʔ)iy^ͬK}pi!Zn=3j(t3ҩ UTT 8i$NE8pj!'񛐐u1.|RJgΜ"Ç'N8*TPrrG|d3N73gάW~mY8i_~Qń !U81ctM4y~yֿ6m:Ǝ;ӍϸqTU߶m//`4k'"WZܔ1g};yΝm{=,Xp޽Mp7 Qþ;>]wݥݦB Gp TL0=VX|4СC#\s5ٹiM6t;gΜ(BtmikF ew/ʕ+͏ "?+;ٳVR?kѢEff9l*x f]vmиqc_GwְΟ?_N*Tߛn)(G! :tH 34hN:?+=/YYYZ[|G}d+G mDH1alْMzzj<f /1 @g o }SSSmۖ1&_\塇n0vX3au f Q˖-V!^ t4:l~X_,XNK,ˀ_&:h7:2dv{̣;kX .讟uҀP+Dw]փcgezy攔<: P53gmPOp>Ν;K,1(DAɓ'P`#l~Xȑ#E۴i]%P !QjkF (BtR-qY둝ݴw,qǯ_ r ";-@ A,lw`a!-q<(Td~B8/_IT*lV|3۾c r:XOBY~e6ϔ65 G GwJ~jbS\;NSU*qVlz>{|8Fѵ @@t O.3xcx^?Tj4M\.ץRjIk|߿Ŏ׊] 3v>[3Ea+^"ױV7PVvTV6 Ch4z:sUU`Ku]`qw~XK-ШVRB ] M1yb(~||l6x ?*Lv[ژ e;Kj&6b10QO@1x bݮ47  xQanZ-S%L~mK7]Vb ŗ DLB#w@=(+M$/u:G#ŲbY 韗[ endstream endobj 5 0 obj <>stream xWrH}W#<"ɺTV9^H P}Kc{@83]"JJ#u>}ɽs:wCףgyNvG$pQ i;L hh.ܤ=N>/XObject<>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 8 0 obj <>stream x͙n9)Ncd(;lAGĚi&~GHJ& _X$_g㌒dy8YIy~MQH |nZ2ے%v5_H"einOy5"fKmYwM>:xuf%j547jtI3ߒ˺td}r+׵Mۥ38opzԺumWK{F6b^Y~r{h?^îJvAEI/)xn:htl3z~\m-J~;TMeiU̚'l8TUφf({fꉙS<P/A qcY pҚcrzkB+Ҙ &PXeLF>QC`M cy6][5Y+NpǵzYGuVSftq0P3rx|NߜTw{@_&΍i!Hn]#qPnH[}ְ.^\GO~ly-ެ׫Mi۠|"zګCWK-ޒ"dA/i1vb % '=d2_A` 44i%^d6# kzql&qVű9aeრKĿ².k ((s}[S$1arFt,P{v;>@=`?56+zx*{*k ~Ea=0 'eOE;od:. n…pWm~ ^ endstream endobj 9 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 10 0 obj <>stream xY[OG~_1⥉m/(QB!$ Yf X۱M g;3v1R/̳!9.ia hsL@VNMVch}WN6M](LЇƉShxa.s*(&,`b{*78zj 039'کԉLeyroFa"O6j;p\TbQMX {.,BTn0pafy*hsT@N]=%4DuW 7|S6 yr!{yqnKJ"? |؅E J&f<Q`﯍Nf8wMr̛-2=!1(7N6 ]0D1$7WX!bǹ/u"6!6ټ ?8 E“uوPw9tfpWfS;cjDV { Q F e`7 `(0sтEM[YAytA,)Jh`{Vc$j >Y:Bһ>^- RyD, f$J-,|T> gmejqũ% GdqBaP֥ũc+ O-vۣn 470I$[-PN^ƿ~_.S&gѰ]h;>W٤տ|fj2%ef|Lo?N28T&<*T@/,tю0zSM*4m:}߇#иw?Po.HoA}כ 9IOf %gkq*K.Pd N;'W燗;Wݿ;-$؀J&NL" $lF2[сo>!X'o  } =f=4>157(jV.()jt/OV6)e1prYn.wVee2T=]3.ª32pжl8MbI'ײfx y +Kkqf%&ՇEɚ]AoUYJvՠ2[҃tJ <-upWeyRMӽ8|콾8;]5PtS M|[L9]9/:/OҤ`w}F䟿qc iokV jC˖ohuGu??0~~|Ɛ6 endstream endobj 11 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 12 0 obj <>stream xXkSF_&ӑz?c!8X8 #Z$],~Lai{®'`d  jNPoJ1:-BH [w%az; 2e\%H^0j F2:daQJCBVfgHA;Y*\CWH͐Fn&Gr0M.NL>*gM!RM.L; l3)z8d7Iz7nD ^~[-4 c9%nC,% (?4 p4_`]_ ?t%]/Gt<xet4=ttK`{$*LF~&hI$YΠ V$(_$=>ӰvyX.YT74vՍݒ,NYD Y}w0ۢ<@y (I{fp}M6JͿͽaJb9Tel EU闓7*G߇E42oCc?"`à \U[iXJI<ɇFT7-xB/&z!PY$ r",LO>Mͧ"aLpi ?'4EYrX'UsEu=ɩ]aϒA bC})KwgMtN2XJ%NCR+۵1{K.vd<'נNhx GAXx<'2M¸@ߖayь΀*蹃cag"voίmvߨ8 <0>'9}ym㝌\`E2y`5}l.UU}~R%,ȚD1X n$,V$Y~2陂n]d83%$nI/h:kY[^>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 14 0 obj <>stream xXms8_x9;;njR\J>LFu`-wVv=24dk_gU>jO/`0kFLby\j ė5J6kmC9݌M"7l߯L ?[W)^i l~rwVn`2qg;:,9onR177vw63)[ݸjghktuguh r_zuPUv"_oC#]D;.Fr&sxA@~N~U|6QrS[#y t|g}:}"ю~Te4zgT29֘' ^Tɠ CxGBh]OJZr1O x}@'>U/CKE?L>0//y9By(`xtz~^Hf,*j~1 KBYm%e A7QkQz^EaخoVY=yL9V$X!6݃^|P1k2N&þWoЀ~=P`t,k8SH*`uU!dϦUPTlNႯek$&Uŗ S=mYVNeURk^O%c`5+zA~ݩ֏ k_[K URV ?owBʼ_ endstream endobj 15 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 16 0 obj <>stream x͘KO1VNK =jUUB*RP"B aYlȇlimcZ'P.Y`7PX[aE:# hCGujeC۫es~;.?|"S=f;cHR0n°/KûмӬy/526T<K(iT2Kօ>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 18 0 obj <>stream xXYsH~ׯ)ZхlRK0bW (r i ?w{f$!m6y[(̴fMz7X@Ϥ7(DL4 'H*iqE ,ÀX2E|d acn QJ$;Z`kUGFSTWƶ S5dߎ”fTESKBl> 1t)3 6֒vadѴR6\nqM&;`UJYaudf14[NՉNĪ.Pl镼̍ kJ fSEeRhV;0;jm]P> #P~! ~jTLXiبU*&VL&U 4 nTZ[L4zeUK%Z9'xGz}ISŮi -Ċ)תnk i`մP.J\% blۋ%USQbXz0œioz!{0NgmX/muYO,;I-2/ATA` gEUewǐkw.՟iA[iV\tru[/C蹕~6Q{Co[VN3.GYS=6OuݼY :Ku6mA,uܣa}XS]#˼VFHQ ȆA'+[yM7TA'}Ҏ(M /VpOiVI;'w(,]<]lҎtBG$M|vJyxCar1$,h$G8 ip?HD"of^ Tokbȵ &XM,L+t P#2]lxr:ƨ͇#ƧË$gvd"8V KUK xA H!6x$2aD|3tm'0>h+0_W8r>k̺ޟ]`Yj' ب A,׸k ;4bRu@V%T-}S 0-hX} (y9ˆ怇̦q1;zq27&S ĮY~[L&ooa#//`8?5E0ar8`uiE}|,p+f |K2lyXmxez ^4q*#vX{WZ@B{a6.('6G}ZLx:EB)Z +8= 6 '";Tzg] W&Kr#=  ˆ:ipIh̵:?@ɞz۶1L?Άxs>IKf endstream endobj 19 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 20 0 obj <>stream xXnH8VڋY؊m'Uȱ'b;i"{Ƅ`J"1c;s[S./T^UFtۄ/ ?'8UBp6 H2U\ G#bkAI /s˰5Fv"֏g Z؆Iu\Cδt4êV-:kKnl!} $7Ǔg*v6*>j}e~3]`'1 v {g۰NSy|'x~Q@>_S5 xLA1GtKBEx̠\CT"?!-ۻ(!%NAQfg&K2R?D* Ob eK9yXpe 7y'AoO]MVYiqr.ˀQ64ӬDgɻ!g0 ѸO-cTo K+|lW<('YZ_n",yPr8{o~2C.F'1\G%k^*]5nh[y}5涧R х:[c-«?235_d<>Nh>μ)EZiaRG'OxO=(H9EΞ] A2rqW41c$Z0yE=X%UU~Z-)M&,B,UO~S~,",ezPQe}(EzUfX3*@n^DV\Z?-LYݮ>,MV-TJ8[Оa nVO*І *Xz=v惓ןˈZn3x;SZ+cMc(D/e IkuɅu./Eoq|%<#ͨșȾTz%$ !ͨLrKwʂxu֝zwp28F}KFf^Եi.C uPS1Zɑvo̡z}< Cۘ3TY&!R R1yiǁQ0ɔ4ݭ$ͽs.O29 endstream endobj 21 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 22 0 obj <>stream xXێ6}W0FRĒIXMҺmvcMJu[ICOPEh5Iq̜%IԞJ5]j?itFR{j=\!"Q٠Z@z $'"ð@os<ա<`4yE&| o{Uc,_MEڒeulQPc U ^V}"<ߝK9pRR@UP{RsUyJdR,n0ҔI34*|;(eTkr c^ _LUyYp"p,$H\V쐧*H,8OS؍єisS<C ʹaS>>s0(pPR7R(e ŠSӠ]A8_m"iJJr2)յ7>{Ca`gh APbШ?P8똏V͡YyrryqtozdQWKrr>ӛ@?@^? @뭔4û8b#ZpZgevՇ9RgQ'8hO\]QK+HJsv1u9 ZNb3Aۻ/{Nbg%&twYd·9q;mڎfu:WHȢ2η*W%vPn8-^OB!u^V/rk$D $ύ@Cc $2*yFْ$q&m ==-ȨeZep)ÁzўڬHT#uVR}-K֗X1#OR|b endstream endobj 23 0 obj <>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 24 0 obj <>stream x.+uITDAEP"",U@zARDQT)D@-sɽy9dMf2ygfN%k@ٴiS;v0{vWn<=;?HLLnRSS̞#x`OifkҤvi׮s/߯m'N=S2|pU[nժUU8i$g .]Zd&OYؕvqK$wy27-Zpqƪ? +?Mt͛gen.\3Tb`W!>>~2{,s#Ns9zh6kw}˗wpy27{r@֭UXؒt믿* }t˖-/)™enWuC!o76yĉhVZ?g ̹]fԨQz/N``ӍPZ5"E}}{Rɀt$&&zJYYYo5j'(ܹSܹsc~3lQcǎ4؀t`zj7+T&Cxbrrr#tk+WoߞYBt6~x0Uܹs5;ݨ~M[טI&i߮m߿իW=mp8֭[WR%O&77nPJ*3zXBVUÇ55݈젪!ɓGǚ5kjLȑ#qqqqu~MFQ<Toѳ[`-6^//x[lYTD2m۶m[hi?'.l:}=7k, {ÖeZ*::~홧XBɓUO &hoipB_g̘DNHH?WQ@n?e2vڥQzh@ի fMwʧm||s=yVYl"Qzj֬`.[qw6222---==`QQQjk,ӴiS/$x|{[s"M?gpD\/C9ԭ[Vҕ+WvܹhѢ￿p e T^]&A:ݞ,YD޳g+qKu+=?0-ظqjHpgϞիoy4ưXBJٲeUGjoW>mw_OcǎG,Q{Ż޽{~:[xbF7b rʩGޤcǎ>Q Tq{ڊ!K.^O䬬,y{ŻzHEjDv]/jp5Le tߎb=zOQ! *vu%JԐ+VLω\|yy߽$T;_revLd2>"͛7/2:Zjz6n۶M{˧8_z-yXtt~{̩RJkT0`!\/iԨQ-EEEqe D̜9ST9r&;vς }joSNzKz*6lXzzNI套^!e ttUl79w|eVػw['rvv9NFM~I{*l^'?/,v+6+Vph2:~mճv*PSUw{V^%oDrʀ|gts+Gj%, ^WgϞ]jUfܶFwؖ2:/G.ڵk}˒ڃvԨQz._q"]jժ:*TPӻ4y̙g^~ݻwwaMX,!E(D2iӦMTҸqN>-oz]Ӊp8D0С{FJ ׭[չN:)7IIIp႞ KzmݺUޡM_c ]uСC}˗o߾dq"4yW^yw9uT7sݻf͚ L8Q$3gv߾}999ۊq>1ưz֭ۮ]7y-K(!y'BTԖ+W[zt˗k2rss pÙsZns~m'–enDxX2Pf29RbEݙ~a^c uݻw;u 5IIIb&۷ozPB=t͛-Z4uT1Ϛ5kŊ~hzvb%^W,Xehc BX/CezXl/x[lYTnVZm۶{VR%++˹J7{PB~[}ʕ ,gi?+{jo}vO>:'?/Zw 6,_8G (Pxwuر@"ݼ^zC}ݞ5fyׯ4lٲ ypw}LKKKOOUTTH7Xe'11+rM?zO֭[܉a&/wW_}q|(Q"99YuӍmڴǤ*?4<noݲeKB)=zزej?VLIIտ֭[{:tℒڷ `'\֭+^.^vѭ-Z/\pDpҍj/"^mݾh ۛ~zU}ǜcΞ=;?n XACN:UX1#˗/?{C333+Tczٳg;?wepB-iiivҸg4Mbb3gv옎;`7nT,XP\z7ߜ7oۿ08 2D-x{Oq̙#رE-oծ]N욾;tɵo[RJ={b>-"U+! iF/zgP#|G۩S1f͒_all7_vxbkAFÀ~IYD-7v߾}09Řt9rH\\<0N"MH#oٳg7n,dɒ?3G8N7dq.]JMMSo~"ꆧK^}VZU5K.iӦy:~(VjXϞ=}swX/'z殗S5 y?F"#y/}jܘt2e<@f;j(`Q3Eg+t*zje|'r͛7Ͽg7q^d.b흇g7~<`ҤIzdsvE|/*^_6m*oUpnx ~_XS:sĉK,)I~cMؿjX"E\/LohB57ߔ?jOza =Ln+뮗U`AOs= /mu'|Ry7oo9hSESxq+n! <^1v $[]rG@=t7 SŦMMMM4NtsȑxYڽ{wu=5Q.ZH9Fqʿ߿nݺW^՞j&‘y*]zzX:݄zF3ƍ'q?i;vv]FF}bcc};vs;wNkrr*x[]z'N@=ԴiGyD/?rKgyvڪ楗^%JtRoe^,X+T?%+˗u)Db Pb ne\zUۼ &[_Miv7뗜l)W֭[==JO7~!7B<ݘx.\ǣ M7{U8E qOies=%iY֣G_=Wy~iڴi*e{ӏ2X/#P:/edee 2 P=:w6رc7 zROMLL5k#WntR޽/^Λ/ݘx9o@=F{V:U?o!QOq{꛿~s+{-[ʕ&w{{2U!ːe^F@nu@8paN[K/; v΃`_};w·Ôt{E!G>=souLL KCta?b-^Ton۶횎t Gy)o_bUCvVsjjZ}Yy? hc yo\X/C?/ܻw5kʟ̺$ xMr\WccoѣGy N7nެ,ydttJczsy~e[kҥy9Su}rӧ+^zΝxM7>])^/ۭn;|r忻~Yuȷ~w֭[Fc yo\X/?":ZYf!ݻw뙤U(P ==n{'7mڔ-Pycϝ;guI 5{|* װ[o.$ߏ\+a t^2'i^TMxwWXnZJVBoB$yT 8Kݥ2X/zj_oyGP9uꔼP|Ԙd)\dC$\kHބ… ඒ_re 6/}%J=hV=h;Ee8hvU{zӍ &ΠC*Eu6+ʿWvC= hck?a2\)**Fz3f̜9s/^\ABxnIVtѓnyD355uѢEnOҍ8˧q*?^ުK.ԩS}e(^e!e9rD?+$I7+ϊ+WjiڵUVws,3+ݘu=>aذaSE8Гn]9._ukh+V[n'N\`//v޽H"aF]ٴ}vyYfil2h ]#zz~zw;vl֭5~=5xkI7,{ qU_uUwBٮ_^"55U>SBu˕JSn'T<ڳL7\餝ni|}'[]VZ2;.edz:yEM:u̘1Ǐ5k֊+[ Šhݻf͚ L8qܸq3g%}߾}^{)kyDI;ݸ %NC|Zp22e sc Ƨ'_/ь+7;w裏4v9=sj߾M*V]X/#/X/2xY/lϬtsM%BZZZFF… L G˪'OvC=ýxzyzzKQ])^_f̘dɒ>HgBeeatzzyzzX7ݘuY/?ewY/oaqY/^Tz/C?O yAnndt\4 VAnndt\4+YX FF @nQd&X FF @ !LGiQ`-0-0IFind2Zt& BnBh!Qd&X FF @ !LGiQ`-0-0IFind2Zt& ֢n͛W֞y7t@뮻-H72JE#ݼK%K̴[nW^no80v !G}tMN &ҍkN7*U2 2LNH7OY Bnndh#H7H72Zt\4KiՑnd-`:Jk!H7h!d(M2Jt##cDGG'''WR{]pŋ[)SVV^qDA<ŋoٲ1c=j]^h!duʕKj_|ڒTtiӧό3ꫜV0GiQ`-/J_|k vb1BF @{Im6Ն0GiQ`-:_ЅSN92p?~wBh!iiiztMɦ(M2Jt##C.lw_.]7ozA3fs+We˖ULLL-ٴi# ;vQqqqwBh!e3N:۷Xbn(M$4ZH72ҍ1T/t̙3u_WDk*G6n؏# WT0aB@n)ܢBJ%\'rZZݻUD@={(۷oW(M2Jt##C}ʿ6j~B:tPҥKo#4Bh!"++Ky"ScP )Qd&X FF1 5k/bС~{o#4Bh!BnUvySIFindch5JѣG{oZ-"P*V< .ܧOq۷/77W{[JS4(Mҍtc /ɓϟӧO{OZ(~(h!d2nܸD=iӦ͌3~7RIFindcB }񻅨\r222ԩ:n\\p_2Z@zwܡ][ƌ#Ci s& Bn۷W]n!eff/h!d=iҤ"EhO>jCJS4(Mҍtc =/111$< x BF pW^} &Q}&0GiQ`-tҟ9t ^̙m(VXG}ȑ \Bh!*;;O?O91S49Jk!H7"O=i?a"BF a~)Kȑ#49Jk!H7xAήQ=BX-"P=yO,¢[u6k֬MB2h2ej[JS#(Mt#x[pҍ1;ի+O-EieʔaΝ;h皸cRFFipetc =/˖-ST"ZBz'xBeϟ?:::11dɒZh!d222ⓐ?~׽Qd&X F5<\W\9E5kyf-L q뭷p]j< 5JLi:u|/v֭zʯD2Dܽ>=H72 =(Bȸŋ-\Di]N8dt)ztի7m4xt=H72 (BH7Ə5%J?N~${ir8_|c=&󑊉o' FF={ƉBF|&/(Qd,MYYYofFt۶my3Bn.]4h סh!d'Q `q4ɂQN8O~ I&}YpFnrssZUV}k(M3gΈGyZdddnݎ= ܐnd2 ߱cN>Rd.M.\xQƍc!nd2 `:J̘/tyZƍǠ6FnBH7(M2#KӖ-[Dgܹa\H72 `Z0IfpipB~+6AnBH7(M2SJo]X1qCH72 `Z0IfVi:uTӦMšx H7iWʦ (p-D2e T\H72KSVVV>}=j(=4MffxnfSKn߾N BԫWOl*[Niԩb}jk=LnBLo!PdP4Do&ѳgϜgGnBB͆liڶmswuו+W̝ BFF @H 3. Ҵe˖;?;8t## BIRiIIIb>F2{.Q0-,Z +bD`ҍtBfJҥiԨQbÇ7{"0FF @ !t IfҴ`1~80--`&K믿.&ߥK'nd2K=Pd.MowiD`ҍtBfJҥi˖-bvqH72 `Z[(M2K;v׫W80--`&KӞ={k֬iD`ҍtBfJҥ/oذqH72 `Z[(M2K[pCnB,B@iY4mܸQLM6fON7)))mJ*D^[:u괱)[=nd.Mk֬{͞n^{6yd7t@hkk!{ ,]-Z$&ߣG'hE B'K=nd.M#G80Ff.Yiѣ… ͞&fڵ'N|gfϞu00@s<駟^dHK=pѬJm&&=G#݈kP5kVEj$3bR͚5I7[ݻ G:.O0x`K=/Y4.]ZLfOʹ"E.tڵTRk׮-RHˑ"ߕNw{&^/2C@ݺux Ϡ'׊8cD\ӧ{oҍ̺?2{.0%ֻ3gF4'Z8-"6)vÆ 6!Ww[lwXb52.zoI7pѬs) =fذanE$ŬZ&nW-/'bND˻[zo .SizoD`(ۧq+qU]H* 0@8&w4L7miu[6hN)##C|ѢEfOb8p`}o aÆo1SOo"U`ÑnX1ݬ[rU5k׮uW䋜q>"xDtju[6X|>͖/_^|Ϟ=fObرc~6yֽ{nݺƿ뷷=&>&:!:Z[Vl!DlJSll2̝;K$Ųe4e +&_E.TPNNs,n4nȑ#iÆ +Ws8 `+"h߮xߜ`+&_EӦMڵ'G `t؞Kͺ%"1˗=-L͊+|ɞ={sS㷠H7BnD6l]w鞱cjˢ-`'-M\4rYqE>ss!݈p|2~|U>I7,BnoظqIQe$tFXVl! +++W9O7VJKKzk1͚580@8=ztm.(;s|Dٲ233=ˊ-`3P|e$[1ӧ=F "^nÑnC QV-["g„Ȏy:[f¡4efsΙ=駟/HHYzÑnC Qz/fƌt,˵CiE=p=̞at3o޼bƈ2e kt Z01Ffzs(PC-6ʧtbŊ?Dyu~'ZlsڵkE?o?tYfttُ `s]tyu9sfٲeuJ.={lw~}//^\`g !H72Ӎ5jTdd?c`90FFLGn.\ЫW/q訨'|tXFF @nqѬ̔fʕ+&&&FVGnndt&)''gܸqUVo9.lt##nQd/MΝ?Sf"ׯŋuPҍtBFLGi4mܸAC8q,њ_> C"H7h!dt&YJSvv#Dկ_?.]4==]%22gϞ'NH72 `Z0Itoy{Gw/^\J翋> ÈFnBH7(MiӦ%$$8#LJJիWϟ_\9?VPW^ CEnBH7(M#B]v(M2_Kӯڼys,XɓA}ҍ,ƙkV<_x>^mBn>gq'&OIOKKt5 `?s&77wٲe\#*Tٳ%8UVժUKE4k,`o&OI܁pDt##8sMjTQG !;|M4[M6 #^^4+M1cho$''~ٹsBG !nsp7z|~57nY`'δi8kƍY# 0gϞ8r7]ڵp)˗/wa֭QdyhVZN\x+ҍLgq^"^"ꆯ5bo~Ј eK ӻᆱ}B|j!>n.: ܶmtA&'.]ڰa؏<}<pt#әnlZc7˗Ϗ… &7{7|ovُ[{ǻ cix|- 3BS q#G*Tө*n'K~5mڴs>}q <؏Fqljꫯwg}gܻv#u|KFisrro.JOC0ndL/a*UrZԨQךޏÇB#' -̏… "+VL.A/^G%J,H AB"#_Si.N;h!d~Ν{RRRT%Hx4H7ҍ_޴iSÆ EݺuG5h!dyl!Sd˗/$#ZH7晛zo]!BBdѣG2$#ZH7'!fXn]Q ĝ7B?'55U*BIFndyO7N"lذAׯxiӖ.]Gin`-Yҍ?ܜTr.\POnNjGKעԋ'1c=j]Fh!dAj!rssC(M2 t# x'YC)==/pmtJ:'O6 2ZrҥK;uT|y% {RRxo>}̘1㫯w]IFindBJJʩS[nƏocs2ZڳgH"^OsyE{u s& Bn zMT╢yɪW1c8O%^,#kK6mBǎkԨ:b\\/bdk2Z@9~xZZw]4(Mҍtck]:s挈$ʿ窧rdƍ8 :H &BF !BA:uڷoѢEbŊk]pCiQ`-0Ycʿ6j~n:(}R C !+*O… Gܽﺄ9Jk!H7KA O5wQrneddԩSGuܸv?h!d2n8O$99M63fn˻.a$4ZH72ҍa}zjB\"effBh!իwq 4fu s& Bn 7W= xWBF @ٓ&M*RO6]0GiQ`-0t/^ڠA 9s4.B$bŊ5o|G _Bh!իްanDMx%Qd&X FF1mѣG='ޅah!dA駟jy)K4(Mҍtc!"=nBF a~)ȑ#]0GiQ`-0گ6lPU<➾KKaE2Z@ $ԨQtc 频^rL~EX/:OLZL7ڦLږw]FFi4oFŃ 2L|v"ZQc).n`!OJJ"ݨt#OJJʶm~{]`xҍ34moTcpѬfakBnҍH75j0`@zz~8p`vD=PrQ6lsϙ(ላfe\4 {۶mKQq|n+ /=K/nT\fҤI.ڱcُvhVE7SL!\Ft_ ~[8p]w%N1ҍ+݈;wժU2ͺE7t#sAa\ҍLn >G #\4">!ݸL7ؖӀ@q6Γ˧߿smРBz|tw:/ZVZ: קFYOh&LԬYSn> _&ҍ 2קYOh!N -U4 P t# קY \4t㖜nקG07tå hV"=zT -Fdiii[l11J F6̿~}Zʕ/_lcjY*?!ݸ6i#`=72Of>Eڸh!Kt㖧t3B4 AE^q,BN+&FnקmݺB4|OfY&ܐnH7_VT)Os -L;̿~)ȳ>k EMn?!ݸn_>Mtkf?5xB5ݼ/~TY=h!7t ժUN7SN-Rקz7uS?fKA*Uĥ @qѬN\44x`B}N7&&&bcckժ]>h .]dc Ǐ*Thnϣ Ǜ}Ƈ(t3o޼:u5<.ՉfjbҤInxcjtN7~'~x6hР6mx:Ə?ٳgk &k.^¸hV?.Ey~y4MyMyi@qѬ~\4hhw 6͆2`I\4+.E`kҤ8f?@მ`Ro\4.E(8zxホi;|pczKA_Kp}ni ̓a> E \(׏/\{/EAa> py0ק(J>1קN5hُ<x0\S4i3D` OYPX|pciӦq}ܘKAmb0nLig44@q}̓^LAQB@ק}f?C9rt\ {/f(!;7+]4ק!<4aÆ yc ![n4h JNNy'NR@ni܄OCXმҫW/.h6p}3dȐ*U{ \4 _eggf{f?;O 6!fYYYf?;3qlY /&p,. 5\4 fC`E^jСCo֤L#Glذ 7n_<{l^fwN~嗼L zƽYf%&&Vć7%Kt~ҥK+WVWӧOw;o߾a-[t8y P|ӕwTϞ=ݎ,**ʵƼʣ(PСCM+J}wT{arLr.\0H馛Zj"ڵKH{5~N:)Pp'OꟌv`ڞUPAU7o΀Ox6L[n%77?TRuڀQmnݔWT7nTb;N7W_}uUW^"o^9rŮ?1B'9蟌vINN߮mٲe)y8>/ lb6 ,*c`ETZV*HŃPBXL"pbp"AB)!Uspx.Όӹ>*\^}}Ҟ>>}|<LSJd4Meep%Ȣ=|Pn.r Dð"={Vv\AMv0b52cG0Ma d322b%Ȯ8q"kQܦH j!yz ScD^__O N%Ȯ}V{j^l|v]#y+i?(Yl)c5o޼|O_8EO t=AjWV͞vѣG&{/X\)Etl/)c?F@6O %Ȯ6qOOEaEҺ]xQp1ŋ{ش߇aycc\%yLSJX,5~ QJP OV>5صU߿~ZVVj?#hljs H$"]s`1Ma똛˹w2Y `{_Au޽+SSSU1b[[[ekee| )\MS6f*nݺ>ojj677 Qܖ Vk?y%J&W\~ݽ{*rjϞ==~Y )\*ÇWWWSo'߷ol`H-A;x@qqaOOo7ɼ9^(ǏֱraH$VPV_F 0(ߗwK{}i&&xA2A^a.Ǐ---ùs~mC +12Q:::=P(Cyi خ.T[[m-̽~F `t@i W^KJJfff&<\&TUU}1kO(Ac" endstream endobj 25 0 obj <>stream x xUl$@ #&l. *,":@‡ 2. n8*,g-멮tj|SUz+}Nqg !|Nׯ_t3bg[3fpzRaaa͚5uAf̙'O9dJJ׮]McILLLOOTRƍtlܸQQNenwo_C*uz\Cڶmo_EnN`? YxqֹsnbWѥ%--_`̍:=8LNN `Kf#믯[a{>enwйsgdn{혓'O5J 6enV^SO %+F9v옼N:@\|1*Sn n+5k 2\fРArd޽uXtW_a_rd27JժU+W#YLt^zAroen"7gEh"YDoDnrrr͛WTT܁& ?"dgg;_~qz@\DoD}eZڵl8ë́ ̷3fn~bn 4 vW^߸q'N8}x$Q+> ߰رO82m .sӸq Y<d/9qp'ZV^7/QF9996O27?lmw F)S !fϞ=IIIժU YU:ȑ6EÇ_s5̝;{jJݺu==:ݜy晛6mrz_Q>}ڵnʔ)sghѢo߾۽{ӳ 9rf͚ ˖-szRnqɳ>[n޼y޼yzj޼y5SSS8㌦MoΜ9[n >FVVO?LOJ.ޗ/_n8ҞtFt3bĈHɓ'{֭[ڧVV~邂N4hyG:%mھF!eʔPmH7-2Vx^} *DmB#Gdffjzgcƌv{ 5؆tsEGO222LStUIQ }ѣG˗/د %nlbm~~۷rʆ׭[۱cGpp~~ƍsss:,to6uTuMƗttҲeKݯ1+3f0_wqa6l؉'BmUTT/֯_?T),,<3;lذLG^F(,ҥ߹sM7*;j2sɓ's1]RSSq-?n>[->^/㣏>;vXF2W]uըQ-Z駟j,eJ:u7)WIII{":g͚%ǫرcVs/9772TժUP)['X\HI߾}7)W:t+ {1E_nڵɞ{r+W&''ڏ!՞emfΜ{O6|; Oŋn;wL2?=*a}pA\/_֍W _5ĉ? LưX\vںݻ7)W׎9ty"ٳGjժ rKƟwyVX,QNݫq׮]t-W81Jro{{"?~\099~w-dɒX8ȯe/ .[YbpaXoҷ~k*TЍ1\jժMʕ+[9֭+k=J_s=uwyGT 3X/׽}]MF%O u_R%yƘNe<*3GIj߾Gx_P5k^wu&MzWA=Tbpݏ)+V0d3g]v*hǘNe<_z%9,99+#P~}M*_0qDӖ꿒 U?[x2ӽ'L`,ς6m˓~혰SY; 4i"U^==3&d~~n%l}z'Nرcض*(33sڴiVa :[u/뮻|'O<{oذAGy®Ne<׬Yc8R%zh|^֭[ǍWzP?lCˀ{Wj?aɁoErr W_O>[@~OjI|+|^F(?x [IRRRrEEE&a Ǻutư? lڴ\rNPtmܸqأKnhx">J˗_.[`O?dx_תU+{,:pP_ƭcǎ W^^ӓ12& ))I& P3Ǘ.]:{gffʣ7o,wR/l˲jժwzR.N:<>/Х,_,w*,,kkÍgܹYnӓ%—eDnTx1Y27zvWL_45\ίel޼yʔ);w._IP3ٸqc؃Xr_/pǎ%KfϞf8uG}tŊ_~hVvb'^FX,xec CX/ezX|飏>;vXF38iӦW]uըQ-Z駟UW_}eƍCm{mYw}dɒAiӦnݺ*THNN.W\*Uԉзo)S8qps=Wwk6ԱT׬Yj|ڵun~nʕ-);P aɥ1cX$##KZjffɻ{}ر+RX> C{1$ݼ&ۚm߾}ṛ>*駟TRG]Ѝ[a?L.-[\n-#??_ukK,om%ntöo.ߡ&MTr{Jv~{`̯oVn`EՁT\pj{;vƍsss:_|A lׇСPRM6<2gF5sQM6 n>3/PTgӻwo9G u7xCoԨnX#{p/ʗ/mMN8/X1cȑ^xa:}nݺ\ur:;e;eذaX+_|;nСC_|UūxիW˭.\haٿ*U ggg?|8ryuԩTRrC ME޽{N8Q7XOtÞ|Ɉ[G-]Tpyg1O7׿ȪUb~g9,lN'݄}@vڕ*DŽFܟn׿k۶mMժU?W¦.LnjXt3zh7pᷟZKZEs˖-a999[ԩC?~k>IeD2X/2;Kٲe1O7G#yQr{ݾ}6dۓn> f͒KW{!\ܟnlכ6m?ԬYヒXEլY3Ua+ׯ_bŊ 4D o>[/=w!wd6e˖W_ݞNU8>O@2zX/#{m}wyGS΂ 8|w#k@)6߹K+3fXFD?kh*Y[?|J1֭ nV 8P5\{i޼n'2< ѣG/Y0`Eio]b?CxًѣDb^_w8~xCA\wu&MzW eʔyTq~:(VoFF܏Aw.΁? W_}ҥKbe5])))vn oED~}oY{:Ati?y8tlGy䑘Lq2~X/ O1qD+Qm۶U/q7Ӻu^zGLϖ*U^ N7V[333xN/R%n׍7y t/)SujG+0.#h/]vԩsJ^s>ӘLq2X/C2ѭq KN'nMVXXhX&ztjo&/բޚzPυVMȖ-[/t^xq1*ݬ]V/((8tw=e!rNi͚5C͕|r+Vܪ>ago>_w}r& b 7X/2bu^njN+zipl59dlİuTq}Wa[c!NJ7\pAztȰ07ipJѥ9Icj=Ǐ#.osy eWKun9I9x`r+ ''GȰ&]={ndbbbo++Kz$t9R&L6~.|r5kTQ*/={nUԿ#s`eȽβ^^FPIq7]$sZ667Wa :TJcD{gUG5uO"|<(JNVҍa۷Eg6mƎݟ>[/^\lYN^+$ ^ez@X/{z|zQ5W^WXnqW\)uY_߸$|շlbmQQ3ua?_+&TW'֭[CM/t*U*UL6IJJ2t2N^^^ /]t7\qI'ZdSbɆ 甛ҍ2x`À5j 2d֬YK,7o<ʎ+ pl"B0@ma)Dp +T_bEuAҥK"`ԩr޽{lҢE!ez Q:2t&"c8IҍJ1}lٲP;wUQKΝ>nq^o۶MKPZӟ?U'Vͩӯ yts4W\_~ӧO_hO2䎫ҀM~2WR]f:ISQBn_,Wj9nת1=xѣg8a 4'dZ&UM؀?V]t1ܤ^z>@X/#^FLzVlǎ%KfϞ7uG}tŊ_~eqE5_}ի-Z4}|p޼y7a?{p')ky_TR^/^dZnm+fΜi-eeozz)SΝSO BQz1׬PLQzQaw ex eeXtԽf ^Fq^FXݽf ^=ezZS@e\9">5+++~rN?3g!u{~r𪫮*--bh.]g@iԨѕۓn(n8㌗ѡC P[ouw/t#nn$ 8.  Ht8f%.n$ `ҍDGi(MҍDl@ !B4I&x F"6h!Q$Jt#nBH(M BH7 h!$Zq&o!H-8JDin$ `ZpI4[H7-D 8$Q-&f-|{1-5\Sh!w"H&xIyᇫUS_| 8y nmO;N F4[Mm>lӣG ࠰&Է>@\t#Q-t؀t#BYJt#nn$Zq\4Kiבn$ `ҍD 8$Q-tC9yn'Qgff6lذ{/>rvZjnenժU>ԋJ*;v۽{}BH(M BH7_$6_ MV?V%n c̙>E !B؉OQ`$Q-t{TӍo߾V6Sh-D a'>E!JDin$ML >\ݢE.]K=•+W{t^_}޽۷om^^^`+bn{k֎LOOo._>nݺ5iDw۷x-D a'>E!JDin$MLTZ56]R͛7cmvƍt=#7/*hoUPsx#ձB49:DiӦEX-D a'>E!JDin$M?~\6ݿEEE1 .zysҍҵkW#XA !B؉OQ`$Q-tS|tӨQCYܼXzV;B'fرW\qC-(M BH71Q^=tvvUo ͷ5&NuɡCf-(M BH71&yWΝ;~2ְ(**U-[vW5Xuj?IIIoBHvS4I&x F"ĉ':u*dddɯr,P {=9Du4hРizjѢ۶mh!$Z;) Q$Jt#nb`ƌ999!;mh% 2$W?AQwrssc=ZN|C&o!H:qī:n8.[j7u54~(Vh!$Z;) Q$Jt#nJNAA{ァ5t?|`0]F?v֭[p1:T\r'Ok׮=qBI4[H7CվO0A{JOO߽{w}s-(M BH71ÇC:sL;JQPP[[n uҍBHvS4I&x F"Djղ;C"m۶VYzvyvZ)))h!$Z;) Q$Jt#nbBx2e;~׮]~~lٲnk5o\;`sp3ZN|C&o!HЦsfmXjv@Rv)@f-(M bnMvg6m\wt={LII15SLۆTѸqc혡Cuy Ѳev )Z7S"H&xIQobbbO%''_r%wt+?jFڵk-[V=ժUssswa`ٲe19Kf-DZiXΧh!܀OQ`t#Q-\&nBHvS4I&x F"a=zT[h!q, ^GH7;wV_nsҍD 8f)M:ҍDXfz;3g͚uȑH7'Hu[n8q/R$Jt#n+Zl H3-D DTL 3I4[H7A+V8qkh!$Z "ybb:kbq(M BH7EEEE-ZH=BH@u,>ʕ+~ơ4I&x F nٳi-J0{ZTXeJDin]v3oΞ=-ZHMM >e˖k,XЀutX5Z:rșg\@i+MG}WnjӤIS޽{[ gt#n+_XFB":u6TZu…:ECJTҴ{K^yɁgZjf͊.H7.BH "/n}Iaiڳg7nx*Tp3DH7@XG9r$ZtDD5*15&)楩W^i߾}r1c8p VG#H bB";vϞ4iRln4I%W3))I?''gѢEsH7-D۳gc[JTҥ?Kj.!Nn$ `Zt8$P}/$&&:%w8F"6H7(Mmȑ#&L,Z?_"HpI4}gUKII4iHn$ `Zt8$_=:jԨDuܮ],"BH7 h!$ 8JTizײա4iw|txIQ/>U\+nj*SL9r[  t#9XnڴiSu+O^dnrssջp;Yf.] 8y qtO@-M[nd+$ `Zt8$9^n5իW;5 xF"6o!pѬkKC=dgg#H%-ϸhͥ  ҍDl@Bq$4)S|\Rt؀BrU 'Jt~Eoqz.p#ҍDl@ !Ir[i*,,T]R t:t؀Br[ !Jt .@ͪSN*8= F"6\B$4UVUuz.pҍDl@ ! IrmiK*k9=F"6\B$4M0Aͭjժ?s[n$ `Z-'(MKɓ'/R5n-H7-&ijׯwz.pҍDl@ !Iri ,yYg=zyt؀Br Iri*((h޼' ېn$ `Z-{&7LLL,S̞={ FH7 h!$OQ$>}y~NO#HWZ(MWJw}Wm۶9=8t#nBH^i!4I*M RSvs3H7-%JԪU+5᧟~&f̙k~ҧʛnn[koZH7JO<&|8=8$u]굑SꮵmnիW/UTOt#y4:t\rj~s2M"6t؀t#yf}V7o&;='N /0}{ ;t ~Guw?S#=BE>+Mj?}Lŋ'vBGͩ$ʖ\ŭV^m2t[ɓ?uN=#5wZ.Ӳ҆n2-u-Myr圞lei999N}QFt /S''ĄS _~BZ?t&a/s]e˖G=2iZ֟oRS̙jw[7H7wKw5M:=ݜJHHYreMH7 !T9Jǿ+> 'tcm!Y?nI\NO}Qt '4愌 MH7 !\颤ItvUaCCn 45lPO>qz"͈#Z3f̘k6;YjԄ}OJ7,=l0Ñnx1ݼ 48۔ /?䓥J'NL87!yXgi&m!biT\4{Ǐ;=ʋ[nC]ڍ7د_g}HIKI.\Ra͢K^l!TlINNTRe/i^)=jfk;,[Xm!?biT\4I?|'y4ݨܩ &hMڵk{9+#6b MJJJKK3ym!?biT\4;d5<'Ot-=/&.TyfIYbŝwٿunt[(ҍ*8ƍ暫znʔ)b GK꨻\N'CQEvv.]3U V5?I7 cFbnnRZevX^l!)/'96O7+WT)N#]&#6^}uiUvСڵK:[g4Esvjos|noh-;|8t_|1H7 ⡅hڴ 3mZbnW:Z4f_y5a9=8w馨(LUV:A<WߥstXk!f{&:=8f*u/PVH7 ⡅P ';]wXk!fۗZT~F6*@Jt&++Vh˗@nWj+ _hQ ;&Ka[жV[e˖U\~Ĉ JeZlf/CESNUDt/r?-_<+Vǯ]vM֨Y…69sy 60o!|Cի=mڴֹ8p&Bhvʔ)jڵsz"pF"6 Hj!YCeggnذIt؀t#y|,NJ// ͅ^D0ҍDl'-DDB>FiQ$`馛\t#nBHi!Ei\R6lؐf2tP Ln$ `Z%-(MJo]B5}0QYjotի?lqpnn,~[je}&\>}{nJJ s6loݺ~…:tРA/YiӦZ8t#9n~鴴45>}mVDfŊe˖~:?ѣGw^xA3 ^9d"m!z}M7Yb;"5J*o_y,3ҍDl@ !nQm۶j?III{ caaʕ+7oK/tͱxH7-DGi_~i@"8_ ?6j(իW_|9 &H7-DGiS<د_cZjS… ?qt#nBH%n$E]6lPzKLL Gƍ.]ZPPP#Fa9vشi~B !<ݼk]t믿Gi(M'OUZq-{%$"Αnu^?3 4XCbd~B !0ݨ\%\R$EZvue\4j% &k>_oBH1I7&/nPJ˳ MŋKID#HQe˖sRB~0D !'\iӦ.]v14IKɓ'׮];p@[J7~ O%F"rM &NX2yRoE]$חsFV$Ew7o1c֭+K4ҍQQs…jՒ'oŊ}yqcǢxٿ֨l޼Ƚ룲2*K.]95-*w=5jԭ~G|ѢE},?ARD-'LPBPS8*.Eq\y}O1x(NQFEq֨(E08q?GwDwܛ6m+uڋGZKFibc%xb >|8VH7+ [X [&MD^_GFs(h'O BPʭ+W%H#GJ|$B@ !HTZ_ג{On!80iҤ,+q(1JDnzmڴѶ-[,**-TBfjժ;v,Oo&to!HYͳpժU͚5 vj}bB3y`ƙ3gN2 P$ t#'l2 BH1l!<2NŊѽ܀P$ t#?Pv֭[Y{|By qȑGygߣ4Ix FU b7h!j! cC(MBbnH-8JDin$Ml?3=z[z6##CU/xsO;Ǐ7\EHmٰaݻ/^XGZ 2j*#&^T g^^ݻ{|BuI4[H7& !}wuZիW裏[Pס:3g -D a3 $JDin$MݻRJVʣN7JVV־}[uL:Gwh!$ZQg Q$Jt#nbe7-ZtҥW^:t\ræ뫢Dڷom^^^`+zn{}D;2==~PGTu֤ISSSon3-ͨ3(M BH7RjtJ6oެ޸Un۶ƍu7z#Fo_TUCAT#ձB49jժDiӦEX@h!lFDi(MҍDǏkߣo2X.xj,\P{yjϱ:]jw4{h!$ZQg Q$Jt#nbBn5jt!뛛WުPa1vX+݁D !B،:$Q-t+ӾMggg##tҍ*>YYYOO˗/EҍV \ve͚5{pѬEB ^{-FG&M >zr|hĈW_}'ZtU6m~% qѬE ս_uU%… /Kݻ~aҍN0̘1C=DwhVYxBYfn`QWvv7.WFq5רStL74hPF?.5E7 t#Ӎ0Ob( FҦM6ǏwY_ҍ!mQԶ\Jy\6loݺʹ瞫cq* 8xɒ%M68Xn> ͆Ep78q9h ק4]QT4\4 _ܐn ק2|7IOE&h.tcHiÆ srZ$,4dplX\4 w2tc$,<}}Z5> Z<,<})} ZEp'/nH7קnP_BHaC=̿N?OpѬ\4  M0ݬFFٳsrr> bذa_ZΝ;暧/_>Eq,\hԨQ0|M7%%44J*մiSB:묣G:w *̜9<*_|ZZg$%% hbĉN?qѬE\4 3f Uq)Stm۶=zO/j#G+CDSN3/>ӦMSU}ӦMN?qѬu\4 4SyPMab4h:. OL4(6mڨf'$.=⦤]tE*8<7%KAqlhn{n7%j6קFr ⳗi(i|qcOthli@DQPr_]؃C<㡇RA쥤q,Jȑ#6\<،%קCn fmCQBlōSyOCp)`^lFQB ō> Y\Q|q8OCܢypً#(J q\~`]v<8KAC|iϞ=|q~{͚5> qnyp6ms=Ngq};@\/9rӯ&;v̤ypӧs)74D/n\WU  @ͺק!:cƌiذp /^z={^~ݺux~iYDo.ڌW_}WPyqƍˌ?N:'qѬ{p,P|q*\4 Df݆fpѬ;q,DܹsʕcǎK2224rssnO0M6gqFR*W|饗>Ckqf:tz#Ǭ,QFg@FE[$Dhř<řn=ڠAc5gÑC رcQQQq'˗N<۪|͚5M_o!p 1u9|pt5*ݒn,v'%%eݘ^{M{rر#u1cF RRR^xHOy;/G:mԨvK8sq{ s8϶9=2Wl8s >~?Cyf^ gѣc_[[+c7662OL*w~~t&%k,!WWW-%dF͊2y-ۏ? G=̳+6Q1oG"ٔ o߾5 0(=*xԩwe AjTnggb>@(=*ĄWp8@HTFb pyzK@n2qiCQq_~UZ{1YBjWn p2:: YKފ]0ޠbϟ?+*ScxѣzaooF\pN{Pڕ`4 344 Pv k'ϟ?ǣhii<͛N&rk׮%A9+\Hw>}TӧmX Qz_9s毴p/@j~I^+g'NNNʹgccC%|>өL BGNI~Kb%V7@6Уb\>0-4@T^rNWW2͛r׏z5ܢ 7djj*ybX@ҋeu?Ջ PEEE,Somm)~:YI1vww_|(|Ca2^Wl QzngQr/˵2azzZ&={6 g endstream endobj 26 0 obj <>stream xT]O0}/Pv'AڤR--mL() k>ȇ~vB2&?{ͱoсXoF:A`bX&! /sm8Ѫ6K %Z#IT4IsJ,&&ac<ff=g"liSE%my8+-\a7cMVc֖M<uKɿՄiqЅ)=DwoPkdPCvAމT^Z\.hwx<&Gdğ=n]i(X[j5 aV]\CƱa%!~W!TE82_%J P L>/XObject<>>>/Parent 6 0 R/MediaBox[0 0 612 792]>> endobj 28 0 obj <>stream xw X"]zG+MQ&**(pAPdiC]TPѫW+E@"eas$L^K|2ɜwf6@Y~}}ݣz+ 2)ǎKIInϜ9c 6+W{ߕW^in:ud۷G%..v<ú,ӸqZjqԩv|ͭX.dee?^ճ{K.DGG*UN:;w3f̦MTe&&&f;v52N7^k=pGNsӦMC-Z=7H7Q̛7,Z̙3u1 pҍ?=|^QѣG=otӷo!ӫWUz 8<enݺih߾n&5eΝ;7l0_nװxxfժUe.][o+I7ӧ-UTein/]ɺŘtkР47KtMŘt_Td˘xinrʔ)#d2~Ç_y5j}]G@Ns&./B:M@L&##c֬Yyyyvhr 0 p Mɒ%G=v in7M3r۷o{@:رc9rn&n5o 4SN^oSٳg~@$(_Z׿ZL|\/B>|m۶r 2l yN:~W˓+2 p y8p 66Vdv3Ns3n83ʛӢtԮ][dFFuo̺|\xPYnd ^O7-y[?f@6mm6E_^Z9s#W&::[nٻwcc&$''_tE5[g͚o>G @;ydŊ4y.Ν;WV-={݃?ի+ҥK?l-~~zg}sܶm۬Yzٰa *$$$\tEݻ3vk_|Q;C=Dœ$m /x]Қtt3dȐ@ݹs^z饦MڦV&M{FjԨ]{ t$ [}{ d{aA9vXJJ~Om׮]͛77kl"75w\2 ?(l{쉋'Nf…cʕ&ڛoYD@MtsTbMFcccka ͕W^i=:udy}E1NRRRrt ( mĩS/د ;l߾8(qqq0~^,SիV:gΜݻw/iӦK.8ݼM4x$ /s,_FmJƍuƬL:y{^͠AΞ=k5kT^Wͽ袋YHC1_/Lչsg]g 5ݨ젫!JVVuX^=Aݻ7!!AɨGovf 32>￿m۶*THMMMIILѱcaÆ-\p*e egg2J*ƫ+Oۘر#yڴiryu>}a1h"ݖ333 YV:_ꠚ4ikS:E}Gn2_zƫ+O6mڎ|5ȝͤoVVZd |r+VڎW=5&@ҽ'OlJ᝿^OEy9s999Y7wT8wnŊz8+غuIan,o߾7UW)WG*իWO]vp^/kntttRʗ/_xݣ3y4k&L ?#eNeDͭ[\|~.//oٲedvvn&%Ko8+8{l۶mURSS'Ol* e LUVW9w\FF|3F߰a,[oսU=*O|3bcc7lؠݔ}hѢ%C5n/ï#GXUV^[m۶d999e˖ ]WgzÆ Tu7n˛ NիWתUPK.׷4y~i32̻׭[g e ȿJF*_}Vzru<>䓀N似<Lt;#%J|k֬1ӹݻkWIKK;qℙGp|mܸQn3YjԨQmӛ6mZt:UVzg^{Ҙb_jժ N2E%Yf;wrC=dͰ5:_juzuVG7ސ[SL0~.IxciRre|W͛w!M4[_|=}&@:z-_S#ӧuk„ v [O7111u۷O?dɒӧ'55UaÆf '־-˕+yFtVR+r ] /`k&@xͽj&Ído^{p^}U +:ݨb0 e̕eݻZj3u]ƿd@s|۶m8qb/nj)Fi&;e ̹~ݻw;/>}IfϞ|Je43a //&21Y ̗a2a _,駟m۶PBjjjlllJJE]T~;6l…7o>w-t%J,ѪUw hAo3SOhѢ>*HժU{W4譙~۷owi7bKLl_z饦M٦zG=s999f[8ڝ\Xwyxlk]&//W_m۶!'wܱav [2--,f-5ꄒ >+V5!,t1rH; zk_9dF$x]v5o{l=䒪Q| Ɖa>W?޽{|kn׺u@OݦVZ%/NU/V\E^˅oc֭wؕnI& z5k'|hn{r%Kzmrٳgn޼O<)WOAdժUMWK\{^?>K*U|yY-tURRR*ٳf~˖-kpEl͚5Xn r@>"E=))\r!ӧ;t IOO^dNR%}/|"--M fZttMrzN}]|ڵu' pCҸqW_}UXvv/^|7{+t[짟~_yt1rHW\Qx[3~~iX׮]7>Z:u Λ; {\F4hD:׬YSzuӍ}Y?~x'Z2t3|p |^?Lrˇmn߾]XFFFUT~צMIn~0$e,Y,h޼yA6t?%˕+Wx[ b~s5ٻwoBB\O3y otc>rH-C'9"|/5h@wW뵏רQCnԉ(ߜvz6`r/.G Ըqn#FmP# 0_F@/2/C~VhтLͩS(OroܚtL6M.޺ƲgZP9Rb;v/|/]}*zkE*_x@}6"͓O>)ߜʕSuBu[۷v.j]|?rRݻ|p̗0|=ke|r*Ư7/õ_?^b㍇ɵkF.0uT3hx|T-o5ѣGv,{ֲw=?tZ/IIIx߾}{I7^Od wܡ}t߾}Z=%?.婧 P/#8̗a2Μ9Sxq_cP׍7cǎ3x+(o6P.ۚyfT[lٲ^T˚t$'' xTA~:_"EkԨ5ݨo׮'NY^Gd={Th&x=t߯hU\rWR)n!7|Snm!*"e}/ G/cܸqfӢE & 3ֵkB1O||[owㅝnGGSSS8V~K76>:u8p;J7֭csrr??tD(V!]voroVnGU{;k9===zH /2/îg|gϞm۶'F;y\[ kpAݷܿQwK*U6n y l߾].P<-ZT J7,&K/4c \z饞M73xc'&&z]뢋.-9h C/YW_}w۷/Fc 5/2B 2_ƙ3gF?7=zp,Eh^D <̞=;_!O7&izԩc)^YsW|b/ZjYdgIPn]C .lիyHe˴O*m„ =Ј`̗!fe0_FHuHعsѣ˗/uғO>)7RFU; tkn.ܹsMݿ\&a( /^| fn;VX1_ﮌ 狼2OE'3nT7fal͂t1~x3=s\266,~A /|_+DԾ-[V} ҍ2tP;vlb~ӍAo\ټy\xڵ*ਘ/=zЮ˫0ac~|rk!̗1_FpTtZbEV.m۶dpU+VL}ro1b 8_߭*xѣG?ݻT`47n^o_EtxauZ_~崴4Mgn~Gm4//f7ӼiSVnΝ;'oTˌ3k~)z׮]򯿯ꪀ)|rk!̗afx̗0˸֭[gf9BnyN:~ǚׯMJ T+_|[Tՠ.X {[뮓(;I7^O[oxxӍ" YfF>gyFE-ȃ>hH0_ܚy̗0og]HelܸQkʕftSHV\g5xW^yEn*66> bkAtc޶m gΜ9An7ݼ[[tD3'kFu_}ڨQ#9_|a u@/g|}zQ57ސwXn~WX!K/߄IQ}ߜ/_ۍ[lٮ] ><Ӎ믿VH.ΛH7MFt1n||ܹsn>Co|;vk-[`mze0_|?@ꫯ<+y<ؤI;]t: :K.5x[SJ,J /4k, mo˕+uOj2ݜ7<ʔ)ӻw)S,\p*vm ҍM6UT/dСCMG} q֘?͛rҍҥK7t3-DpA4I&8 F".Ԯ]W7V>r0݌5J5\c+Z"TvQ$Jt#nBZjڏ%K_U.77x]qi?~p&BH!cI4YH7&$(RSS;t0sCy]gjB~7Uc-Ç}!T݅NLLvn}/8Z"$v4I&8 F"݄ٳgZlH"&LrLm%>#1]ԨQ={lԨn v a`-DpA4I&8 F"݄JNNԩS322Cʈ#t+I7 u'_݅G !B ׎4JDin$Mh={7=zPHIIFllΝ;qqqVKMbb"*-DpA4I&8 F"O O͛7O^uFjڴ駟6]HҍJ4eʔiݺch!$Z1Ci(MpҍDcǎ>jpkSRRk/!!G !B׎(JDin$MHbx _feei?uY#''G7ݝwk/pF !B* JDin$MH\|iii*&|>k./ZJqXnѸ8_wn-D a I4YH7&$TNNNV]tQ.OѢE=]>//O7]x"BHµc$Q,ttclڴiu~֯\R@||={.-D *\;F(M bn&O\t.լY^zy}⤛ѣ75 'N˫Sv]3qƭZRkF4Y ҍ:]*66e˖^8&T~+W>\ʕ+.Bxk.33s^W4YkQ"*URa1(l\;FH7 i-D *\;F(M BH7_N(Z"vp, NGH7۷Wȑ#Nh!q, NGH7k׮UwK6mɓ']t#BܹsܸqGh7Ki(MpҍDkܸ^@3-D oLMMUL3I4YH70 Ǎg^5ZȖ-[YŒCi(MpҍDLkԨQ_C !Bڵk~)VX3I4YH7t.Xpa7~BH@/߄*P$Jt#M7Z)])SN>-#ZBϞ=eINN{@Fi(Mpҍ7r-U\PJi[j;x=h!$-D˖-vھJPbyC(MҴy޽{?os H7w7c QDGGs֭+BHӍgf ~gee噬i&di?ȗ)S[n۵kɃF"&eggWPAKjjƍ~ץ̧-[|Ç{5%K8q*k&diڻweFݮ];[j_ O{{7oCH7I 7fhBHɓKI4I]v͛7[n%Jiի7u  0t#n3nL^ѢH7@&M5ʕ?NAo$4|G#Fϡ:tx7~t1gΜr-DpS5Z&)TI=z$&&z^&MZ*77[H7ԩSC5D !nF%ƃ$49r$++lٲSv{/?H7_BHӧOתU U4ITΝ[JOiѢŧ~] 2n$ `ZtÇ|&PKSNNΒ%Kʕ+u>}8p0vAH7h!$ `;JdAi?|A*UjժU/F"H7(Meiǎ;wܨcǎJtXB"4Ig}655Ur6lfpҍD,@ !nQ$KӮ]Zl'++˲H7-DlGil)MΝ?~|LLwy+G3H7mP+v5x}-DJTXH7_.Zڻ:L)ʥ*VعsgOtXKyץ°A-M[lQ=pve,ܙ&n BHv&믿6mTB ?]ÀSn$ `Z7͆gi:qj0222mfHH7F p4R+]; F" HaB$Oi:qD֭`jԨqa0EH7h!i!Eiª4:u.Siڴ ;vt#n BHaB$[i:x`jԐ:ut9CH7h!pk!Di°4m۶-55Uj„ vat#n BHaB$gizע6mdX^H7--Q(MRؖGyj{,#tXB &)lKSNNzE:t萗gp.H7--9(MR8]v(QB ovt#n BHB$yiZt^R:dXH7-- (MRN: ,n$ `Z)[(MR;v$''A_~tXB p=J4n85=،t#n BHh!w4I(MOX lFH7h!$GQ$gyFZj999vv"HSZ(MSJӹsjԨ=؉t#n BHNi!4I*M7*㨤cX`ҍD,@ !9܊$94={rʞ'bX`ҍD,@ !9܊$94eeev6tXBrV IrVi:|⢣onX`toʕq)ovOtXhڴ_?ҥBDW5|ShROE^8q Q|DrM Fr\iՀ˕+̞;$ `Zq->&ɉjժj} H7-pnuGi=zzRv6 Hɉ-241W\tXt#9\$94-[V {֭vV#H[e(MCKS~԰Nj@`5ҍD,@ !9܄$94y}7=Xt#n BHm!74I-M۷oWȰ{ F"BnBiZTFsNKn$ `Zɡ-&&ɹnP#_lH7-p J4~x5RtXBrn IrniZb?,H7-p JҴyf5=Xt#n BHm!נ4I-MGU#/RH^^cuH7-p JҔ믿=Xt#n BHn!w4I.M5RCH7h!$G;P$GVZ{v!H[(MK_n:n$ `Z-&ѥW^j/uH7-pJԯ_?5 =Xt#n BHn!w4I.MCU1cuH7-pJҤ^J5y=Xt#n BHn!w4I.M~s=g@`ҍD,@ !9܁$94~O ~ʕv1N7iii=\f͚F~[Fup)W;n$G&kv1H7=\WI7[!pҍԲeK57=X D, `"29܁t#94UXQ ~ǎv!H-4t =X'BիL̙3¤nΝ;lذҥK:\fTvܩF^BKE-[j'fϞQ9H"E+T&UMVZe<q qm5 Ǐ>ᢄě:'%%6`akD/94j-[{ oedd-z*wwJ^:JFQ_vTb]g ?z7n<|C'Fo!*dŒ3|-p ҍҴ`5oR駟:ݨ$y[ E_nUH7m۶ :TkR-(mnwn F䤛HijӧO{ ѣSz5ݜ+b _n DB)[lg)>QOGBMn*M 6T#ߴi\nԳHuV]l*R*nʘ1t1qࠁwn 7ͺ48q"666..ɓvrb2dHFYxݺu .kpKB QI5 d; `'5kԨQ!ի=?3IiIӣGE;(% ~ġ-&N,Mf?#5 =X͉k׮\mֻw_|k:][TA+Nl!TlP||6̝;aˆ)RR˥?o/84*Bn}'԰ `@`556'5vXmXnK/dfwNl!T3~^ƿ9o-&N,Mf 55k=X-rҍyNl!H794qӬ믿ĤG7(BGѧOun[ ҍ*8Gݻ8qD}9\ơfu}Y5뮻nTGQdrΉ#FD7iTf9㳘tX-D@F-ߠA-RŇT)Krb L$@94Cy֬Yv6p}YbEREz+///{Zjj; `Hh!NTԟǣ*WN/'DBi J={=Fw)'b׬YkwBԯ_qÆ(m<9kNpH(MY%wO7׮I7yyQq+W; `Hh!Թ_K93sk}q->P}l^^^ZԀӴ{,ͼyJJ.^=R#B5 _|Νj}Pn;n$ `ҍp+nuniccc!GH7H7SZ"4ٻ[o{ F"" Ni!4I(M_|Elll||?`X`3ҍD,@ !9܍$i:w\&M |An$ `Z)[(MRsVTɓv#HRQ0/MRcAX HR@$4Ia^vײe˼<ǂ@H7h!0o!H@i¹4͙3G-=={,tXB &)lKӗ_~6T>vat#n BHaB$gi:}tf͘F"³" I Ҕ׫W/5jժ?~ n$ `Z) [ P0,Mw`wiXvH7--i(MRٳg$&&~vt#n BHB$Uizx̙cXH7-V-(MR7x#iÇ{,_tXB X&)LJӪUH;9ҍD,@ !I D2JI sCڠAl _@fʕ]vӡgϞO>Ʌ333kr5k4iHn)S\K/\.⮻Rj&;wnӦMM.9ϟor‹/_Ʌ=-:&v8@"HٳgLA_ (,_hѢO@}Q >m۶&^zzW\\^zv2BtM~Ʌg͚Ure ++V3gɅo^z\xe˖5P\t3dɓ'[?8F" HvɮtsСkF:99yʕEH7H77J4[o[tO>]H7F"4I#FDGG^vewfp ҍD,@ !nQ&_  T'6f̘Pt#n BHv&)ImM5ߏ92l0oiӦt#n BHv&)TɓMmذAﹹ/\8pѣG /D2ҍD,@ !nQ-[Ԯ][mdɒxjժuW^_eDD#HI*`i˛>}zBB'W3g.\XjU?VREfDHn$ `Zt؎$4:txg233Sߚ5k.]_@n$ `ZCA(MRХIŋF_x~/bnnnad)ӓ'O^dIH -t[o]yN6I 48qB(onU5($)$ܹs˖-QFzzCbnB !0ݨA=-[|k@4IoaÆ^_ȑB}ҍTt5?c\B Iy=ƃ 7@@(MR@gIIImFر_DD8ҍt}sRD nVXQ~}m#ѪUpQ$I^JпEi(M?[j*YTKܫAJ7~Ο?RJMOO?vX|8}t4m۶}?x}PVeɒ%>&塇z pÆ {3([l1~#BHAM5( 4y8k^N q}w _$p 6%K.))۸qcpq& :xbbb{^ /ҍd2xkgZ4A*#roD !B|'~¥>MA&B''OT͛ *]|*d/dٰaC*UlvHJJ FU$lРAI^~}ڣG nU:owܗ'N 맞z*.\0>h!ZÇ;DNU=qT\ СCp)ĉ3lذ Y7F'γ>܉E?8k[n zqԩSQXsTu}1"///$H7;rrrGXսu o fϞ=A|( B nR [˔)#KǓ'O+I*BBUԉ|ZvBHAG}t%Hx4I8 F ׯ_߬Y3mkѸqc^BHl!dƹO>p7JDn晛r wj}wBB3~3cƌPd@$4I8 F*HS#lܸ ph!ǎS'==]) JDnj֭[״iӵk|kBH!o!NN0a߾}HEiH7pҍt@h!B/TIo>d kRRI'++҃y(M BH7wB_~TRf*@FIKK;xg- ҍǤI>$Q,tB|w _h!$Z:|1hO I4YH7&Th!$Z:{^%h)Rd„ %J|GcԨQ={lԨn v a@i(MpҍD ;Z"rrrNy1Bt3`_+_Xfff|4I&8 F"݄ ߝ-D QT!z7G}UW2 Ν;?3(} yILLL I4YH7&BHMU>Ht?8ojpETA۷o`JDin$MSxBH!'|==u_Yd Uty睾Bq"JDin$ҍ54BH!t>PDЮ]tj*`ݺuGՇ£8I4YH7&Th!$ZR&tNNNV~.]w~e~P}=zTq5kذv{lj(M BH7w0@ !B66mn]`ʕ#@q"JDitn!nݺSI C}Lƚĉ~*Au.3p@n$Ju֪4t~e܀NaBr-&Oi! D@~wU(ԇK6mA}~ƦԮ]ݻw{]L5x$&&?8F"YVZHoI7*TP'Oq<;7FGGnT rzӦM~qEH7p_~E5Ŋ3tCߝDqT ۗ_~iD(nH7p)Rtҍ*>iii~/NkU %hyW_ݠAq7J4 K(q 7ntT[˗/q_( ҩS'UOh!3_+ pӬMpυiӦnt%Kv\ 2WF'?ݨӯ_ڵk9s 47<n(FO7jø?  7Inf͚=vb@Yi/ +mQԺܟy\wuנAL.fq,–pCWi@Z p+PHi/nE2du]$Hsƍ~1'zB2H7? (47"}x]~KKK.9lذG(^x~ٳgx]W}|]6!9bqLLLԖo߮[歷Xbwn+Qk»+曵TRĉ d| fU}駟 2x _ԩSߥqqqWt<ѣ[ꪫrss=vX  ̟??aF n{֮^zӧOkxuxw6| fI7MJJڵkgݺu8_Rw߾}'MԽ{wJ,o6]NMh~]{ DOp=|pFFvGy$ѓ'OV\Y*G @/a,6zg?gϪ^rmfpvY%K3fѣG6|g'''x fUmܻwo@["';uCwefDq_- ^B,2ea!bQ(3O EH IE6PBLڥu;>Ywq|t3Y3|7,ѣGsdRo4i]X(8z޽{UUU[[[:[/X۝;wN B~(==PTfYdOlbAOnj.D1?iʷo P tJ'd4[ٕ+W'O ޾}kk[XX_۵k2#G~(p!Aqu0i9QPӧOr8eh={Kѭ^iT* ɝvm˗?N PtǦ}ZDdM .Ȗϟ?EhTkTyzb-A scUVVEv&G(:au$ ۷o9``tt(C={^'׊rf[k׮ݻwO}y먑+.wɛV̌T*+++FoE1Upu0b^*<.@WV*7,y].[[[ʽڮ.evŋ{7w+qu 0fW  |4 2СC|^EU`0H$WWWM_|?ڸNئFFF5@.]X޿`}@{ުp8l#f1ݻwfM s֖ Q M(:abv:abC{0 endstream endobj 29 0 obj <>stream xWr6}WL_$Hb$[N٢t>@$1]~@-t<b%x6aSf(0g$5(_ ˄[& p{j)Ǹh$#҅QB)ZQKgW"?LUįf8epNMm9P99k<5 *^+I(!ҟW$=g&Nj\sVv& R%Ԝ˵: 9cb/o,P` RLYՙ`Xj`Aϓ՗+"h>z`]Dk>nV@A|Jᜏ!AacxZ)Z!Tnd<Egp1]mۗhX'+V ܵ5%QxO)#wSUGv"Jr@%OAp/tqD OOn,Z1 {|!-$J 4tj,6(˴ybͳdCq&`V|ӌϺu~[e^0Y+'H$2D˔s a_V*1eza7 sR,ۮOۮ BRcZkZvqfyȷ3kW>ܔdНoG,oڙoĤ|"M^EcH Ӫ$U׼{ӺdۺM.`j77k 2*E endstream endobj 31 0 obj <>/XObject<>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 32 0 obj <>stream xYnH}W4`a;G{=<$0TK+yOŦdQcˋyXXdשU̷D~ht0 ~|i.Z ,oxrMN|A~+'0Lv [bi 0bŜL0Ck 70!G1p |~vo@za`ۆ-~d:1|1VB56 O[tTTrkzD~5 щNq4,bԈ%J6ֆLmtC:D 'ۮ%@LSUIȱC)[jCsUa;a:HMTfz6ڐҭ!]N$Fݓ]잫 w ,}-DZnQh$uwic*ɠ:9~4(eKmh*l'LQ[ӝFV5 щh{ݳa _ *fQiCj~:tooҢLU%Tg#b/c%TcKm]0FmʭyqShiCJt!:񷒰K ' =E/K>d ˂apW& 284ƈ^h (/epD>ag7|AF4P3Ƀ{i#zyOC7&~` m?4LAFC[>}\\}~A1g{)P映뛋ͯ 9G(XbWÇ JOTx*ݝ+BlA>MVdI=/H)븢Q\-§} EV^1@IH:ޚEICq9E7 M="A"HDh0-@?N\-ARۚ@pNp4;ݝ_C|0~S8>c[sRW;+N*?=ϯEu II=vXy*{dz9eJI6T}Rڍ=x>^AFZ+UoFl)#xO9>;.!^,Cen%y~w.m_6d|dhAa#*HlCl]?Vh)Iy38%!uS˱༑y 7ph's8RR$1-ti|4,Pܤ :H'ıjA/B>w}cAb_0hruGP؆>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 34 0 obj <>stream xY]o6}ׯ bF i.MC[$,;#HJ\[PHt%s=$/ޫA!0{A$Ї3G^$ AEqȯ*L}ȮcRPFiFS#7Kiba&)0Ѧ""l!Iyփ:~# #d|.y/C;v#y ۘ1hY0uQ(A~$2 C?k1f5h8Oc$j#"ZD0a-`:vGG Z Npn-bPٷZ ѪJ8ReQkAZ~9\ω2UydPƱ+-%aNI¤lmqS0d!n!.1 mPn[m8yn0C$lCZGQ P6UdP"JR qtTrMֆU4N$hCcwBmd ?!F hIATJ2FCJR qtTrMֆU4N$hCcwB#~:LZYK SzN*$"ZD4$" '͆0Luf~pVT76 HڤvW$,~ 0keړkc=6&5p(kq3 (اL 7F &Ib8[0AҞK!! MZwRO5ij$04iL^n^O6{]lzX!DmP&)Һ#Y}i(9IShFz)#*=13K~hDn(=g8gk8C~h3*P)I/epDEg!;g]F䆢Ih6ml&ml&ml6ml&ml&mlM(6]M i C&5mDi CӖ$eq|=mA{-5={7 `P rbLJ007t8~f%K"(u|YoɌ,#$l41g#pWy|x78:>=E^Vd!O'PӪ{{~[ @F)-Ȳ$~A&e9};fƾSz_~gƛ* 6=yG>8! 6f#O*J%@x3_P8=cbiȄ򭄢ޝp<58}ɫRs &c>WÓNj,Bxd: wò+O;xUE%mH Ɯ68( =YFrݴ Ggsc7B+/RaZ1rޫ?țZ)zkMckiO7}A -oTͺ5Aڧ7@Q̚ ϟ:N:`RI[􍲮B>}cP*n(cםc!FÒ8j7-~>u, =կ5o}km5w|k3.;f& TtϱجsO?~Ow[?\ endstream endobj 35 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 36 0 obj <>stream x[s6)0~jf47OVjwKmLmHUly?\ Ȳ:ێg"?n_(N)>=zzS/<14ˆDXc^j(߽#0BPp xM Mh4PGn0-mث-++ή$މboqYVkF~R'*w6u`6![t@ܲ[hZtXc`övYaT LW_X_i{넠w~ ط̣@6/CL@O2ʐ&]ԷhSv4MmamXN#+ +n˫=_#՞z0tL}JQLޚ_?$aI#O FS[x=a~"@ #tcNQ/0=VpS 3>sT0TdIE<ch0b- rm9k#fA+CC+SI']v#fAs@+CC:EaAtL 4 4pe@ aU^a-kۻTmäXKơ\%u\Z#aFC4!p xԳnJ)§Q90G6D0 KqImp0OhaUaJrfy&Vgby&ngb癸y&vg<3_-N{yԝZ (DzimlZ+HUPd?idwr~AI7EVUgpoɸ,/Wt|9|+XxB[~&Ϋ)u5*d\Wd )L.et#eiI6cpq1/W~Dl'0ih*>җ 4㸹G740^e +T*'XLAy6,A."oy5]˖ZjXl$q]`2ʍټ P5q]nUpuSOT쮾r">d/HIxOEl;eM9y6|}tryWlEs;/{&uwm :&v0"Ju`?*\\\fRN$`%NP¥t3K"]"C뵩9\=n­ce6hβFh<$4y׷}С${.OwqWX'1q6No8˨8 nGDž(nɿEjf^=;k'T/fj$Ѯ5M gY!؛uWQ05/Z |KW[ endstream endobj 37 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 38 0 obj <>stream xXmo6_Ad)CQH݀tqMκbVj~Imi"eVg؇"@=G><9yM⒢n?t`q'@<8aEF*kx\M2IRf"p>fz 3H/9K 1+D݅(Vd20x`*AĘ zrBփu1$h71! q y^*fJ2^B]jYV9v*s&pF ]syc-6&bD TVV GC4xd ̝qs@߈} ٯ v[1iSnY.qN- 26r#$eƩMԙnylИ1Daa CAqf ,ҐFIX {,bw[mЌž)"Îj 0YJdMJ6hƌM^^J}ٯbRSL)ixm4S '{KoT_?=N s9F/q3B"=E L҄aH)iiDݼlSv)oam*${S0+LE9o ''kX3G' PP 'Шc (6D i3GOKi!=mcxQ]5^ۋp$< GUu;E*8w9zvx~zv^߷O_w_v;h8{墚ŠBդ7'ФWj4RѴ ` ^街hZ~T߼VsG9rف l*)m1bd;Q>LT?63ME⽳vzh*luhu(|^'lhZTH'՟ }f V :vvi]M ٦JF۲6٪«*DD?Te/ {v endstream endobj 39 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 40 0 obj <>stream xX[s۶~\!'ױxu bGlS5z̜`Ae6X۴%(a3a+L *Qn-iPm[C[EhM}Ovc"iȡvAt[%婎S;7 >6ǩΦ,¬gTc-@ k*¶´Frv mG֐6D+VvhC`S] KA3dQ]!%:ͩ@yY*ƚ!A k*¶´"yq)l[C[Eء }Ov{6 [W-}/EN [Sm[Eu0ٛ Zmrm"t+TX7"ب?[eء ̥"lJg#BC5p 0Eg6Ջ"L]r#TlOi^Q"J/S%@,PU-8e#ՠm-hz[i4IzKT_%gb d;*2 COzGCȊEUt|8^ggɸNL5wEV>hxI;$eQhQQ~.6a2%(Dap{e@mMKr]fl]uP4K^"RnMXH-s]W6J>^5Wrk[*J=zDӺa'EWxPkk_^Ds_Yq[uHI{~?X)WU*Xu[>yCEu=4849/9.fF-*>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 42 0 obj <>stream xX[sF~ׯ0U(IZuMpH;iӉ]wsh?4JB{9h7Rñ{_a(J4IYYtTa6W_Z1>jP[%pqb]nXsa_YNd[6ۉB0܂ݵIשMگQ %d[Pۙ:Y(HiD6E6WRk&@C-g՝?x5;Fc\Z|(Q0$QAEFKz̈Up (־%Py&Y$EæD%LVB0nNヰ>ܭ^D>|bN3x ɲTbFk S s9KB$HOkL0@T6ݦ iզrNzec&&nbeyM@3oA%(qQ\ i"eA5mfd,,/i`"hBKb .e(Qi0ˏYX) 䛨1n Kw;d%bwZ|BvCѦ០cp0CiӃa y iy0Eb~9Jf>%g\J1f9m-D@6;P )SɍX.˸P0a!D"P耕B28ŏrN&g8ckD:]ʏ) t~d6􌦹6ZF* *M<#5چpeV;QvMfyn="mKy2Lh-5$ZU[ϲ<`Tes`-d,_";,}7N" Ϻ@Z>J?õBX9}m} 'K#3s~֫ft\wFTcqhb 61K\h: mF 9R9\BnD: @+Rlhjù: ]O#Otc2+ %(orr67d%…dJXorAt'6/+! :;$QtV :J!_Ob ytL!= h#U{աg>O'UXաSy u0gܩ]zN.giI>T+%?ɃEq,^ًsh5.y7\h.&Ri!+7.~xx|o\j\a߽ad[`\}c.`W2}R+{$>;[t0zl♐(ՏR0q&iyZ_b-0 n( kV08_|2~-ۭ@#1BuM+]U*߇rمWs(3ov=G endstream endobj 43 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 44 0 obj <>stream xXmS8_ͧ28lMaʵ77!ØD %zVve^z"׻ȶv}cnw#3͌ո1I,ýw|̰͠=pm;vD񱲬_AA~Eg2RׂssmN=fq{VDn6]:, o0q*cLWЖ-VoTk)+QjWTږ66OlUMr 𬿫u YjyVӯN\`kr16^r(1Bsz{MLY:HbkMa3uN=YGaKLFQ4kzY߮Xb&sԃ>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 46 0 obj <>stream xYn}h Fvhv6dYU [^(N2 쑸˘]!OMUHŖzPnz(fdֳ͎_ffzQ@nf&Q%_I9 B_ 1򗎋crj~V(Љ);>UfƂ\6i%{ɼK318 Bai(9%YZr6e7Kﱟ_~djbIpFp:L $V:Y2nnx!5yvO1a t-ZѨ LiYHUX2^G}L0T,!0p/ɍo_%,Da i1Q'@s$r,؈ x҂( 1"Z%rB:ӮT>EXЉ `\X (a!86` 9FoOu\"#DTY D%Jm%.b'0q9T3!,f`<0`,EKjiAX0Y0C5q<`3 Pafc3yfc3yfc3xf#3yf<1<1<1Y<<]f7v3`,EKji¢L̔9 86$TFG]6fq5fKQcZ[]Ckyo `3 P1T,_c1u4f%J\JK ¢Lcq5f"cQ  /?"H?@JtBևYgoNGo97dkmۮI1鳼?<"yK-gN^'E6i[VW[ZeRe+NnJ|/-X9h4I;<_Cߏ>lO+qg+ȁ~r'䄐!g@tL9IG yy:>%::%wDiIprUʟ"^!P$?#YW)&rVޒrHZ5sU7E97Vmk`ZYـ#%d6XnQz:_yur戜?;;8$ã7OϟovְM] Fs4 2]MR9tצB;ĺ/QaLKVdIQO:e1 wo@|'mhh7tɢ%\'Ytd%r98uYMxnj_daG!d!ܕe`ک(]]&vK& _R?3pw%xX& dHɉYv:tp%Õ\Խ8.Cꄦ}#]7<ă \I͜ek;I۩8 b?{E9Qd}xB1XXMƾ~?;˄la[畮:\pԁlKKUr1N&iʷ]rQp›nc۫dW~ _td?@́[iƒe`k[}(tp|r j7I_t-DFPjɉʄmaQC.p/EQARΟ[+VXX"Q& aaD[Dq{ʈ>ϋLƽ#6)@P'>Z>y 1Z{bo]}z̘v.uzW24EËK8eA'$ׇB:y٧ L|œڎ6ԫ]꠯D!Yō!( x6+X<5fԥƧ'"Z=2vD_H#CE@-*|$yAR<6 uXGD) e_PO+`NrY00J.%g[`uuvxxZ۷k7EADlvu ސ]?,; 2/6eU endstream endobj 47 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 48 0 obj <>stream xZr6}W`L,p팜;uK2qHVr;-m:~/{ @L{}?qx{?`Ą #"$~cR> '(}LcQqJbB>CIQ(l(erX+FDUP @Qּ8)ȸ~҄ho%Q{MqXrURk CZb!Gm )$$1uTPK m Umi znjm[ykHm5Yb60|Q#U6 㧨c$1uTPE.6Wd:yqSn!MFV:ad@ǑEI'rp 5j#mh@OSGȏ`m*.9%a0 $ 5o7Ns 6䭼5 шJB64,tU #núC6х$r2}P+rP{Ms]ͦYO `]@ɛn;$aWκ6?@ ?muD1ad!B?duЏ1"Sc2 (*g+#l1t4b#zy+Oj!%~is#дi+SRS^vH#i`#Q~x/CM۴K۴MM۴K۴MiC(ő@4 mi*jKFJx( C[=S83 ! Y jqDlʋDȯ gD.A3 4ahqVLKyՆg֜欞(ggq&-V&1gb8Kp%! ǧkY>Q$Cpteu2~C`xÚ~0= =ނǗ. Ef7 -@ 0x/@~4qIL 7J *g,iUz x2ԝr94+G9DI)pN {6+C'kVe,Qi^JB]*[5Gl>MGEue91;c^6HlD J▱I+0Y1.,pNnA:Yo#BCf\f6ξD˶ lzק罧׃Nu0!5u!M.[)_Ίjvt"ݫl/2iE(+F1{8#6H ʞ+YO$<-le(i֗Pg9ۺ!#V~>vSÔYd_fƷMQ4K2n]v- #$n2[{n%#4OE: %/Y'UVe$a*sY:Hh#) 'Qz?qB`'ݏkZI)T]*!@k ,,7YYY t1d W{}[% Ԫ-F8"GWC^R1JYm?Un26'I@mg?)~펶I9,MH칟D5~Aʏ]IM]v'!m|OJɬ?0~s?)$$Џmx&_|$MlN뫣U'Bs˳5Խw9G;\u endstream endobj 49 0 obj <>>>/Parent 30 0 R/MediaBox[0 0 612 792]>> endobj 50 0 obj <>stream xX[o6~ׯ a"$h۸Q7E`6,WRߏ iESP|;/O}`9 ̬~`}~Z`,>?,耡3#DuIvv;\{oKnĸ1īKm*ȝD5g{B>T<;>ϢգmD$"gLZ˨SQ!)SDVQ%Sfrexb W7&DCX}3!R5uQA \y, =k+=Z\A90QIu6WuVi4R:Zz.PbBWФ.4;TR)cy@9ߕ6(ᴸHjV1C2FD:`J[XfS8d$Jr$2x#Y{ 'o vT/;Tf(mPjqE%,G%H_L $eZ#TʰNV,V\L#JQb?х&vju.$C6,6ȝQFWR R ::2*@ȡÔdz&Jj(Zq1+7DX}C} Iq 鵍J@-NQ/AeY̏JQԠ [iB -i 2F[cPB E+.& (B}m`EhqܟDm|x|kp0BbD/0Z1_ƃaw5/4y\f1,O_NNٰ{:< F a{af{2fiq.8`6·/H1m'Ky.[Tt8UTdl l9]y*$ ga7NF d[;Oݠ l痃xUǑEDlq"Yoľzݽ$ޙiH?F|l _,#=o4u8#_).v1#V<;t'a#lgan6/Ebإ6CaҦNGaEypA)/[Mk:mȧ9<* zsl!1DܦfGcO$BcqMbcgAo<(EbxEK "/SgC5/?Ԭ!b>[U! endstream endobj 52 0 obj <>>>/Parent 51 0 R/MediaBox[0 0 612 792]>> endobj 53 0 obj <>stream x͙[oF)&6jEkmHje p [TH:HQ{JRr"6A hgf}yt4 6iatm1/P[GмsuMkYhױV<2T׊~yI;:;t >8>XK<ײua J]]e;&6l0mܚI"6Qx[ꞤIU](FӍXǬ959Ќ} ixl")Ң8LħMlP:nzv|5$ʔuS,ٞ}ISDSBjzԵQe;jrh 噛մ0fK2/#0rim:W,k_ZP6-)s1e?BbWud(^' Ql^LMM2۾-%38TW1۾l>6|Y j6=5fæb\%D̝bӯ!e*ffT̼N1j65pQ̮/o,񘣊VdžShEaZ4nV~EaSrEL-$`3xkb?3Ll0k&u5h5%D̝b,:RdjθE``-;eؓ0D ^\B" N2[M'V3$Ll0{6]g+bbfk̞C'V`RP̢N@0U4 ۗbvm%D̝bv E=٤?f(fQ' `vib9nuPC&%D^4ġ"D~uC1:W]|uz4#7, vloYd¨n.Ё|$Dl-l!ސt2/GA0]^hM>Qq1OQ0~MwX}Ǘ`o$':Cú@8 KRrKPQewk=~1 wtÅ1 7}Am3llgbwFC{I.W}OӴwOaG7O +?~`&>V>cLzR;υ 0vJp|φc}&׶u9`:E1[TY^ z=LIBmmQچ=I :7g)5’NtBU$XH(z IB$~G94D-k*hNsJ5*Nj`xm3ɂJMع1ṅKVj<-g7La͉,S aaghCvYZ0Yj\hK"֞/hj2[n3&.a'I+`JP#DM74sCs(wzk)ܩ1}8ys ,GT~3=c_*>w4p1t~4q$/XvuuvgElwяm=ѱ;|lo/|ޮuxǛrm҂gp&x-N iA+Un:tWm I9Ii-x^}<[vZ]̶#r95Oo~|_;& endstream endobj 54 0 obj <>>>/Parent 51 0 R/MediaBox[0 0 612 792]>> endobj 55 0 obj <>stream xXmSF_qtNPBZ#+,! 'YXg>=#_p͌xo\Ԣ? N~OO(N@|!zN3JXTa7l`D}ʫBlb<7.Aw\F;ණEZ F}aݫmqW-'e,?WJtlvmIw\W`˙jlޣuZn/ڰm179X\+4kӕTUuoס%tB^ ķ\]j!H7(O*c|/V %E}R b|z?#D Jɀ[pJɻ1νoU}~ F wo<ɻbEJ>qҎo45Gqfsp/xZI14Nϓb '2^TjLxꄝVCr6}KZmSjNL`1K 6:ͷrqUχ"@\ALPV6s\iwᐐh.l~['x&ˊĥ$7HiRwe+ ,'lt)}pxߍ˹.T7.-ʟ/nn}`>CI{n3::];%=+ąʎNŠ@YvS@\S{!(KɽkqBVBͫB#xzLVLΒK7G{:CuHQ<,ofINe]Tu)Yh#|Ng' ! )~ar=8wEw_.0]n cYM3ƃjv'E,d'Yqgr~òBn3T5O亅@iP Y\ )9988(E58_|.@4d T6ayjs@e9H$ps"Q)ZAoJ!uz shvQFb/)oA/^#mi(3yh56IR4 kB"TxD[:E"T!;>L| Ճ{SgI{=fk=E %{rF endstream endobj 56 0 obj <>>>/Parent 51 0 R/MediaBox[0 0 612 792]>> endobj 57 0 obj <>stream xX[SF~ׯ8S2E.Q'3!7li i.HS{ֲv`t&cZ\h+L"`i&}$6F7MR Hgp6 %2h921EJ$+lpY+Ec:G5m*ৣ&tdT6;AXᄡXRb cʩkT_0va+ؤQ^edC{ʃǛ:236[Q˧` KwnK^^IZ ai/ǔ.Ѣa+ny}_Fgg"%iFA_ hSC3yhjvsYfOm1VcZXmЄ[n؜ Y$dΡ\PE&2UzA]D+-Bx.j+xE2Ccڋ*`;oO2m9nw^r?8+.qHT8{IQ=\Wܲ>M'0=A-/pA34K>Oo*wpV-! /y5Mp*<@UA,3%;2')xvf1/hyZ+Gqkc0qbn7-o3Zt29e}Ik|498}c<Na:?'__>HM]БyE Kf'O`)D^u]ېS]6dzXeM{B:2~f|C!}J9ǁEU-{.AT6/y@/;x~} w-l>IsW) endstream endobj 58 0 obj <>>>/Parent 51 0 R/MediaBox[0 0 612 792]>> endobj 59 0 obj <>stream x]O0{ +sĎwA'11 lcB!u۰'w6 1&$79~VЇQE!J舧Lޠc;3d' ^%f2$#Ta7KmS cǘG tQ')TE#SC(Dan LL 1ЄS+)#qMSz4MH6Nf h hȜևc6KwޖKYbXV*\5xnkV-ڪij%\\, JWs*%7-UJm7pVc`Oҫ]mfEөYp_Uтk\ \-b|j&8I74x8x|qxt[6^:l]ƯOS| ˽QHHr'ʗd)IEî{ {x4sUM$KELy_ǥYU^Yݽ?\o{(}ʫW)=b0 endstream endobj 60 0 obj <>>>/Parent 51 0 R/MediaBox[0 0 612 792]>> endobj 62 0 obj <> endobj 61 0 obj <> endobj 2 0 obj <> endobj 3 0 obj <> endobj 7 0 obj <> endobj 6 0 obj <> endobj 30 0 obj <> endobj 51 0 obj <> endobj 63 0 obj <> endobj 64 0 obj <> endobj 65 0 obj <<>> endobj xref 0 66 0000000000 65535 f 0000025115 00000 n 0000141855 00000 n 0000141943 00000 n 0000000015 00000 n 0000023519 00000 n 0000142122 00000 n 0000142036 00000 n 0000025304 00000 n 0000027500 00000 n 0000027675 00000 n 0000029208 00000 n 0000029385 00000 n 0000031060 00000 n 0000031237 00000 n 0000032683 00000 n 0000032842 00000 n 0000033685 00000 n 0000033844 00000 n 0000035570 00000 n 0000035747 00000 n 0000037127 00000 n 0000037304 00000 n 0000038807 00000 n 0000038975 00000 n 0000063346 00000 n 0000087642 00000 n 0000088419 00000 n 0000088632 00000 n 0000112966 00000 n 0000142250 00000 n 0000114190 00000 n 0000114383 00000 n 0000116554 00000 n 0000116732 00000 n 0000118695 00000 n 0000118873 00000 n 0000121322 00000 n 0000121500 00000 n 0000123316 00000 n 0000123494 00000 n 0000125332 00000 n 0000125510 00000 n 0000127100 00000 n 0000127260 00000 n 0000128614 00000 n 0000128774 00000 n 0000131018 00000 n 0000131196 00000 n 0000133190 00000 n 0000133368 00000 n 0000142381 00000 n 0000134834 00000 n 0000135012 00000 n 0000137180 00000 n 0000137358 00000 n 0000138743 00000 n 0000138903 00000 n 0000140446 00000 n 0000140624 00000 n 0000141480 00000 n 0000141787 00000 n 0000141640 00000 n 0000142476 00000 n 0000142555 00000 n 0000142639 00000 n trailer <]/Root 64 0 R/Size 66>> startxref 142660 %%EOF sipp-3.7.2/docs/scenarios/0000775000000000000000000000000014525516253012314 5ustar sipp-3.7.2/docs/scenarios/actions.rst0000664000000000000000000004711514525516253014516 0ustar Actions ======= In a `recv` or `recvCmd` command, you have the possibility to execute an action. Several actions are available: + `Regular expressions`_ (ereg) + Log something in aa log file (log) + Execute an external (system), internal (int_cmd) or pcap_play_audio/pcap_play_video command (exec) + Manipulate double precision variables using arithmetic + Assign string values to a variable + Compare double precision variables + Jump to a particular scenario index + Store the current time into variables + Lookup a key in an indexed injection file + Verify Authorization credentials + Change a Call's Network Destination Regular expressions +++++++++++++++++++ Using regular expressions in SIPp allows to + Extract content of a SIP message or a SIP header and store it for future usage (called re-injection) + Check that a part of a SIP message or of an header is matching an expected expression Regular expressions used in SIPp are defined per ` Posix Extended standard (POSIX 1003.2)`_. If you want to learn how to write regular expressions, I will recommend this ` regexp tutorial`_. Here is the syntax of the regexp action: regexp action syntax ```````````````````` ================ ======= =========== Keyword Default Description ================ ======= =========== regexp None Contains the regexp to use for matching the received message or header. MANDATORY. search_in msg can have four values: "msg" (try to match against the entire message), "hdr" (try to match against a specific SIP header), "body" (try to match against the SIP message body), or "var" (try to match against a SIPp string variable). header None Header to try to match against. Only used when the search_in tag is set to hdr. MANDATORY IF search_in is equal to hdr. variable None Variable to try to match against. Only used when the search_in tag is set to var. MANDATORY IF search_in is equal to var. case_indep false To look for a header ignoring case . Only used when the search_in tag is set to hdr. occurrence 1 To find the nth occurrence of a header. Only used when the search_in tag is set to hdr. start_line false To look only at start of line. Only used when the search_in tag is set to hdr. check_it false if set to true, the call is marked as failed if the regexp doesn't match. Can not be combined with check_it_inverse. check_it_inverse false Inverse of check_it. iff set to true, the call is marked as failed if the regexp does match. Can not be combined with check_it. assign_to None contain the variable id (integer) or a list of variable id which will be used to store the result(s) of the matching process between the regexp and the message. Those variables can be re-used at a later time either by using '[$n]' in the scenario to inject the value of the variable in the messages or by using the content of the variables for conditional branching. The first variable in the variable list of assign_to contains the entire regular expression matching. The following variables contain the sub-expressions matching. ================ ======= =========== Example for assign_to --------------------- :: If the SIP message contains the line :: o=user1 53655765 2353687637 IN IP4 127.0.0.1 variable 3 contains "o=user1 53655765 2353687637", variable 4 contains "user1", variable 5 contains "53655765" and variable 8 contains "2353687637". Note that you can have several regular expressions in one action. The following example is used to: + First action: + Extract the first IPv4 address of the received SIP message + Check that we could actually extract this IP address (otherwise call will be marked as failed) + Assign the extracted IP address to call variables 1 and 2. + Second action: + Extract the Contact: header of the received SIP message + Assign the extracted Contract: header to variable 6. :: Log a message +++++++++++++ The "log" action allows you to customize your traces. Messages are printed in the __logs.log file. Any keyword is expanded to reflect the value actually used. .. warning:: Logs are generated only if -trace_logs option is set on the command line. Example:: You can use the alternative "warning" action to log a message to SIPp's error log. For example:: Execute a command +++++++++++++++++ The "exec" action allows you to execute "internal", "external", "play_pcap_audio" or "play_pcap_video" commands. Internal commands +++++++++++++++++ Internal commands (specified using int_cmd attribute) are stop_call, stop_gracefully (similar to pressing 'q'), stop_now (similar to ctrl+C). Example that stops the execution of the script on receiving a 603 response:: External commands +++++++++++++++++ External commands (specified using command attribute) are anything that can be executed on local host with a shell. Example that execute a system echo for every INVITE received:: Media/RTP commands ++++++++++++++++++ RTP streaming allows you to stream audio from a PCMA, PCMU, G722, iLBC or G729-encoded audio file (e.g. a .wav file). The "rtp_stream" action controls this. + will stream the audio contained in file.wav, assuming it is a PCMA-format file. + will stream the audio contained in [filename], repeat the stream [loopcount] times (the default value is 1, and -1 indicates it will repeat forever), treat the audio as being of [payloadtype] (where 8 is the default of PCMA, 0 indicates PCMU, 9 indicates G722, 18 indicates G729), and payload param as [payloadparam] (eg: "PCMU/8000", "PCMA/8000", "G722/8000", "G729/8000", "H264/90000", "iLBC/8000"). + will pause any currently active playback. + will resume any currently paused playback. PCAP play commands (specified using play_pcap_audio / play_pcap_video attributes) allow you to send a pre-recorded RTP stream using the `pcap library`_. Choose play_pcap_audio to send the pre-recorded RTP stream using the "m=audio" SIP/SDP line port as a base for the replay. Choose play_pcap_video to send the pre-recorded RTP stream using the "m=video" SIP/SDP line port as a base. The play_pcap_audio/video command has the following format: play_pcap_audio="[file_to_play]" with: + file_to_play: the pre-recorded pcap file to play The audio file should be the raw samples, example files are included for PCMA, G722 and iLBC (mode=30). ===== ========== =========== =========== ================================ Codec Payload id Packet size Packet time FFMpeg arguments ===== ========== =========== =========== ================================ PCMU 0 160 bytes 20 ms -f ulaw -ar 8k -ac 1 PCMA 8 160 bytes 20 ms -f alaw -ar 8k -ac 1 G722 9 160 bytes 20 ms -f g722 -ar 16k -ac 1 G729 18 20 bytes 20 ms *not supported by ffmpeg* iLBC 98 50 bytes 30 ms -f ilbc -ar 8k -ac 1 -b:a 13.33k ===== ========== =========== =========== ================================ .. note:: FFmpeg adds a header to iLBC files denoting the mode that is used, either 20 or 30 ms per packet. This header needs to be stripped from the file. .. note:: The action is non-blocking. SIPp will start a light-weight thread to play the file and the scenario with continue immediately. If needed, you will need to add a pause to wait for the end of the pcap play. .. warning:: A known bug means that starting a pcap_play_audio command will end any pcap_play_video command, and vice versa; you cannot play both audio and video streams at once. Example that plays a pre-recorded RTP stream:: Variable Manipulation +++++++++++++++++++++ You may also perform simple arithmetic (add, subtract, multiply, divide) on floating point values. The "assign_to" attribute contains the first operand, and is also the destination of the resulting value. The second operand is either an immediate value or stored in a variable, represented by the "value" and "variable" attributes, respectively. SIPp supports call variables that take on double-precision floating values. The actions that modify double variables all write to the variable referenced by the assign_to parameter. These variables can be assigned using one of three actions: assign, sample, or todouble. For assign, the double precision value is stored in the "value" parameter. The sample action assigns values based on statistical distributions, and uses the same parameters as a statistically distributed pauses. Finally, the todouble command converts the variable referenced by the "variable" attribute to a double before assigning it. For example, to assign the value 1.0 to ``$1`` and sample from the normal distribution into ``$2``:: Simple arithmetic is also possible using the , , , and actions, which add, subtract, multiply, and divide the variable referenced by assign_to by the value in value . For example, the following action modifies variable one as follows:: Rather than using fixed values, you may also retrieve the second operand from a variable, using the parameter. For example:: String Variables ++++++++++++++++ You can create string variables by using the command, which accepts two parameters: assign_to and value . The value may contain any of the same substitutions that a message can contain. For example:: A string variable and a value can be compared using the action. The result is a double value, that is less than, equal to, or greater than zero if the variable is lexographically less than, equal to, or greater than the value. The parameters are assign_to, variable, and value. For example:: Variable Testing ++++++++++++++++ Variable testing allows you to construct loops and control structures using call variables. THe test action takes four arguments: variable which is the variable that to compare against value , and assign_to which is a boolean call variable that the result of the test is stored in. Compare may be one of the following tests: equal , not_equal , greater_than , less_than , greater_than_equal , or less_than_equal . Example that sets ``$2`` to true if ``$1`` is less than 10:: lookup ++++++ The lookup action is used for indexed injection files (see indexed injection files). The lookup action takes a file and key as input and produces an integer line number as output. For example the following action extracts the username from an authorization header and uses it to find the corresponding line in users.csv. :: Updating In-Memory Injection files ++++++++++++++++++++++++++++++++++ Injection files, particularly when an index is defined can serve as an in-memory data store for your SIPp scenario. The and actions provide a method of programmatically updating SIPp's in-memory version of an injection file (there is presently no way to update the disk-based version). The insert action takes two parameters: file and value, and the replace action takes an additional line value. For example, to inserting a new line can be accomplished as follows:: Replacing a line is similar, but a line number must be specified. You will probably want to use the lookup action to obtain the line number for use with replace as follows:: Jumping to an Index +++++++++++++++++++ You can jump to an arbitrary scenario index using the action. This can be used to create rudimentary subroutines. The caller can save their index using the [msg_index] substitution, and the callee can jump back to the same place using this action. If there is a special label named "_unexp.main" in the scenario, SIPp will jump to that label whenever an unexpected message is received and store the previous address in the variable named "_unexp.retaddr". Example that jumps to index 5:: Example that jumps to the index contained in the variable named _unexp.retaddr:: gettimeofday ++++++++++++ The gettimeofday action allows you to get the current time in seconds and microseconds since the epoch. For example:: urlencode / urldecode +++++++++++++++++++++ The urlencode and urldecode actions will replace the content of the variable specified in variable with the coded version. For example, if the content of ``variable_to_be_encoded`` is ``this: is a string``, then content of ``variable_to_be_encoded`` will then become ``this%3A%20is%20a%20string``:: setdest +++++++ The setdest action allows you to change the remote end point for a call. The parameters are the transport, host, and port to connect the call to. There are certain limitations baed on SIPp's design: you can not change the transport for a call; and if you are using TCP then multi-socket support must be selected (i.e. -t tn must be specified). Also, be aware that frequently using setdest may reduce SIPp's capacity as name resolution is a blocking operation (thus potentially causing SIPp to stall while looking up host names). This example connects to the value specified in the [next_url] keyword. :: .. warning:: If you are using setdest with IPv6, you must not use square brackets around the address. These have a special meaning to SIPp, and it will try to interpret your IPv6 address as a variable. Since the port is specified separately, square brackets are never necessary. verifyauth ++++++++++ The verifyauth action checks the Authorization header in an incoming message against a provided username and password. The result of the check is stored in a boolean variable. This allows you to simulate a server which requires authorization. Currently only simple MD5 digest authentication is supported. Before using the verifyauth action, you must send a challenge. For example:: WWW-Authenticate: Digest realm="test.example.com", nonce="47ebe028cda119c35d4877b383027d28da013815" Content-Length: [len] ]]> After receiving the second request, you can extract the username provided and compare it against a list of user names and passwords provided as an injection file, and take the appropriate action based on the result:: .. _PCAP library: https://www.tcpdump.org/manpages/pcap.3pcap.html sipp-3.7.2/docs/scenarios/branching_01.gif0000664000000000000000000002506614525516253015247 0ustar GIF89abp,b   $   $$$$$$$( ( $($0( ,,,,,0(0(<0,0,@44440804844880<00Xܴ#SUot7 cSi6PgyUV}$~'^0T@;ȅe~`#B՚i1g֎c9gUsVAH:hPzdqfhs9sop85-iQ>\-r iYZʩMM)".Is)HfY啈v(ba ߘ$$YZj%I%cynzfbJ݉H'Oqb%kE]4[IܲHIvD]*wu,{ܒ$F*;\je@dQ9VM zJ\1eTN].H.Vlg,}v| l(,0,4l8<@-DmH'L7PG-TWmXg ar`|_m6gxDgkҬ܈7t]wa|?.8ሳd7n;.9HG>Un9SWq49RkXZP@ʥ?OH(GtHRk&sԼHkԇ?02&/~ ^H/w> `%:'H Z|*Aoio (o% W0 gHi`2F8@H5 H"Q7T@@YDk8 T_Q1 _xE-#F.W x4m`3@@@@ra)̰@ف+.CETt@xd! (dd$9H҆$#x0@>x|$a҅r F"r(`dRХ4qIKvl4p]s&nB: F1% G1,<I^tҔF09k23^RF6h 41r :I:24CY!@D e2y2:SCXz]44%G;QapZ&m&91P)Bܥ ^rƜ WT AOh*&ТKm*:ڿrEE2n5$C5Mәx1JYkJҐK&uLSC?fn84buDE>PanRh& Vb#fqf&kG{4#q!Z.ShpOʁ8/ ʿo4H'MwW.2o);KA(3=!~*w'La&h@> q/ ;EÐH#F6@tw?' dqB,qҼGQ!?A̠L*[ , i`\hNL:ŐK ]Ȁ4m\_-9#ӈa` 4$`\ZhMW@_peћ^aOϭ՝ܫa͹͚֘ܭq}ד3ܯ- ؎۱8-ى۳Mڄ;mVMn`܇ ik%Kuލ77}t6a35/Xs!^5O⭳%)okKsP3 <V0 @$#we~ip@H|C'zOnP9vzs9;pNxeטMcXMO;񐏼'OybH 'H=Gsy'.`^%H PqqWDBa Ф4i/7!Oep?9³/10@8AވjICƐ'5r)dRhUIջG4!$> ?z[GXeYqFiK8 @}H\ndYԇHE\K?4`|˥Y8u`Cy%wDFV'I<8THF KC ̴~E#PtSĕO%Ub.(Q`&>?YH=7Q$^4MZuS& RPK#E)APNF\ eI-XzC 0w᳅G@L6p}wPdtME40PH8JOMdLOUf?IUXaW3-t5B@0a9IҕE' VZ@F_iELqHմ vuIRB P{# (}28@44 ˅ (Yd4H5_V0RuRv%L'hIҥp( ^ |; }` N bb0Z? ^qIMW_)R`|~-i`ƗK8FWq4}c}(= up  +z# z'1Wi: ` @ڀãcTih9zX3pxb `j舋i$4 ׀ғz?qzEė2 u (d-ɘIC 3PXc°vx Cw9ҡp3ܠЕqH@ct!Ma .șʹٜ9Yـ4 ]H=pBdQDU^gҐ@+  pA)ːi!`? gן ި .op@0'A :Q! % 3y04i7zאo)4?JِP4iG 9ڞFsNzHCUz`J3[` Ma*Os;7,Z(5OjK\ 05]Z5*Ps d5Otu`Tp lp:Z )5 jP2R: Z?p4{:ڬ?z idӀ(NJDJq /A V d:o =h hVO;ɿ(vUX#(|xF!hɣUQ q! sCާM7AH @WeďĆVTK˴PehR1+./oh;l;<W!3y!/Q aZ#  8@V"A # ռ?'_wexdO0uk|@}EZ:4B%X I! kR q}߇‡҄'Տ?@·?dtMpbFkD\K F;^Z١E!!MglrQjBI/BsIȽBqq-Ρf-#b,qQqM=)2)  -a'A1%.ڽyލ˫%" 1L,+ K"#? `"[cCG. 2>4^6nx2 okB)>&?,kGV~XsJ aatPu Oԣ !}[eg< S k`(P[!z >M{FsNO 4{*ź4~qw w lਚ P#pPR⯎p:^SݺNp ^pPh]7 }@5P' 3: J ' u@S q~u5X p@n5P P44Ts?5_Qs4 MC4ϕ4P4ǐ!:s:63"P&(*,./50~s } :#;c;C3"0{B?D_FHJ?7` ϝWs<#3"\^`b?dM7 =l>ch.d?t_v f?9N F>ގ(ruC6u5b,S?9OϕPp`)d%(cUN9 a-t cD#T P)cO 0@ϟ/9jG?~tZ_Kcp `8`$:r>0K?K@,)XBtĂRyØQF=~RH%MLU*-[ք45mzMd`BTЏ"PLx 'uSX>|(pό,YNHTXe͖PhYR:D^FT:pF/x- NZraI YfUp.#s3]k2BY(&5RDzLTٰ0F\H X/Ǯ1%{c2@ō__w*u:j&x/#%(*XhCfx07َ/C 2BꠑY _ ,z;l;A,^Dqk* ;1H!C:Ti,E) riP y̜2L1 s@DK~2,0ń=2ۤs9b"RMNβތ3Я COBP4҄uҳy.Ey?K=JRQlSS͚MˆStTI)}֓y -w5[a UVaWcC <'Xb6bŖ#b(54-,`mI)`]w߅7^y祷 JE7_9 }[F8afaX!b/8c7c?9dG&dOF9eWfe_9fgfo9gwg:h&h=h} jKNѺkF;ȲN fm;Fn[3;\pÅ"')qŁ_?ao/s ,@ 8{{?|'_<}e9 ؤ~?`8@5C}]|MD8A VЂ`5A}5X]0`zɑ`UBЅ/^ #Kr( 6OrD7ш$vrH&0)"SLFUCNt<Pp`Zt!/ 2 l(4 Ҙ hbX 6wD7XA5?~pxN9vK۱l.w P@f /Ϥ=׀O 2y-?ϑ?%pl3M2HYh("=_q(z${{ ˿ӫ7&T&iR#&)%ڄϙ%p:ii(i:?9+ #)S(ƃ HZڝ8'T!؄èι!!`#VtC8$B1Bi?C t8?COZ','-|{C?P)S-;R(9j)!$ DCBD$DjB<")NJD^*N,Gl'h +x츅!x%O#"^('Nʦ@j@k;J,J(nE%ԥjOEXBX^t4QpԠ+d\wEj&՚#6A)tѩkڥYr>"GY( %\&hzG9-YI83dT3^$ DRB>@;JDrII9|&Hœ#+"d@dwD.hx dҋdȾ j<!^, 3Cʼ/*[m0˺%.l Ԑm8L˗! H=y!_sğɔʤLα͔δϼdX؀SP0}ĠFnO؞7MM NNM\N4NLNNĞ,NыIQ:@ڴ +  x M& ςm!ZX @|7mp$5*.$Мˊ ř = P}LPP'j%QmCѠ)QmQQQidF8QbPQP ҜQ "^8P%[0'Қ!,-=7Q."h50ՙ"PR2U3m$U5Ur7=69Ӓ!4S9?mm@6Bemp'7EMFGTT>MgԒTH3803?TOT/@-=Ah1CT ]UO2PϚy+D^ m'pM80*AdUV` 5Q9D9CsrEsUu=v]WxEWye׌0WwtWw}}WWU\`ڔ;Ud/؈mXXXX؇XُYXMY=ٌX'ǗR 8 Q "q<= ٝ5Z ̖].6m! %ZEÕIPZ9 T]m(Rp=[m$& pD s">4l5UZ:1(xODJJJ4&vj?jB~,,kʚ pV-ܾH%= ?%\C]XAQ?2\E!]^r#ڋVOZޯRʘe_]p뉼C]]EkFP,ңţJ"U#գQ')j"\ڳE>b'6_:LZʖApG]]B"\&Q#)? ݟjB'M,HkLG<:&86Nfco} wJCD A#[7 #_" +X2C] '\`Pڤc#^ ޞا^\)9Ъr*%5[(,BEJſBʋTGB%C%0Vda4 ^7%9[]ǾٵF+'$'eQNk'+îdO*a;+sY0]•d]"y$h7>wb-CiNi^iJi04ѓ' ԙW\9:iwjjj.j>uMj^uS {Zk /`6xVxk^%kp+(=2,x L8#/x5mxkޘ}VU֘nɦ!gmԾ^mֶmi؎ll>}m6m߶6ZцhF ;sipp-3.7.2/docs/scenarios/branching_02.gif0000664000000000000000000002125614525516253015245 0ustar GIF89a.p,.   $     $$$$$$$ ( (($($,,0(0(<0,0,,000804844880<0<<$8<<4@4<<<@<4P@<@<@@8@@@rOsuK?π,(2σ:,&9JQphPrQzGG<(MJWҖT)Kgzd@4)Cl VJTuEM*BT:U LmSUNUUsUn]WYV5e5Y 8p@Wu>r3Jub׻&( ,4Ntv!%D T3)e0 "h'K%走lgKϦ 6n'Z*Mr:ЍroPp4j< (8AA!PB+JFZ)gElqŌ Wb_5~!@X,ob I7$4y~(P iWrٓ>P T#{P-\0{puE's`fCl:]5.Ƨ 9Rr%􅺡E-|v02A)ctXjy͛ eouy3,+(K3u|ObJ\Ab\raIw`qK\f1,1UW{'/%m4չ &sDN'.*˿ˌ-!o,7;4%]7LA _ݦ 7p!E{\dY y rƵ{ޝv=zT%04 ^ ~y`iFnR++o^\)QJlUy-Nק:C)պ˸*>z};f O`(ux;P^4 6;%Lx?B1}x[J £]NoGOȮ _ʋJEȭgO^ yTzI|- DO:\AР9:an/oz 8HZ,} ?L"d?er1UNRhXxP~ Oǀ OXOGHONǁNp!hNEG%XN)NƂ-Ms1M5:8c@ nӀk&`5!.S.Ulp5"oW.BP3PӠh PQ Б(A*:FqjE9 l=V2. B/3k0 6)(^&}- 4lKdXg v+8t`dʲb;tH  -]6h18d B )b$5LPSiҐ>ᥗA *敁)oAA 4bIg斐=2bR^"J xp9 @I;Dp6 P<=+'2U e l>p +TDE> `JE(#`)xF>A !%ʏcY*Jz,.jwꈢ7*9;j?77 % LzyNj 2Cz%7q Xfzhjʋy 6&d1O+6 \iYu~|5q {q %pfzQ fp%4Q oPP  3 r  kX3A)ሼ* ZNJٸϊg8- o jɊ-ЭުUZ}Z骮r zQ,QK+ j jF(60k5Ѯ ko@{J{Jؠ"[ _( ڲ10Ҡ3@ 7+p !D@G+[pV{XZ\۵V[G! Р p808p r;t[v{xzJ `@Q `z* ;[{෡%@<'@%Ѹ( X  ` 'a  bۻۼʑ yE +λ 1Pyk'yp˽۽ 0ipAxkgxڛkޫ    3P" 1p <vJ:iѡw[OKq iߐ "0"I2 1*\ic`vnW|ek &;L03 W0y ߠ@@ @ QK |:s ,`\YCBď[e <N 0 0lȽHip3Y,c\iP"!pI  ߰pҡ @@, c}0̅l  }  ̼kBɚ| ˱lAS<p , &,4},# ́ M ȕK˅lL|ol&4nFP*˲Kvp ;>!B.jEѝxѳ?pPp:>f!XNVn^ed gn'] jnD>\vNڊy>iU>{>R vp.AJ {nQv@`r~B vй~ LMӤke: K4 $LtK >^~쐠$ Jt!K^~؞I-$F~pdBsp~ŮpLC-&̀Ԏ N0L ;ō^5ٻo oNdc`x*N!?`AuP')Bp?_ ¾p-N`0d ("n:9(1=op]_~ H \?ѡ `-~|am`T PLP ӠyBm @$ ~_gP _CQէ>.?GRNhPP 𛼑ky" خ.$?/_$>s\MS  Ə" Bt̔ _aPD|ØQF=~RH"ReQFM[^V4N=}b@Q+B Y -CBJ& mZe(P~VGnTEٴ:^ztYu UQH6B\d+Q%Jw׻-[&f)ظzl^ɥMJYj$O{% Dm$Znɪoؽpcn͝W\I,wf1ivϽ]eb ^~OnOjۿp#p'd6`?J2@ 0WsŽYbd+5%ORhE$ƆL1DkȠs dI'2J)S#Sr5@ L3D3M5d1 3N9礳N;3O=O?4PA%PCE4QEeQG4RI'RK/4SM7SO?5TQG%USJEUzHȂUWgp]V]峵\wV^}5Xcocm-Ye2gZZm{6m[o%#q TS,s1! v |^h60Kj ' m@PoŚ"5O%8C#ݦwd=,7̂0-zAWl^X0%k`w #9 aہwEms&G0] hEJh=}k3'0>t\!|XpA?~jsN %k2` 5>.`v / pXҌl(ƅ1 IhrpЎܚ :`E5v@)_ KgYtBGdbX+Je3h5ό$a_0h̸=q8B74*m%`Ђ:PnMX )(&̠2:ҋn4Lh@3 ZQԥ4iAAF"}iNS9i J ̨GEjRT hY霴@4hȔS  LlЀhdf/ " @W*Ĭy]Z 6X*au*vWjcuխJVتeg5jUg[Њ6U*iQիԪTjkIUZmo[-Pַ2/@quH$Wbn5\ZEWԭ "!bU Zw;*w*y+wj{˩w}kSqHʯ~;d5T@0C0ކ(W ws 2 = 28vh@$@S uSuc?u𤋮vn;`OrSfh\ dt88j4  ,nqZBۿ#t ( pA. !$"t/ S؀"^})*+,T 0 By> h t2$3DC8 EE /A; k(@SDEDECMDL<ĨOL)ERD Where n is a number between 1 and 19 (we can easily have more if needed). The label commands go anywhere in the main scenario between other commands. To any action command (send, receive, pause, etc.) you add a next="n" parameter, where n matches the id of a label. When it has done the command it continues the scenario from that label. This part is useful with optional receives like 403 messages, because it allows you to go to a different bit of script to reply to it and then rejoin at the BYE (or wherever or not). Alternatively, if you add a test="m" parameter to the next, it goes to the label only if variable [$m] is set. This allows you to look for some string in a received packet and alter the flow either on that or a later part of the script. The evaluation of a test varies based on the type of call variable. For regular expressions, at least one match must have been found; for boolean variables the value must be true; and for all others a value must have been set (currently this only applies to doubles). For more complicated tests, see the action. .. warning:: If you add special cases at the end, dont forget to put a label at the real end and jump to it at the end of the normal flow. Example: The following example corresponds to the embedded 'branchc' (client side) scenario. It has to run against the embedded 'branchs' (server side) scenario. .. image:: branching_01.gif .. image:: branching_02.gif Randomness in conditional branching +++++++++++++++++++++++++++++++++++ To have SIPp behave somewhat more like a "normal" SIP client being used by a human, it is possible to use "statistical branching". Wherever you can have a conditional branch on a variable being set (test="4"), you can also branch based on a statistical decision using the attribute "chance" (e.g. chance="0.90"). Chance can have a value between 0 (never) and 1 (always). "test" and "chance" can be combined, i.e. only branching when the test succeeds and the chance is good. With this, you can have a variable reaction in a given scenario (e.g.. answer the call or reject with busy), or run around in a loop (e.g. registrations) and break out of it after some random number of iterations. sipp-3.7.2/docs/scenarios/dist_exponential.gif0000664000000000000000000003057714525516253016370 0ustar GIF87a^YYnppbbu]]t۹zz˳sqqqkyyy{ @}}iiiCaaa##++ꡡҲ{{̢kk©::cc\\CC11 rrJJ |RRSSStt44##YYY"""AAA**ll|eMM UUk;;; LLL;;||333[[jjt,,,Ϊqq{H»yysskffRRmm@@**Eggx >4llmm==^馦]]55D``o99Hyyę֐坝꿿EE99SS CCVbbxxhhLLiFF`55[UUgL&&PGGd++TqWWʋccccKKvvooϽwwըТ//NN""}xRRqJJ|{^^CChh%%NN00ss,^H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۠1d@qBo+SD'\pPD(*^X %o˘3O@YGt`C&8֯UvZvmڰoޭw߶N|q‘W~r͓CgN}u؟g;շ>u?PY!V("XG&2X6VHNb` ~(#rX$h(b0Ȣ/H|'Da&PB9L*$IBSFTJ%Vfe`n]bd&pq)gnI|駞x) @R@ /WA  eif)n騝ꧢꩤ*ꥈS\+k&6F+mS"J뫲~ۭކk.{n2`+Nbd,ZSHp+1/lcLQzpzq1&q,2*ek[0<@{/֌l0'}*+4HCW3c=`GbMN-3{S"-tmZ- վ7Մ6څ8/^ڀmg87ycNlາ$Z7K:ެ\mܫCoyzw}6{m<O_=qSy|ok/߯f9/YLS?؃`D'8%xs_0K G>Ac` #HCڋk-l-Bj&=Pt6L &j=`MX:}I.zY{β6M1 w.NtcYFJυ[<%B<"ЏbD(9±"2Kd|~Vd*y}fL`HUXL%"E΄#GQ 4-uDbz*YILBӎa {N̢TjZ_ae"Iі6 KRB<0 C1$8ho/I* L1ʱDc;CnS!$0%z͊rg.9Ox2<"NH;xzT (I2s -2 (u~Ԩ2i4:]^ݲSmr2c*Onu\$P "TfUhR }rj(UTr>(YNAG QjQpl*:Ȝʒu#I,*]c[gzWS q&@UXRtJ}N4$E7z>t=iHKҔ=BG|>5=о5+T@~ hP$,\ w^MbNf;ЎMj;;.Ic 2I2;4%cםn?ݻW8ogrw6x0 x'g7(q~P&vh6C(OsVYY /7gN]wR0>Mt}rƗ-5^q3NZݷf_%.NǤ 8's|[xtsU8Ur[07V="ձ;^tCeO<'7T]};OƘ]_'ovɻ]?a75lcPEg-'p(m j:|I?6;?loT I(>ppP;G'"۳>czGy-!t0L]#Exs+p=;@2&2@I`;N}{H`OG~7x1(Qg1w| zP5X#(%2;`Z0z~7ZWu0D0"H?0xr8-@8n 0`0>`2 ,wyH,WHL8xȅgT6y7p,$0kh;`,7wQ}mM6H1//u(@40 >`3*z،t2 u|KXP2Dʨ}(~(g:guȊ-腯烐_$(D@`(X}YXG ,`hw$-R G@iP(Yّ C,a@ eHgJp(}Vhy8_R&xvY<Ȏ: i䔁̓< hVWiTbX7I@LْŖn5DIVCp-p/p<5Aj])Y֗BU((+(A|w$`7`z瘋iwv{XɎ]8wR$=@Mw0G8pY6ԱQ]wPլc=*@RB]d=fmh]D'"XrM7\m^bs_"i}j]؄}؃Ci%,`|c"Kpz-ٚ:u^M-웊؆=ڦ]ڨlm:69%]-M4*ۺ]>Ȁ+ǘ]ܧmܩ} ImGzIv-{~]x `^Z=UÚ2Jˍ}E$qw$M"Vw]c2]MP)KM, M]KQme)y AdD U~Χ80H">J,9HN,~Q(ȿ5L0\HO/U]"V|Jwv#^@Q`C^R2lNOnFoN:}S pO髙苾;m Q+ L%5ixomqnNιH;Gps_#)Px1*e6E!Ǿpm!n"p(kwǥʵz+~.>~˽/ &ʲENR.bN/#ߦf D@B?D_FHJ_Qykt~&@ ^`b0p` tu?vxwz|?_?/_o {@m.P0_o5p@ yr0Pȟʿ?_؟ڿO} 9A{EH"IbnnM 30FH7+0`Hp`AdB!N|XQE5fƏAY2I(MdҥJHPM9u blaI.eڔ"<8xƒNnWaŎ%[Yiծem[ E]58PX1}.fcȑ%O<9.ͼD!I:*F+C`RpeرeϦ]m WB(*荳']\FIظ"kt͵3瞽v^^yէgϗ_?}_ˊKЦ25HmB +B g@^ 'B0H-t@ [tEcpGDGk?c3#DrI%LI&t2J*RƝ2 * eTsM6t)lLO)3|O@EݶPr ;*t+)RL/RN3J> QRIT1~A[uUX#۰6p ..= l4Vb5EE-5LH p.HC1OOwo\r#PROHarxGEEv_~}3NR_@ ]4Ola#nqeյ6_\C&dS4/(Wbk2Wij&fyhfKe:@%8jZk=]{V{msS6ya_H.gj;pzqWplin|b|s8h!sSW?K]Trs|wy׽i] pTWW~5['Q_taZ8>y>3֪50wWwouGu,~+~D,( O |xʁj+O;O<|_c@ @! da v [=Pb_ IT/* ?A&KF 3XGFRz.(T,gKS$\'Lhq"|`20 l ^$dp$gꄙ4P6TIpIprshqd41&%J|}FmH$6qF@#%iIM*MJWjRP)LmzST;iO}SUC%jQzT&U>J(,"7&Ou1E>8A$U3i%i5^P֛o#A%#!  k_VSjX H(ϔfcXu~b ,\ B~l:xJi8k:{cI{$V4\tbm[[BJ=evY MP+j*> z\Kek'K9mu] ^(,HA;P3o[ EP (0ʻ\KV`ЀέVn ^ gxO,oMBUVWuƑ+Prw,4gY86U.ڲIEAt^X^2KFE W[}2)X=+e4udD|g&\0~He;e3Z -5VʏIz@Z]66%y):g<ԣ|)瀞Ǔۥ3VvG /|">l0v{ajLҥ$8=}GF!FX4@+@ PeUI퇞A4pp]ا؏<ִy2C غM> h6(0p0/?X(T(XA0:S AALAAB B"B#B$,$C?C@ DADB,DC{ +. .J E_@'%(KH˕@ &$4?2K=$5,}I7!ڢ>؜@(kˉ# H|i 専LÅQЄZh% pQ i5':S<2(:hJ_Q{$"2d@q-rs%aV@ b#h -XV Qel%^ :XQWy5ú_!!8؋EUJӱ\Ts 1ӴΓWT1wX}̈́Kp%YX9ה啷s x.=ը5JY4 :$+UBxEY[wڷEZ"0z+8<ܪ]-.{[K *3N 8 S \}3&"` 9+ 5[:KML#EA5%t@j%ݴ\|7^ #8 W s] 2]e 9<9^5y]82`V `zXXdPEiJF=V\ծ3iX]njɫ H=J^ܩ#ڬ\AaX} .@:3$:! :) bd&\HEU=a5+4).$V pVGjգy ЃЀE\yUaK88VE#Qkc S,k!p%d!覦 ,ib\>MRmbV`vcPe՗H @4EaHf<7\L Wc`޽MH)`Ȃ jkad%66M-^bKV|]WNr-ΜN.2ʘ1c)ㆽ<]Le()YcF{Xa3c ݤ l ^hDCֲRs[ɀ82i``f*.i| Xk@j8!pSֲMA: 8MX؀A([] d(Ll' 2Bf "+oͫ)XҀ` ؎Hm>PX+2x>GFmh&n~nnnn4>pbGbƛxȈ,JNonnnofooom HH=Ѓ悄.nЂ;0 k8: h(Dʌp,q/q-Ѓ18nJ0KE998q / r"/r#!?rIf;XSo>@Jש*J $h3H0s1s2/s3?3'4_s6o4Ws7s9?s.s9Q =0sC@3P='N1_RsF_*Kt`6EsPOt>05sNtOuUN50>uQgZuIUu^u_^7t=u_t> Ks7'9šC xvjvlvmvnvm7vklπi7jwrvt^q_mowuOw{Gj'ysvZx?zv18Ȁvw1 x `xsjxxpxk?y߂_@yoy8pyj|wybF0 S2 S0T#8(/ ,8r! p//1{5ڦ2?z15{{{{{{{{||/|?|O|_ Цw p w(h ?y  .f+@{h(x gT~.|/p'ȁ ~/(Hyـ+v&R|p"(BH"C'RhD"l1*#ʔ*!xqE$@ :w4dQJ/fzh9?i"Ҭ7@ S&jX'>spE %bԨvF4! /)ߥ&B$\LC|QE7n0*")坁_}Q&9 ɆepddEExaeneԅ (|!m8(eVڧf馗v~*jjꨧ9h4声*무jk꯾+lֹj?RVkfa46 kn>[rkLZमJ!,pb+W-3y;& q /"|n3[0,$2i̲l<3@(ˤ29L78+i&BgCwjPt"ts]7x߭wbC[Ղ-'6Lp;{mGxEttgO[w*O^钣nt9+NW.|-p 5:3lԫ}HS E(//K/oL% ^PuR a s(0  7A zЃ`GAvЄ(, O.a gCʰ8mp> "" ƾN 0 BCX̢.z` H2hL6p$D` (y'AL"F:+ $* fde(GIʿ%t^(WVeI%3֯Wۻx02 ̲fĥ26yaJCV0}IiZs;fzs)Flf:m_8Iωd+u\@Q%ҲB`z:8@ Z@7 Ўԣ?+(P6(M)$zGUGg*Ӛdr' T@ rREK1H@npOOSyw= XIծ~RRBugMT*%PR yzxeXw)(vEZ+4h_5eQ_rչ*$,[7Ζ.XȚK08ֳ5,lg0.`tTҢ+Jf@ &8fO6npy,1QZ l xK[CbI$5W,`r`Lю(!pl3QP6s1G,&Nq|#;w$5` 8`@Ak}K%>2,"FN2&3N2,*SV2.s^ihXJyŧbG{Y7\jK*K6:ղ<Ƴgymݠ-JShꡝE{^ # KOԖF5Z:m-CGqεujLezϛsf5'b)ufٽjNضQbkRц6ukYv]mUxRqhE|`־LжDUo{7e5[ϸm+ w!Nrl[%OW%]:y@I"sHs.qCyЧ^W$dL4PxK[o!aE  6zNxϻ*^@B"#@$:@ >XC'O[ϼ7{GOқOWc~ |HЂ h01p#PO;ЏO[Ͼ{g>d0h x @* Ͽ8Xx ؀0/v-@c30\aqG(Fvf/SJP$jSU*xBׂ]wYP P88/(s " BHPx/V')T"MXVtIWr\:FsOgI6h8+Mic S^8]X!Vi1ho/`giETa%vW"et7픇t KT("NgnHh}[xq$8T @8C1k8w( ޕh1ŊFR`%#@*؊!  P(?-a>@a82Ѝ(9xȎ⸏$2!o鸎x_':gEg8Hn0Hqh#kXv)h,&!(61 YGHR u+YBn# %qxNYNT3`5&0xeXPsCْA9i-Fi700A@ɵ04ٔOi gyA' ψuy(B])0}iIo l  p_Y9Yyٚ9YyI !cyٌ`aƜٜ9YyOObMniPA@MPTzUIY` pu >v 0ۺ˺{˻ۻ;ƛȻۼ ;ڻ@0Jp'F9`XgI6 Y ?Y ۿ<\| <\|{mٳaE92R2@GpgX6|8:<>W{Dp\ _1%Q5 /PPЄLeD+{Z'=.%7xZHq[첊x ^廰ҺRR% p=pm]&tySf|Q#qO8LUJ[vLqbǓǖPLȜL3opl_T+̄9P)Z@`U2p_ Tx K|R".pQ~UBZձlN7 AZ?P@0V٬+p# !i4 <-ژ 0@U-Мa\ t1>S U @hz<]v* q>Sl7&-#.!xh% L]$ i:-79c>wGS`F=. ڸ.$ 0pT=p# Ld!O=֜m.^T0M(4`؇r* MMU}O@ѝ֛M4@}Pw=U2cځ=Chfܕ0ِt٭]K"/$%tjM)bYB=m-Cq+ڝ%V0mߟn `eUI ;.)Q$ 9@uQ-'S*nD`5 ׏K_gl%4@j 0JE=w^`b>wRw*@C`(BZPB hU; %(ZsAFDDGT~膮舾BT$^j3@E@Tr` ``뺾`TGC|,_FPNT'pcXBQXz@V$c4YWHq;;] %Vz@'Qk~b@+B0/IPM^o~-'>Q_v2>2+y0 '@NF^x\*+7@"pGn2ϻR@$5 eV'`#?:hVR(?22LPGI_/voþ@8XF"OaBf+Bqkmo"JFZm%7')Un?p;s_L鲭@XX. ~Ew81,MaW>U8~fу"4f20'#NuZj -|/;~R,]E5jo $HP$/,@ƀ( ,HpChƏAY2I(MdҥJ-cY͙9mĹgO<ZШQGXSQNZjVl"F G,\p h<[qΥ[]y_$RV/fLVxm{0!# JL A]:iҨMfڵjحci׌L}5֮o.;l/N*Fxc;cCydK6dSVye[vecX `^ ȡ <*l CVz# C1]$R.0iN8H6lV{mvm{nn{Kzq }12"[2H$ (r)zTP F",P&_,wC'<qDT|kvs}w{w߃~x'xG~ygy磇~DhJpf@j@\J  D=S= +!6/԰XfP$`c9\{R1BjA*l@6@qS (FD iA@'Ha ixC;ayCCbxD#&KTbD'FSb $ndpAv=.V$@Oh `XFl=WxGHgZڙF To^:/6-^qk[4A nA_&M6` $ oCK&04g9z N%]Kg0<Gp1`y{u7]c'{~v]kg{v]s{^AT΂h1D&5 @g<Cb C NBX(̗H( yԧ^g}]z^}m{^}Q>|o` X@7|G_ӧ$?$ y8 _g~__O#:(PJ((w-k'1 @ AA,#Зk{ /3!p"+ ) [ON"##LB$\B%lB&|B'B(B)ԡx-U @S *L197|CWɢ,tl 0 HЌ&P-@@sCE\y . <2*RZdbDP EC/d+A )xòDE\ ō-HJ|\B¤ t.\@|Iw6DA%LP M.L1t LˤL4 s2Mˌ'K( ɻJMt%9,q|MNċNp#-_XλЎ8 #t# XHH9B >03ZP1 X!(/8h'x:w )( N xUrX.a><0&:zgUDi)Oہ N(Ђ&W* dRT_JB NO҂`E(ab| (_b;VJrB-Ҹb5a7,(OʧH9X X$/{2XP(.;bcځ 4hD0EN AX2 Ccxd]88E=FG Q䍠18eUVF 1a|J]be(N,/HbxeAL@$S0(X3OՁ.蓀IqƵ0D,_Tgbg:pGGfx+xrW`XfQdnT0'ՌhhMh\g&gxfzkhFN UlP5hQv_Fh DfI6恀bgf.# pOR{.eM]x!QdNͶH gnPiX*`@iG0)2|*DQx"6,H$x>θ^^)0Hʚ/xΞhf4\ #X> H˯KB )jxTQ܁ l.G~:$SJz\ȍ (ʵ<g"= ”*AK4$C$P݄bp2fjLLGQk8k| kh(.<)e i”.S0p5l Qb `VT:qqiG IՌPLNZ>p{=@ lX/«k$1>S8^νot4qGDf$qjЊ&s:~S"@eD1@ 8X SRψ'pqpNx%RWus1ZqpRM)V%G@o==a @z @&@*xN-(T&jM',)dm CTpT;_xqE=,;G$Or[d3`n+}vyEr J+ls ⩁ vGXAF^,(`אD%v2v|bntp$x!L)kz؁.X@<,x!@橃Qt.XtoRU2zW::?BɎ@\H2u0O$v8_s'QpcRuKŗl>0H/hl면 .d{P  2 C C*21A;d2`bÊ(SND'!R\͜5kJBYtќyd!ҨG&ZS-+ذbǒ-[Z^mV+R,FA5D'`G;N]BRn V ix ɂ" F 00@ 52P$7f 1,\d`nj.B,P #D4 8|P@BֵU#J uk}Poӯ?&ND7D E t4HA#,BfDWKT(̵ DQ(4%PDs;ܰtRDb\@C#7P 1ĥ :XQF @V 87na'()K'hu!PZ+r1%WkYT}&OnyD21֟fzݸEf)u%E B 88dyI% r97B Fm1Q@+O "6IG FVD|) P}|``)@^2DwPLDwM;} ;JP쭿g+[@DHI` !Et%ȘQ^\AQ;=*l $ *Ps |Scc-/s Dm? ĢO\n.y:&CLPc:'B9`HK0j+8 @mH;TV`pgJ5` -V=6' 7&ECnw.6Iiq ^\ЅA4=3@L1Ic$m# )0P9ZI>A/=@!  D0YJg!D,W^pXz&@UAb5ZQmd&) _؀V(: عF66~W>#!2Lr %3iIrrd%;)JOv H#.`)1 @BHX@ 00D@ ĀKsdB++ K<8ASa l95@a_yB2pjCl, BAʌ`tI Q\!F@Ev U%&r2j~*B8m)$ UpA8:!N'8BJ9@$` )NsS`z 0!:B a.XbSi*VUr5Y HF@a%8?Ln}+\*׹ҵsE\@-"{=:;a [p$p *pvpD 72%3NVr&\"[YV62 f1k&+0f'N ļe9Ѓ.F?:ғ 9"0!Mx̱~\8s oYu+Rw;]C_y].'B^#4WB `x@ o%TFTN%UVUVe8# p܃E^X%YzE >H؀ BWxAʕ]eU B՝A} @< pA+Xyc%ffSp4 @@ WApD YWjf'q!YQb@A fs&W݁؀ eqz*]mg !@jvf@wf  Al€ (z6&ف1 @E `pA۽5c#⃮(ReWdA(֨(樎(()")&.)6>)FN)V^)fn;sipp-3.7.2/docs/scenarios/dist_normal.gif0000664000000000000000000006025614525516253015327 0ustar GIF87a^aayᣣmmslpp{jjjjr㪪{{۫ꃃSS;;]]xkk ++""̵űCC[[bbJJ55eeffnqqquu1166 yyy驩቉MMqqttzkkkkk{YY&& |eeΥ==))YYYaaaBBQQQ)))}}###CCD999eqqll88 44III~~sszz334]]ubb s<<RRY,,[BBZZbeeZZWWMM;;zz[[~SS|SSRR66:‹kkɬؔkk33%%--===MMccȳjj[[11|^}bbDD\\~~KKV ^22dGGttSSbbↆttGGy;;z==s..fLLo//|""__##},^H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիXjʵׯ`ÊKٳhӪ]˶۠N<ݻxo䁏 M Lؤ/= pGǐ#[D8Ǝ>rC >-4ӣSVzְY~=;6۶sލ M~& GC0~T sBn:V L"(A&qH\"8'*1X(E2U%YX9 {IǾlyqq~qc؈:Ngģ(?2sBX9򑐌$%1|%cœGα_'}q0ftԗb+iOLL.P(4gRG(c*5߇M SV՚`WViԤ+dJkzT ^Weկ{l_:X֮}h^ єZYNJ┪mke*ej WYzVlEm᪒rƬeO/[KԴ,Lq[ۛvnoK[S q{TleM%'&V1%`.b;^Zw(X/Z]'*_Qҗ/Kⷈ);_h׾8Iz(:w75MqG`Ĕ=hI*f=gNi219A6r\dLB& c7G{^r[βX-g7Ek5_ Jopx~a gҔ4ܳyЈN*=$a$.rAv`%+_tz7Mf.M]RԡZl& 2C dbVt]&^{LS40Lsl3%KRqfڿ6Г4 r @Սv~Myv-}淾p# @/\'5qw8EoFy9rI&A@(Cٌ%P@ЇN`F':ґ/N?:ԥ?WԱ)@ x>'FG{ծiqܧ>l%H;b#b 0@o'<'/3>_<7_y{COћ@08i8>5eʴ/s4?z\/{臂hಆ /0򇐈/Hh(x{5?C5K 5|5Z5Z@ ?c5苯T"'!Wԋe،6(64HT8иȍ34ó^8ap(|FT*ȎX]D8ZJ3j8|4G7ɏ8x#94{SDC hQ4e[dM&fץl' $)jXfji(9TWm^51IcNhGB@ՓBړJ" JlUCivXYYyDهYU-.XN/^e6f5iÈ #B 斣foɗ1IjhWahJo$P\_Ov2RyfPp֘)KIj*eIRkio} {@39vPr]ٖ'yUSRbOyh_lVIgEdy:v6pɒ#Oۉ`ɞɕFfuT9gIg9s@  )@"Y0$(rfyW)caWyh~Vgɡzh%B #%'<9Cih Szg}:<*U;pSD4bbNڤ .0X$273M6dzYf[E:Pnd_:c6S*C ʬDiIi99W@p~XdI0a<+jiʜ0)d [Xb:Jnz`H%@! 2'g dkeXh_+` );VgʼhXz@`%n+Z3 xn XP۾{]B%&4YGMVd"V;҈ 08`dLd ez F7q+e Hr% I@r22L`"0_p!~"o!W.g+rTpVroX p`q7%gl#\rrnp'9 fVI`e|p%n,mƍ|rh jrLɓŗ]|rkll 0g61>0ʫʪˮ<˰L˲ ˸ʶʸl˺|˹˿˼,̬,̷< ;̪)P̿\˕œ͉ ϼԬ̳ LL,,<\l ] C"=$]&}(*,.02-ʠ0 .p<~ #]t~LC=#T\^`p$a`0;&Pr r=t]v}xz|~-mp ~&n0 @`x &mٜٞ٠z-J2wD =pQ6pkG P.Pk%$D̶@~ `n1û&"g0Hܫ #X)0irWH0q3~  !IV5 !P@](PJ`n1-X(6Ύ2e`_Ҷkr(+`nx {2}N.7`؅2468|:V-"Wp Xp5>NP8 CRiE GH~, pn" >/۲{甘窻盨-:.E04mYg%F^"ƒoc}x|S(nh%`]EÊf'k( (ࠩލ^Ů>E/OǮɾK:P@΂EW%jJ-K5X."^r΂+Q;q,ڭkC^4aG~']eɑXKyiG:'ى0/pU0QW<>/O:,'v>kٶ4p $Boh{?.kI'vRNd[XI@A=Z=>^Zҝ%n݊f68zߕzk?qrAc4ly * 9pVSҶ)$; 9ΔkjߩiPYoίB *l"px܍%zH͞(+h/T#N\P`A X\Y #! `@$0@ ^qAKydD/a|˛my'͟2c]CI.eӦXեՙȫ[n3,ױ^Ś%'֡ouؐp = ȠԃH Y], $I˕)y3[hAwrc@e5SsΦ]mܹu&ďKaD2Dxtby-Cw{y߶I]tn^6}e޿ݾb ;, PH.jKȦtL; --MDmILPo?TK,t4Ԁ,z4R!H`+ !ZBhX# Y pd. $[\'93H;}3#s(uMGJmRF Q[C" {DT:21(S<.4)qM~tT> ѴD]Zy=_QPb coY$sf3XU ČfT \2R dX +7s[5Vh w)Bgȁw*d`>QI =R)(b #!V:JB wB 3IPAwp ^ DyyEF߀U1&O&gzy\54ӅU@:/yXZ.vI Ǫ"!xr1_h4E*F`sN]89zfKP.B |& Ď@L=B2uY|m{ F"/hBpGh+`HU-?*.# !t9,@.vp c`PW:haĄ%3Y5j,\ KdgN$6ӛ09b5VR]0fl(Ёٞ\@ 5HEw SaeAD`AF$fHK2T>6]8Lӎl#"]h*O4(@CH-daA$0@25 @RIDlZb2egH\ !Bh,`Zә7miPZԣ&uM}jTZիfu]=J#@#kP?a X:ӣ6`Pf@ {ڡ~vPX30F`եnx[ޯ5h `H@y Np'?xypG\x-~qg\%j n$ZX Lr \& 5 ?&38-"似&򓯭-3Szխ~KXp&bg{v}\P@\LoJ&@*H(SG>@覲u8H-yG 7T(`̇^hOLATr![$2@4A]?))lA |AAA𐲨 .h93 0! /YY۾l-.BC/ C2C3BSlôǰ D,&q@YIPh)( )Q%@?7D嫑RCاuB^IMDEDPDP*‹RZ" .l 1C,`&F r)QSb$ETcLf4RdFHCKD}jql=GmxjEC$#|.;&0 (D8DQDx& BLƻ!HqFrGoFLH *ptHȅHlȍDxB ̖;Ŗ){ED,~)FsH|HIʎ J+D)I\J H?*B@'*pX*CM lE|&dJɤ+hy++2u)"2/D, :Kʓ"͋MMNH$Q%D̟,|ˑ@" 2*ELm#D I,9]ЭLP4JawN PP6x,,~\HPMЩJH⑚Ԭv*$%mPl!! ˃ * аj .EL(2- DR&MSӄP\XMKDވ BP?SĖHs!D84G lL}QTC'!ETZi F"p|$IR\LlTq (%ȂkSBAsM垙#AP-Fc"MZYV]V n5(z'Zq 'H"T"::տ=T-YA*x(aQU,U`F XyQ U6BU]c\^!½SBOx!uuT ޅ@> _(F-Q % Ya<5 0SI_S@Y5ۘ5F$]ݏa**'pX{Q'a)V_[J*VE'2} M cAm_8 €׏mb`%юX]5 Cd]eu]3 =;()b^&\ԈQQ ]A* &QGK TeybW'0-\#8H&h\XXf. Qc䋎+J㑠 +Qb!)NbN_b&䥭pDnmM1%;]3vc u`Xх[jvP O)E8 3Օ_N6W*&ZbBgl5& f8T6T^[&mPD9Eo$i.*}8bEK.<ۿ}FFK2f**iػT.cioVb*Q⣑#`geu٥).^ ЪXSMԩc9N*UdU*lg8聺kd~熖2%f۬nN%VV]ΆVm&ʏF&p&pX!#h.n?qš-M~&bin$$n03 K !!'7H.*0Bpopg/_pWpp p '. pq7.pZ!,0.鐀))?qq X& XJrX9`Zqq p%&' p'p+q&!('8{;&89s:ss?9sA?Bs>CAgtBsEtGo@tFGt?o1;YJoP h7O;1TOFu[;t.s1(1ppH0K3Osev ^vnjmwoiv q7eGs7ponwpWwwy'zjw|v~w~xjwGH\&^^16O#xK(xxxyx7yxx_wyW/yy6 (\9Ex6htxς*xW 76hyz?ykx6 pAhzh7yGoyzx{?{{/8xAn!ط7M (0.|ʯ|˯J28؅ރ2kY(ν|Mj 0Ј6|؄8H?/~?~'#3&;8&p<l>gx+0hOa@c%,g "Q„ 2l!Ĉ'RhqfH#Ȑ"G|"$Jp%̘2gҔ9 %D( )@A= HKkZpA 0:Ă|@ .xN$ (Rk.^.~zk$ -Bt1Lü4MGUSR  a2b[f'=5gR[hB ⶌd8ఙmmI(} A?Mu XQthV蠧}֭ۨĦh~u^;@Q-Ű@a&/4$xwqo-8Η@+LU$GfѶ@~~ʏq^ ߁ S,I!TB &l$SB #ԘJ2 d8qLIZ6!i>)ԔWOUK;AP3!L֯}8/e6Jؿ0=m'oWAV8[lE-P> ")͈\T';,V䊹c#EܘFBhp#xMVMw2#ٟu  `X@, wS F1BddNЛ L{($]eevYƋ`,A@0`_;Ma覉Mi"A (f$~.*48QRH!SQB6PDّ4y q$$B6.in, Cr!!}d 9L*AC. D,` bv(#]})D4\hOpot(tP.&(ӝ^ͩ<cFu4&*Bk",cXQ(uVP8p%>-M7aR6p,PҕD4p YFI<3Dz4Wں3kgSYz.$ "y>XA*֒6\k[6@%Xlc'S|jސ".u"дs D5kmV j w M\r}…,S;QT6F>HL8S)W_P̨cZ}5z0)OU^3ҟ(Xb%@  pK܈2aݵ1L6By,øgbX=oN<ªa_.si0_`E''bw֒+D6`vNif\W0k<[튌&3mG"r.IeuZ Iiy%}dt$%)t fmIϻܯN4P"Ko7J'[QT.H˘F|1S\vT[jSD[=!A~yV= b͝ekYH]%)N2Zxnߝ]`=;ӣfo.6֩wUal:CtPp;y7ԱDӻbPnav[Ua>F-u^&'B82\m_+Za9.[j])jzi:ϣvHwU*̞0<6$ޒ7"Mc%F /SQ? O-v*%mHYˋ}Շai+?ۅ}uuW_irٯ{ƾ3xYLzmn e[Ɏe]ؽbM!-^ S\H l`*,)4[h@\_Q=Z\!= Ο\S9])Y .Tt]C-)FOqQ5\͡iͩ5vLN wq[]FTQ[!kD}!R׾ܳ5I$@3Y^ҟyAuΝMB)Y!gdd^ :FcN b8 dFA#::#;#A9:A#<=ң>#?nlB8cC8F(D?.>.b8#B"$l,mƤOTV@,@nCHCECIʄJJdBApqX@8 PO%P#ePP&%R.eR SeQ"SPJTeT^P>RVVzeSZVz%@I%ZbeSSrRT2eU%]eX%P d AWJ @[B%[[[Rc:bNd~e6&cfffJf~f2ed"%$Y<@Al&m֦m&nl@ofooogngpr.gmfrΦ @fl &vfvFgvrgswjgwҦs&y*gzfvn'z~'w{&$0HO 1\2sgw'xg~vng>(ygR(xJ2(x~Bv(vnVl.h~g<>@7~DG@)(i "2)iJiN&*n:)!i))ivi")6鎲ih)V@'@ \܂)($)(\!T)&餢bNbjiz*)z*F*fiE^0<**++&+>+F*&Su SL+ji@Dd+^Hqf@ \O,"ö[@C _(: t5 ( ,ŶQ6., Ƣ*bT.`V-e@ VRUaN- (@MQ 8Y Yj"ZYwQ-4zWQ.+biʊm\Q% @َ0-_9PO @p! ʠ)bÙΊEL,^#֘K@󩆵(љ0l{aEP`P1mK{Dŀ)m䚭4 Nº+̒bf &nxwp .- n.BpOEOԁ l(]H! ٦^lU!.YbGݞyjȀp8jl .1!O Q0mKl @nrqjGlM0 N/-% Ao̭tZymK13PFҀ;'40zN!00xnT 2pVLq]EIwFAP U'&E4ug$IAk3 b:6|-*6msbf1ސL5MX/4"<ΐ$XD}wc3!Xn] *s>,4wn0KTRg[vi 7|% 3`WS\B?Y$n7usxl/A;fy]yʼnTkRpAK1/@WcFCqq7vcA71( {hr{ve758vnRg6RLJ6`D5KCBSW3{WGl]+4_7s7sȲGG+7kF}X|K o@Ѕ_JڴqF˷9r?'1|xo`\&` 9ې7/y7tjo@c4s^H?9xϝ.=ui8D@i.ygp,״Z7 s/}u&n?jP8ܺc^{:bqg6 L:8:nzNg0Hi:|ԶJsTo֖;brJl HݶpP_*Kc{r@J;߱;O2I<[:f[<ޥ ˢ5§T3Jzob;(6~m&+ϧ:ssvEwxJbբ_b!yHy(Ķ3^нCHvA] 1,[q`HN&ùܾ_'yC #<r337׳O:xO+jE]!R~A"(7XLTa.6O2y#IL.04bЧ<+RM`F~t(j5B [މd 'k|yه:5K0? @A &T(СC)hqÊ pEppA/B̨1KcδqcK;'J;,aȂ+BeBC5i&LYZ!Acɖ5{-&;$ ,PjxbKz/ުcH p"ӆwuԦV-peJD<(肖d[ںpǰiyF'KB(2g'ҧ?]zp:sK0@Âؼ xҽcL{>54@xO@&o ,D xb, 4emuܑu4@L&|JA<$L0a ^2@B =P6@)d#* GL5Ppg B ZBH.XeP)FH\zE &YZ(Xc-d]VYfuZijhlWf[ R>9t qzWpɽV_lɵ7m<@nQ`O>5*}-Wv\E䔳c-Mט @ }蠅袍>餕6ڣyJv^묵ޚkQA \a ^6elpþ +nЁ _C@C=t=-Z# h"NWܣ`s"Asҝoq?vbP# WQp@v{ՇO^\_3~ϟ_2Ȃ& Þ\!XA Z`ٽ@4p  )4 Q?~[  |@T YD iXD `шQ n`<ӽ.\bx-z e7Qd4(~ 0F;jcgfC?I" iu iFպ.tes["=~A4TVTh4  P~"aFV$I-V$ݥe=κ)A"k$]7Xi,jئ<О8  HTCRDS? !P>7^2E=yOw> @vğmf {UwvQK#%u@˜ $@H<={F 3<BS=CCASXB^4Bs?5C'4D#GBEE!T(d n Ԣ  IIJTJTJTJJIKtKUTaKJ1A!TFATMOKIKOOtP5PN5OP`Qt . ,NUO+5OLQ?S' |@;e fVkVoWsUWwW{Wm@.~V.uYz`@VU[[[\u[! ؠWuQ~ANuU\U^RuEN~E,`LFdGGVG$\`yn<_FL0G4_}cac $dedd  G@,N ba9dIc-6GKH6p|$@ 梧Z#/ig.Tv4D$R\i i'vvmkk F~,$lvm$='Ho\)vC2RXoop單o  `xB2`A 7kRrHJpz*1mrGBboGV܏t"0VHv[v1"4 L@5:!" `0 wvYyWykwyy* m?M.yW{a"auGt  V`zb-|:zB7|WuWmɕ{ Xp )v0AJ`5VxsI 8xw%ix81o5@$O4fzu=8]x;)k3vm JEk'{dkgb$xɉDssFu8H+{x0)Vʌǘz*` `pJ霖8)X﷍؍Y @2J4K*'y#rW~z4 !riM"]DE[ d(s•T(b ]9W8b z/.6@ļBR)x D~L.Ϛ9zYԴ¸?x0h]㶙R0K,ɑٽ~ '/8J j3WIҞDZ#Z_>=- $H"1Q0uy wr3JwlmB$xl8I:L:Ѻtzz}wl:q "ͨZZ`Cw9#:ܘ6<JC\b!$ȮwB5mU3ߚ5z!'zG2%2&:x(wލi$¬@IF/ @î6ΰڳ;I7lک25"Mݢ1:e(4π1L%Gs H4F 8皷Z=%BE//N(ٲ2BFєH($ q""T:$ .[ۗy9qngpm-Y_3#*g:|S %1~hf*qytg)3Sns*f<,Ec߮;J^rgFP#t !0Йږ%h<^lɩ|K|tɷN O n ͭ"\30> 4$Ho\N#X'<:s3e(g &=]2S^ ` t#q'V ]3;:AQ`@ج@-=R`(-wˌ%өŮoĭ],y}Wa6 I ? =bMKJ@D96`%ߪ=iPÐz#>Ot#"2 `! 12-:{3m*ݿq5$J@JyH`T\@%>9'|#}K+N t Z"!@cP q`>Orci-Ra N" 0A0q Վ$H *xh~L!Z/||,%1!@p$`1by"* B振p ᰨn+/!_wp nÌ(^! 2CK @s[>%:psb]օ/޺}qB )oOW :%a'MLz- u1PM3Av,  Ü K#$@,8@&40B%xѢB b2% 4 p4(Q  1x0 + 虐Rx<1fH3:-CTZ5V(],Ę.DAV8I@|A JpE7HiEHNⰋ)CLbOD`'~xBD*XQh/H*@-6P|X"g͗ `̯v-9蝃;NxB$YJY4h ¼[|x::pꇿGb4t g]Z*J\@)xA ?\H!,Dl9- (`Rwi A d~)( E\s6W0X} 4@Z% ,,vx dB".@bB݋y6=y'VfB"pd'P Kr\88P!d)@6IIƉ'dZ&uoY[:&y$H8iE|+,$8^*_cLأj$z*<ׁ* HNr2td0jŹPTs˦"Vz Ob4&4/~&a:,tJû;p&/ˆ;O$>+ڎQΜݼ1ƠSMh> @ x3qܤ.ܦMK SR")Kl*ei84_j7U6D SA y]}7M8>II01TONy_@JZ %$gk8C~Ob (,Ѐ a A,$Z j(0(JqQ4,jqCܢ#Z1d,#-U?,!1p07|! OьZt4Ay*Gx)@P@# Hᑑ$, IFV$'3Hf BD&r P+_ X-oKW e. ` 0dl3o)/ᔾLpM ܀d7=Mf>3P m4"  OK! 'sv{s+mۧ? 4C@ReG:bN hevWHzH zX` ^X v (dPXȆtA*P >O'Dt @'dT]g@S&< ;xg't;B>WDH LS!Oxky6ͩ <ip!̵=Mַ Ci@ڃ쀾=={Ss"!9)@=5v:6_>aAa h%X'܀E +c4C8zeb !N# 8XqtENac@\!F9ЃԫL0O_>}lz0ಶ n3un`Fr[z,3!poנۘ#{4t˙ _hBv#!fp½r BH^ HUp9D ۍ3AhBP10a!9! L=ʙj" MC5u=C#n'lcl; ftT'ZձR'<yhgw: jF8w;0/@(oK~򔯼/k~?Ћ~/Oԫ~o_~V ;sipp-3.7.2/docs/scenarios/index.rst0000664000000000000000000000067314525516253014163 0ustar .. SIPp documentation master file, created by sphinx-quickstart on Tue Dec 27 16:20:26 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Create your own XML scenarios ============================= .. toctree:: :maxdepth: 5 :caption: Contents: ownscenarios keywords actions variables inject_from_csv cond_branching sipauth init_stanzasipp-3.7.2/docs/scenarios/init_stanza.rst0000664000000000000000000000171014525516253015370 0ustar Initialization Stanza ===================== Some complex scenarios require setting appropriate global variables at SIPp startup. The initialization stanza allows you do do just that. To create an initialization stanza, simply surround a series of and rND7"1g|~ FUALdkLwŮ>o,! ﭻV~"nAIh̝髕ǚ2d__^ ߌ݉ŧ]u5XnlaS<]<yC%}읂W|LZ[i=')"I~UW_[ 6^g];'^SMO>\/_Xbߡd?*Q_ShI/u/M:V;~p/0i"(Xܲk̸_w_ <d(.Ï# ^u^nA)dw&Ԫ2N^[f\hylm~O)   A&qB"Ĉ +ر#FZI$C'ZbA5męSN-yTPEET!M>UT7\ŚU֭J~ Ӫa˺PH&fQn[oڤ9s۷s^{jކ5Y7b>?Ydc^Ɯ]\=JY蛖ȓ(9%;a{H=پݳb;6\r5?~ZU@_@Mݽx G52嶶}up7 /Ps0B '/: 10!8N=oD[/ !O"R0Ƽt16k1>KoQ8#zBs2J)) tq9κ6L/(3Ͻ4<5c̓3M5WզP?)kPEJ,|QLˊ:Ls5toP$uTOKMRkQ:F MɨnvrmnDkD;&ʐR/sTnHr2?i7`{'t zP j;0bҍ8ۄKt|A=MG>y>+.>*V>R>Q8uK`8#f4݆ 2Jx$GER ps`2d&| SB !ֶD唰!rH9[/ aЇGAA$!&VQuV*yAEE-qf$x4 JScQb숴\̈w,# e$qQx{l Qy8t$5RQk[$8&f$+iZar =Ur{49?$!(m .XBP;/]P[1KJIǤ1yC]0d$3&!PB3fq:Ґ;.ii5 +m8(\'=9w*S f=xZΏ5YȂx?87&TؔE#"p+X-5Å沤(3UPNJΙ/(OD-qsJ-PPKbA輪'zh=M*Oaӷ=JS*X?FTaxHaepիMTJV%Z:?O+Fo꿜N4JmL W7z5YNUe\ϨWE\,K-XƓc}, = "miM+ rr:*7[^Lm5ȸEnr\6׹υEx[vn' Qtr׻ãnCJ<ӻX2\Im;,i`|9 D$􅠏.HZSeow#<~PO*86 1#^<ՠn%] w"˛8 :[p=rv'HYcsEӌ^XQ]DX(#wH\4\>Qfr`qm&nܣN\[kAJc=r{nKjk\x hB?4¹Kp Ÿo?w1Ɗ8F5~\&gIוE}[.rϼ\1?Snpޛy˚|B>70/ \\ү[7Vwz38nKT.Y-kiGwvڒ);,UH/.w{N:Hݎo>Y½Ǽ#v/D}5ٸgnS)\l?y=s˜jr}{U˞ǹ狿X~>+W_Ơ}c#?[٧e=[8[9n<Ò$@?8@k;< \@ # 9  @3@t5,;AA_l%`Cu퉝<,0C|y;?!|, ;B$4B[]3P "SȠ+B-L+BCca 0444KC5$Bz5| <, ZX5HCjDZ8B,$E %3274/DRDBI577īvCرuc֢/A4T̼UCsW8 dFKb4F?L*+Ɨ! ekm>pDGU\E!2GKx| #zFyBz\hM*RK25-Ty;+Y ^ ?}H\1*Ed .,HtFB!ZTĐױ|Ƞá37SHɐ4:\Ȯ4E4E4۴Ma6 Il42c$|C{D S<#"CJD4[JJ$J*J`I$^`: 7[>\jK24cC4@:C 7B8* LZL;*lǁM LKT߫ADشMj |=N+(AHΉ3*HD(Ś5esζN:D1T[H1NI_Θݛ,Qd[dD+E3.^c>tįtM=:8pqr5WCW9+>ZWlW6U4#uשoUtcp׵3O)[PA\ؙ+X1U_؆ȇz}1"X|XA͏MG[XY]UҖDٛUYTeҝڄZ"%ڢ}NCԣѤu`eZNZDګ}٪5DZZӬЭ%[W2[E۴DBӵ϶[s[+ǽ۾ۿ%5EU ;sipp-3.7.2/docs/scenarios/variables.rst0000664000000000000000000000607714525516253015030 0ustar Variables ````````` For complex scenarios, you will need to store bits of information that can be used across messages or even calls. Like other programming languages, SIPp's XML scenario definition allows you to use variables for this purpose. A variable in SIPp is referenced by an alphanumeric name. In past versions of SIPp, variables names were numeric only; thus in this document and the embedded scenarios, you are likely to see lots of variables of the form "1", "2", etc.; although when creating new scenarios you are encouraged to assign meaningful names to your variables. Aside from a name, SIPp's variables are also loosely typed. The type of a variable is not explicitly declared, but is instead inferred from the action that set it. There are four types of variables: string, regular expression matches, doubles, and booleans. All mathematical operations take place on doubles. The **** and **** actions create boolean values. String variables and regular expression matches are similar. When a string's value is called for, a regular expression match can be substituted. The primary difference is related to the test attribute (see :ref:`cond-branching`). If a string has been defined, a test is evaluated to true. However, for a regular expression variable, the regular expression that set it must match for the test to evaluated to true. Values can be converted to strings using the **** action. Values can be converted to doubles using the **** action. Variables also have a scope, which is one of global to all calls, per- user, or the default per-call. A global variable can be used, for example to store scenario configuration parameters or to keep a global counter. A user-variable when combined with the -users option allows you to keep per-user state across calls (e.g., if this user has already registered). Finally, the default per-call variables are useful for copying values from one SIP message to the next or controlling branching. Variables can be declared globally or per-user using the following syntax: :: Local variables need not be declared. To prevent programming errors, SIPp performs very rudimentary checks to ensure that each variable is used more than once in the scenario (this helps prevent some typos from turning into hard to debug errors). Unfortunately, this can cause some complication with regular expression matching. The regular expression action must assign the entire matched expression to a variable. If you are only interested in checking the validity of the expression (i.e. the check_it attribute is set) or in capturing a sub- expression, you must still assign the entire expression to a variable. As this variable is likely only referenced once, you must inform SIPp that you are knowingly using this variable once with a Reference clause. For example: :: sipp-3.7.2/docs/sipp-01.jpg0000664000000000000000000012176514525516253012235 0ustar JFIFHHCreated with The GIMPC  !"$"$CjH" P!"1R#2AQ678BSTstu$3aq4U%brCDc@!14AQRacdq35rb"B#2 ?n://n(#j d6:c? z3"VΘ I-iR 'z1ӡ'=0i0۩ZSF3q`d^l;w [pp𥵇ЎpZz(@cfTEm1>4UWiu4|_13|Dl7]3u{b~uS3m@^#Rջ{Ѕ$I9_oԬ#w6CUkړJP VH%#J;?zf}K>Ay[x84Cᒽ~Lc`a~b/orc~gb6 a_ǁխtNWl̘% KST*QIIS9mI{Fź$]Tir}e92-% VĔy:ׂSMZf&5ZooѮ،MZv_Oퟮzi=.S2bƨ3$;oN+斌ΑԒ\j:-Y(1lnAe礄IW{@KM| FP`M;߯V&n흝 ݆msS{gޭˤtN4v ֢d.O4m6oB̗NJv<݌4ՆWeByel=⩒d+.0Kw n+_Z)":n#Z5孳7]ֈJqw~o׽luHBmKSkn+2SxyA$P 8W jM3keVOq(ֈNN'95Ҍ:Uҕ#n)-sD_|Mӭ~_3uζRYLL_uc{gާ_ZΊX=Kunw‚~j;J@XtӲ'EӷĹDFɳ-;OBНĩJJ#8 |**f#bw6vn׉.׈ڲĖMkNwsVg6WPF{G].Щh).-h^iMT·T{™q%okj\oRR9=q [1:;LlqYiY-swnVnw~xlucxX7K=zxlu4q?7%<6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^.ퟮz7;?]~qlX><6_,v8^)wٌÈrC%չJQ'v{G#63W &krROGU~ k-8iiM1!9Ti)ByNG,Z'!<|em ZZSMvNCuWGl2ɶ]DDJ-cjKBV:`MHC%*3WnC IpH~˝nz*lh"tƢJavţ;P|q|#rVqIryn[#AH%)XR0+|Wxmګ:&)Td EUtq\q;QhItk\]i76nKD}Rg0Q?60=U]z[HiqĖ2\ +Jպ76AV=2vnESW^ƽyZ-HRdd}?һtjVM9vv>`O_Dd%*) lQҾm2iΉ֛uhɬcCivbۮ0lބv6Ja`6z|`8"c'YA9)mkyFZaU5SgDLN'0jSUULtlt=5Ǿ}keϼef,wzk}U{=U1Jg2?]z?M~q絛vt=5Ǿ)L\:3W߫Cg=U8WX)x˃GTj;ult=5Ǿ}k3ph\~ 8WOU^c}. SסiC_{ݬbϼec5p6U^>zk}U{R4uOfW^C_{ݧM~q絛vS>wzk}U{=U1Jg2?]z?M~q絛vt=5Ǿ)L\:3W߫Cg=U8WX)x˃GTj;ult=5Ǿ}k3ph\~ 8WOU^c}. Sסk{Dӹ>{ҽ1Z:w~ws^>ws^cTs(3[z/NNܧNNܬjclultM;9/rtM;9/rS?qc5ס4}4ƩL< :^DӾӟ)DӾӟ+3(3[z/NNܧNNܬjclultM;9/rtM;9/rS?qc5ס4}4ƩL< :^DӾӟ)DӾӟ+3(3[zLnҬ",NI9y=r*Y]VV Wv9  VXEU|nTO^c5LQUQovfi 5gbID8iw'H| )X*]D܆HĶ13:sQ۴C9]4HN8WmJ) QR VUVsHvVJJTrorSRH|V3:G_oi-E޻-ߎ8eP89JU)LNx֝8ciU7ݱ;|nnNoK՚/s:hElpVZRUMi[ 6mvgRFڲ|g24 (p ,0[;:hȦka\US3|h{cg?U.}ٜaTϛXnb5y)UԙJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJR` kU8=~L򖄭NI%II;A!nV~SY[e~u8eKf5`e4dPQ+BA$dwSt[q!ǔWkڍL]o 5Oݢ}ٜaTk ,÷iI--"3ۣISmy u- Pr2*i>.3vg~t(ڱWPtcSH^88}aKGwޥuHdnͣsy_Sz`INӔrQMJwf0\։ =eVp%S&: )7{{a-h ! 3vQ|*ž-X9c0dãpKzaH[VՄjtPd#GXizanFၼlRztNx;uLݻ<߻1n?'=iljt 7gg`)|(;Ԯ( S/GXQ“6)!Vu^q Z"AB'*r1Az!Snl zZ@Bg)U<[ʰsS:c}'.'y3սa')ڮ( S/gEca7Fm酹ʆQIҞ9lNc|8wn&yLnۏZ:T{屩Ҥ/GXiqz`n>уR`t 7x9s0p cGU]zAҦ#]Y4DOqAU+ x7cɎ MAoKZ#BLݔ_%8ʰyV|pL=*r}'.'y3սa')ڮ)"lvtY6c$fޘ[`o*|m( S9lNc|8wn&yLnۏZG[*BuǦ; _ ;N+J:T 7x9s0p cGU]zלk1斴HP;(> a/y1dzTě0ޖG(JqaO1Ғ{/ ˝mX<w8[Z[[)m)QNPq-'Z5寮Nɐ^ mC3^D[8̇%HR%gzO񦓫wP?%yi/oyRT2dGXߢZC4jQwjH3%ۤ5̛kY(&"RR-*S{`NYvjUE4,vȮ )i)$E 1Ty ܵ0c6J-!%IАGɸ|'Yvj-+*̩m8 qR]P Y!%!*I*EsjY}g i!-m0kZc8^Q4DLĈò$i;gLu81zK14Ҵ(JZy^Zq=z_@x|ޞ}*2_"f^_ IZ]:a.eE88 b,sG]Ǫ;8丩:d [*pw=:u!ԩe@c8RÊmPm]vJFȽ-3Ύeؗ\y9[*9YI]bml"qg*9ƞLνҽtM=YRǔZ 82EnZkHW5a~*w'*}j- sut$B bor~1a(]ܢF$!8Z/F*k\nxKrУ9\m1l))e6JK}+^\LHw5i[f~zLu8|TKZ-+J‹`sg>cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4c*=lv\][-HV…KPP=A)FJGh3t w?&$i_Ph%:a0ϴ֝e3&pslǷ>(!}c`FIۂ0BN?+ғXu䵶=.Rn3;ʎX!m<%_]IF9kPݏIq- JO zJmr|qb{o#.3T.@`TW2֒X̔H}Wfmm)`n)ٜW`DC1e.#ILжe.;p7%G%^KH;"CKm4 p:Iig"seُ#}1[$V0 ' sm:[ZV J(||vEuԺaJ~"35%F*K(] (hiq߼/YKu˹aqliI %Ko' \Α"<]!|[`2Å*QAI V P&g"lC ][[i;] j?h`XuRW5R&2VKr;% uCeyRDTYmQvKD~ӈ|*p8:,9()Hlq WA=k y%Iy[܀TmdZYhv.:›\V +LE-jo ~$RR)t RmUai[.ʨ|Ç2^cpu f͕;wn!a7y7WP?$`-Fw%;R6Vm)*@S{JB;^Z:u6)d\Pݐ/WC)HohF[4>4@n+sDEL#XQ6!*>Qrȉ)vq|iv)ei^h{UӐl(HenCD KQI sSi S#N'Q7 =[`5$*F~1TU}I>:Rb#f曹+[G孴Ԩ(V>rzjXɼ:[}U)ԞϕM #]ӈ’ͽ96;Kd9=¶~fϋv3ޢ-o߳ϱX5'oX,nn˖rU)4!^KXhN>w QHOYo]X'iV6t͎hB#o*ڈHJ{@##;v gbėqNj9":uрw Q ^5FDžw2t2" pdӂrTNNh6檽&n}͉eSZRu lJQS`H/KhCv6Y* H)VA^z.mW0ݚ|J 6hh;7pdtGMhjj"2SRijCkh:tmya_2vy15L8GO\ߴ+m7<)P)V8Y޳=zf9duޘ,6ʥ2zC cV>IWVY~FDR&𔭵ϒ d4nLQ T'LA-8AQI+^MN[o3O}Mexʂ:(  5`DC1e.#ILжe.;p7%G%^g^jO.wo Luy[VRJJt=zptʉnM_Fk}yjI=zZtf&RC EŧZ HAPݔv RvK`]-{aɼԭO5ǹ8nJдSevzrt evP RD#+p(%#vRr%ALӶw/r%Gb\hDž"`K2ڜZ'ͱ*#vXv;Ӄo7sgn88Ϯ K^NފXi[HyZ 8p)ہ`6{]or&ϸf%aYJ*9XQRS RmUai[.ʨ|Ç2^cpu _]||=n-y6m7cVki :"bx{-E! yM}%/*I$hIRhVWfR 0)3ijٶDN=%ƣ5ju)I@ ҤXܢ˓oyH+ Z$ | !\kI@#Jر%mSǚHm`$$g|okEfwafopEC mIP HԺs9cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4*)JP*'L1ݳv-@ k ''t;UGS2[KJ[KJi. r7%@C}ˎϹĹ>J&Ŷ;XmobB6,);r`CV[`BMu2ZueėR}JKJBY*(|6#t*tTm%K6ۇ;v$yoWi׉I9ƉBmevP2(clɄieDjK*)q'pܠpequd_CR\Q) %Pp8%񀡽h$F͕j͌w$*[M-ԥ\ZTTt6u4_Ê2V@< SJXR]"ٍ7qe^nd/;9pBH$Pk֌n2%\U*PFJ+g$˙imیȰiC Idf2`yrn*ӑ)2V!`O7~Ȯ֥"5KK@Xuf\ 8򐐧+R9+ v#nnM Ea݅ 8г^v@2֝yQ xVk[N!(i*qjC|E,e㧧96֨}‹5]Jrq= ^\qUd0㜋KX$Y# x>ud[O)qQeKYL[&u;pu$=yӝ8iJteD8JF=:,fQa2q̶ڷrNe Țriv e,(ĵ9o Ƥ]]Pzwo=3c(lr`q4[hʲJ9ʔLEs)MunLhl`ѶҔ'$&4^.Ă̗Q&OvKAIIz% tP*)HR^\YLm-)m-+HuAܕA"__ֵ–䡨3]e8AڬƋ6[2n&r_[!DZC (3%>@[,LR]'n7y$-{+v7-)ٵgz@VI9$g-!eˑuq *ZJsʟ;e* ʜkl)DFTV>!ALz 2`r&ӎEWj|($8nBIAf՚6n>le*n ,đmʔTYu#)q`II)9L^%1$ca+{@R(HYܬcq=Ӳm #ZmNeGfs.UHVK)$[PM-`IWnl/, K=p-ԅ<[8li+[ƛ|-BǢz0@5V_ wfT':eGutOlG)J hƘGs&D¢ėZ@ wub36%æ}3ZvCW IJTUz8[smK:Kַ屗mO+h˜Rc˽JV$A5|Mmpg8'wsMv-ҭ3:1njT !%ALWtA 9oxzft%F$:Էˊm۷r6\*W4}Iͭ2TKKIAq`R}ٹ QKʈԖTSu*N@+JG v7k?xDٳe82qVn?fqm.C 0ARʻyN5ewi7pK5%w$!:U:VI;HYږm-IhGض>kO"V PI޼Yzmv\GZ}ԯCi NxB9#8~ XKD`]jn3hq[N?y;GARZU]cBg^֨єʕ)Bw-jۜIz-Khz3&Ji$-`Rp'g$i;7+˜km֙av%*f-6HSpii$vּv/4ңGDx4vJVrs|O獿~uݻۿϷf77Z 4M w=";HОY"BPI*(BBoäAqw"~z>ܴk/d$/nף3L/}Rr*/[ˈ-bIJ6lI)NH [!w5pu$!yՍ6Rt$)y-qԥUNj-CRX#/-طJhү UP< .̅r }*` I}Gtq% ;zOа FWk: m37.*q6ACNQ֓m¤aSnj]7s6Z1"RS\mĶw%Jv.n!=]1cf.œBйneh[DkRINWPV_/wFnW7>/@HSPR[I@BԢJI* HV@uh6`ϚHnBXHqc$]Akdl@WT%mGL\5.Dr-dHyjq]YRZJORI9$ו3?zW₩U~3Jv)_dLҺmGn>0]];D9©uJϛXMb5yu@ϸwL¤?җq`yR22f-{,vT#[G[a'r$a*98?5WRnR R R WT{|0eOK!/Ҕ@a;ASO~+d?w\Jۻ~1o]:rҔR[ƃ|2Y3v}Ɣp# zzZRcG)fya p$JRI? @)@uOϷ~LNd1ґҳypp⼢G.S1"0--H*[QJ@I' *T|~/t_xH~pH@#nE涨%[SGBqAJRJRJWH%f$F!奶iKqj8 HI$AJWU>92gKw</DJSlo8F=}O-)]V|Zɝ-8 8JA'rҕ">4AI4ÅQVrҔRRR[ƃ|2Y3v}Ɣp# zzZR.<-cejm֝AJZN TPA yRRRRRR>cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4][7Y6xtȭ >ߊ)#%KIVqS6c{G֡v2vcῌ{; 7ύ~'u.|vy3ǝ&=.[5/q%^6S/P͡ޥ#rUM^P퓗!HmDu+CKiR!'!CϥHj륗O\Q},8Xwc ԁHg|DC>"SItRP5etndzMt%Iw4U:nnKa ‚ At*.ʏuadȳ!l)$%+j֕-WH/0{jx!y8H `NS,Mn)څ6r< S:ʐB֔m Цi9NT+Vfɀ:,jLR.}A mQKCmp2ʐ\N@A-3)[Ԕ6%/4I}F@?8JP)JPY/z^ٮ[kdKt&8PгqNt}!+OZ߬rZ⩹=1ejqXJZ;TIɭi;77`[†\XhYBTNH E;٭7i(aR---([n%Iq n+:yŸ߷:g8RϦ46ÊMK2Jj?I_y' <2B˗۬{Yr֩#G$X0NHTrq~jhFpu1q[x *eO h'/:;5,@4n, RT8Tpn|2;vG'y۸gn=vyuyD"\bDaZ[iTԒN';Kjh1e˝ncZ[МBZJRFHZܟP\ HN41#*fa 8DHu)sbڙX Iszo(S"aeo|#ĆHm_rb?OH;K_<jʲ@F+g%&%:!h%q (qFivݞ7W2%rqImz%omZBY A^mT-/XKwǓ'Vڣ3TKʸl/xܔvDKL †[SBv%Dn91]fwvS6IwO)IØRsFK;-sK\Fu5if9 a )LnIee^l` Y8~pȺnE)[NO@s^OYٙ=jݲBl\uXJO]PhZ%ŶL D5qiQ앮$|+iy0 -Jvp2]VnwdGBN jV8%HN#j|gr!RIjLeLoGR)%m,dt$׭O̶쌸CȎꄗؕ%+$0RzbK6hBv-\n8h$)BR 72DBb 27a-w7P VTJWGYIIzNZj\uH @d5A髞✱iɯ(1U53!2a3q֣@m(\4z9 >bq J&Cr\]yƓۓʌ9V۬{Yr֩#G$X0NHTrq~j>{\be /I1qa|c *VI]/gdԖ78 dEJPC(= [&]lZѪ#bn9:9R6vd6=zCT[~vԖZ7;s 9<;sk1,wGgrq?|:TsStoO[FӴ- T?83-);uƸy]ÂH usC6B1ɥ{[m9sNVmDuյĂ2R!kOܼ='\hQ1*T[ޖ! RV }.ݦ$iV\2Cs_@RxVⷥ%!E-!aѫ3Z;!s)jÀTr( A 9#Yk!m8Bu$֜B\m@w;noqApq\R R R R RchIq_1cGOWӊ l_UO)U~3J QZ:1D9©utL~ ++>oka56t枼@khۤͷ}$E%֒QSn ^s#nIA z"|e: qeD$N2zdӖG\oצgKThJ#Ky,,H`z#nê^n>~^ͳg'7mqd⫩4:Qb̃sT+X@i:AiEySah A 殀"$˙ %'%-9ghi[t)i ᇤI793C^˜#HAlKJNS2l~[Ú#cB]Gn{E@jUA9q.;4s~zw0l.>)!m#<`|BURZ-b*l Ԉ.|)Yp('B'YiVl:MCla9H^^JP䅩#* B.qMqM ZBPcr҂mXK4vV\k-xyN{yeE.>$`RRIjKEZ[.GE0ݮacR⒥,6 m ^2 "^؉riVӈSk)AJ\"I ;fqgLM.c/,rrvBcAYc=zZ _#MZځ"޻N"(yTˆTcMayv͡8,wڭmBkrn:c4a^y9\e$%< X;IIԐx۫! 8[!!JY}m30"LjFfXKl6Ф2pTd$~"^!ȁ)M%J XP "6 5:d\"I:6$)$c8 QmݧeLxJd>ʶ'(3Z ;fZ=KPVܜGJS7%hZ\ P)2;@j9~:Zr ;(qh_A)j"P)9Csv npmŸJ.K(iŁƅ%J봏RGgʌԷ[Zq CISRm+)c-Znl׹Ӆw;>,ValVB%8Jvguz5Mk[ܱI09rXuR V@n2Tथ$s}8lms\mrVPm*Bġ -H1soˍ-=+)1qB2tn"U**‡[J^ $ ˅1xJ[E\xd:(? 2ߨ݉:;:Brhb[f32[(yB9[R>-›Yـ]E}78VH7xȵG%˳ncyI;\*x<֤լ#wt雸ЫZ7wDJ»B $`DQi[Y#¸A.qCnBɐYʹZ#V'nVK3/fG6-n> '@pN]{F\5ݣUhvPoڷU1rӓ$-RGAI*BXfkrsr1$Й-JrJyuސtSKMh1U$=Hsh RJ \\̧;RڎPmq[_0ԩ"bktwH(uWuJy-JK-6 JwAOMXl%NMmdalǀݦ\5+srV-A:S(\((((,=Bn-@e6 q++W9x.ojl:Fke+Ca yZCa)!)l`J /Lv tz:&-wnqQjBVQCEY~?ӓݻDpZB˒ q1ҧ[ܴBߵhRWۏhpbZ-G+ucIS+n<ĀT^T0rS*Ph>ЬgKrt!m!ݥg4ڐ$A@;+B j:yT6%0]RoXjM^iI72a*2{iqQʊs\nI7(c )@(\=.[Ȑ㮺(rId[tn۬qh`wӆ+cip# $`"Jz$!Zu+mi8)P=A`UԚN6ƳKRZ-mO}/ v-`lT 9}M2_qF'cjR2gBAnUh!8$%8" +*VH+^YOLALJ tRKRՂnŎ]BU, ]NafN2љhH\d0l- :B @ڕmIJr./wZDZ -ŕI8kn"[.6䡧cB Z3HIYv۵6\!;Q؍ڜWAaѸ-EH2[Dl# Ra ͕-D$ yH՗ǧE¢j,&Xc6VIސPtol- DQfq\™ q);wyG^{@ԦT*UkoY!--ikn VkV_4 ~0|HgBfO*ƣ9RpN;F WuA~rFZ%e;RTJ@$gj=[>Fݨ/) ڍo.m2ttr2CM",V@!HnqXZ֯ʮP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPla1Y'W| ?ڽ_N(*~ uW?4l_UO+;Tx*YGh|L~ ӴO13*T(ڱWJR]I'C9EFSXtm@RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR>cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4*)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP*ãQY]_.c+(ڂ(Šsc=q^10)1g*lb,C*o%ψ~=䂐;&1X%-ZXy [H98@w;noqApq\-RޙuV1`$H%FK )lڎǻwPRxck_ۗ;<)J%l딘ےt޶P ”~pȺnE)[NO@s^OYٙ=jݲBl\uXJO]TmQnoE wfش7"K- YPv$%/%AJBwtt ܞy6`W|`)Ғ71NSjt$NДm ~O~sj"jB\E! ՐBU3I~oGO\$Ew(;PFUR1Z ][r{^E(Kpq-Č%n-ʒ*gz=t8VZ0KJT4 8*=)1/V @q-KVPI@(3jcW]o*յ`Pi mdnR,[GV&C[|MjP>Qe )lo(XJH>bA0Y3j:oLDuYR= ՇGk6Μ5&lyH-8D8¶kI))RpwwDciZ:f,PdqqSk.*Qe[[@JI*!^zCT[~vԖZ7;s 9~jeǮ;|Gqrb-Ҁ* (![}pAul"a-BH!† R֓JvIu~טH#^wf:[J;̲-Jz$;w!Nq+n:N d6Q;>.=:Vj6&U4+;+XjAYZ$ JAJ}Iovx.e6Z>amʛv*R%X!R2-St& Ėۈe*\y:,2!@v);R#V05y8iK^Rl %yv+ZxIn\9\KJs-I qZzH&i-G n/7,|E tl)1(vn ۞Nnlَ8ڔwH*Yim+pn:qi-!jB_v@ "0tdN0 ;kݷi,ݽ W' * N⬡I;@Q;>s;^7|_7_J}w?_W=?V[>v$Zkf\CbH Jf2G%$ 䂞gIJmw#?{(RL6ՄĖyeGTߥ Xf]Z^ 6xh2J2 *zieθ??MIqnn,% ƊFҀ*!B@=+gsP86%ƈ.ȮbTFUݧ,u"\]8(&ѳU65$ciqi!-ҷ:Bc>;>s;^7|_7_JishM[0"jxBe"DP%J*#KF[XgOQ at=- r:'aC}V>zS1w7#Cq0ױ R =I:׫[S6sNІR8Pʶq6䞇eճ rB"0},Z܂e $%(;VZg~Ţ-ND~L-BMq)vTŶpLnV3gfzͪs 1XmgoKh>d']Q4lfN@yim-BqM8/%+zՂկG"2X& iRZxT#IF6먠\mkQ+08+-1nSE%wmAemYZЂ%(t#IAQ^StjcĸICQJv* b'1]{Dd"Yu>\)m+$KV@XŠ,B;dLi6L](Y s`:!AZ~fTMq.l^ƔC4JҒ0GNԾM_hqmI!u.$u-4))ܣ6@I[i EmWSF6%Dcڔ޾P-5_.+pMcidw6< * <$A7Cj4j;n~mQ^y9|2T)8Q?ֻsNcVAYlU^ɝ!7YMDYS};)uu5yیYŴNEK2|:(XR/#R7sJR` ?ڽ_N+?chIqATSt;b2i]_ֶR?GC7h{cg?U.}ٜaTeg~,&FՎRL)ABhveh/)wfVYB aW2 gEab&l-j}5&y .4K)[kIRH# RRRTSEؐY#0dh));OWT*E) R`#)--iH9R>P^/NѦ:"߅gn3!uSKl` ܁@0xXLJMz'6͜k(ݷ'q^y]z .+C08p|U __2.Dr-dHyjq]YRZJORI9$yRRRh3hE&g*ELEDE1IB2Sϛ͔b˩o6xoV]lW.24ͥF66yvRF` CҔRRRRRRRRRR>cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4*)JP)JP[Kbb=Y3YT!%SCq*JHJ1RAҖK }<=2UTDm)qVu}+pۑ S-vY:[b3JqpN'K-{[T딄 G[ HQAYu W.-@hiJ 8^PQI!'q qY(ۭSGInn!` Af\M44s`Jw':&8cŖR.)RBy,E߸_/})[IĕnP鄝BA9".L訏|9[ˮYCy6 I!y=Tw;N?xkw8۷M-A҂r<]"ґ"|n"vwg[A0z )YNJ@W;Ԗ8,;TNHjYnv{Dvm'OEcL}20v5b 孴0qrʰ:E߸_/})[IĕnP鄝B;nwswz#l۷kn\HP)JP)JP)JP*eɑx05p3nk\{2aAItTJVH"J hq. G_uLԥGmRtlAH zi"sp+ڠn@N@9 FP)JP)JP)JPi}ۛk)LiΤyH[cZRI*QIA;Tw|ۚWuwE.j2<$rKuq #Y%G')J)J)J)J)J)J)JLh-5M&:&]@d6'#zP]5I-ZoJcr ,J, `R^ܫi;YIIzNZj\uH @d5@)@)@)@)@)@)A}?Əd^1f4$j}8v)_dLҝ~ uW?4k[}QeţO13*WN>0]RoXjM^i)JUu&RRv^].XȖLqu{nE gq HRZY u=_Nnhm~ O.Ԃ.$6je)M=rme"Zk$0BO+qޖ2p)HokmĨ JWm)7w7YW*S5w̺ @eud(88ܾP%NA& kK1PҤ Rw(*ehlۼ.IV0&hIFR^]# ssh ,Ow8\O=0$g-{OS]:(W 6g(2,ml4)(+brV0 4/gEca7Fm酹ʆQIҞ9lNc|8wn&yLnۏZ{屩Ҥ/GXiqz`n>уR`t 7x9s0p cGU]z(&#]Y4DOqAU+ x7cɎ MAoKZ#BLݔ_%8ʰyV|pLC҂r}'.'y3սa')ڮ+rq~gݻ!\7s:1Se('b:ۋq^\P  9Ji,X/-K-B܏h%!E$-)RTRTRAJim'QZܙle5 C<%E$`) A[Y!*#Jeňg9 *ͩ|+*ubDtvBˋAy[TYU@]-rĦp. iq;)݌ ;' JHWUBk2@* J@JRH JR $ uuL^ hq..fDޟ#୎!E*PqAJRJRJR6ht Pק]eƜR嶡ؕ'2'Ώ7Bs/qXybd dZF))? ˅ A譵)J)J)J)J)J)J)JLh{&ޥǁq)ԴYCn%D$p:dL w[K\} 0-A)$GRqA9^E֭? i9"+nDt% +pQ>\aI' Vjbb6gGnT@QA[e. 6si0I JRJRJRJRJRJR` ?ڽ_N+?chIqATSt;b2i]_ֶR?GC7h{cg?U.}ٜaTeg~,&FՎRL)@)A9,wGgNIFEt$<)ܤ)%*JH|0cIC)2[u8$;;:%\ic:0TV e* s1rp,l.jF[vo7|[k`'x9pC'h/s{&<[RRV\ks-)Rq)֗j6eb7lM{rw\IIu iR7m܄񨨶oo94QZC(@grueZ*jHCq]Jq-`%D'v=']pT%g!{u9r*N:ל;pfg$Z noXzr zO8]ߚU} 7g[;ӪՄc΋>F=.TEYNzWx㗗fFuG:4S2l/pu.?am} k]oT"w! uXⰯ2TzR>:T$JbShm[FK밝 -s8eXS Cg*N2Sѽ=z-9I u4[1Wvm lnk`yܝr1'礝SYNlCb\S'nс5#iۄ17"И YCx ~N3mr)'N#مG(B1)p#nG\A?Xw/> s_yT2pf}K[{nq gmDq8^@9%A$R3HȦĞgƥ!eN;A sLWdÚ"mMD` +iLOt{.COũ! ULk,D*  >L5-tI MޒؘNJYB(h8!I ke@'i`WAo:۲U.Xܓ92T#ڔpI(J*R җZ~CNǐaC!eyPRp@8>S/MOGbAfK'GS%i=\QR: )I oM$[oSYf>\H@Jy'-'i*$(./wZDZ -ŕI8j}v7y~1+=ݻtx}q󭷔X#c n Uu]M<:k9lYF83PFRRRiMYޙ6eȴ\Us-A"BYIB冼- jtzftfMTva6HcVd 7))JP)JP)JP)JP)JP)JP)JP+O$Y/D4%5) A[k @qW ('5 \+E&#ITyN:[lmxrIRRRRRR>cGOWӊ3?zPU;b2iNؿ:LWWwT'Kh{cg?U.YY{_ Qc4*)JP)JPIL(JӪS`)i%`rT7΢~r>-SKDS K@Y-BIJWrRJA UPXQ5"[4&eOEAJuNp䃵9jq-ʔVԵKhH H@+ʔ Uhm˭*Dx׸̠6'G+h8%Y# b@)@+Kܫ]McNu#BCjLTR*J ڣ)J׵dKK[l'vܵ35pu*RZF13/s{9x&"DILˈ2ӭ,m'!I# +ʯh:jo yVp܌8%BXoZ?=2#ZBJr(2Mfb>ZRZVIq**8 EyR½grp<\VapeM:ξef\=.[Ȑ㮺(rI*P)JP)JP)JPLYu-QZB ˭#6@T2>vUAϕ8((((((((((((((((6c?Əd^Nؿ:LS/N'fko<,t>cv?fsR'KVV|kmX%)JR Rv=?xt-=ۻh83^mLo8Ν7N܂DnRF*js VHj$ަȊ{%_yeI?(ؒ|J&=V!2+FTCq6Zht ,%¢aZgCkWyt~qqy(Kk)J;T(ըv]}1i9ŧ"b%":ĔSHPS\!JP}WO |?w^Upɳ8ݷg+$ل<$mES x`ɎtǤOw8\O=0$g-{OS]:(PAҧ$_-΋!:Lo r7 O b#:Ln.ʔI8ܾޕ Q,Ptؔ}r!삜0RrkX^^%(ִ)z$yimqCܬ)J)J)J)VtoE3 G.BUTv0ܜ-) 8`i"sp+ڠn@N@9 FVZb}p`_Cuu6R$-FJ[ Pެ%Kkk{޳^!Gq)XC)@(FpzzR R R=\ut-#0d0zj6ht Pק]eƜR嶡ؕ'2'Ώ7BRRRWs{6foOZ7l3a7yR6W@$Wۭ9NHm 8e{%!3WRGhPJP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPla1Y'W| ?ڽ_N(*~ uW?4l_UO+;Tx*YGh|L~ ӴO13*T(ڱWJR]I((;\e)Pm$KZ2P-dR@HQiYsbv[c[_n;S|) m8)RPPR.@:&Z c9ZcҤB;r)#7+[[I% mPS TG?HZ ʓ?\Ɗ֥˘H[(SiR;@QZDuvp{~ǓrSjPq Jˍne%*Bn94zm'l^Q neaXsiQSPV[r`8jbNL ]M\in+QN6~8l$7‡c߿d1pu|N;\NUP‡Q:Tc΋>F=.TEYNzWxҐ5o;rnqXWG*=N( SNf6XڊͰmmq,/;;F:u40WwlCrk`!ڝ01标NO:KϦCWs^U (uk栿x|rspnn۸g8)@)@)@\ w9„+dT ()J $I\1/Ҵo!8.B0n $(p}(;Ǻ^ܮsi-PjYtRԂ@0whWIvpˆ}[BT2 mku Hަ=Qi}$:N[NTHQ]WP]$^Դ4;)Դ@[*! q$pҔRR'2*J0̾\6@%lB Pn^VՑmlv\EW;b۔B$(5d(Xkʒn@)@)@)@)@)@)@)@L w[K\} 0-A)$GRq\ݧ,u"\]8(:#kt{ +qDRg;v!sP K´Z-a[>%KLi+ݬ $ ((((((6c?Əd^Nؿ:LS/N'fko<,t>cv?fsR'KVV|kmX%)JR R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R RchIJ!TNT?1f4$j~EmM2TTiM1+N4iRqP끞 4~AN)P ;uE+dTDY[] <~J"&Lh3܎ƎOQ!K@=p>0u>)sr}vݿ9婎>0]sܬ元mX)tJDݝns#ϽH0du;})._x_RX೴mR%;Tz#pѨ:Uu&L]wsز#kml<}2z+Rcكw.{Lw-mÔ>UD=(oM-q&dW T͇IRS*]S߿Γ/0s4m9>T$tg,#ڠ5oVK~e TPp!96ٰ=+,mvIvێs鎼1$H)qvaIr$Ko"CSʖrTzI&խc'^@m_'|kqpko"elY7VG& L 5i܀Ţ%Y<Sg hRѴ6[U3n"୪3QG QmF\u)8;T2'Kد:TmvMf. =TӱR!K-:PIpGUG'O](ynRzojKg QR(שAjMZELKMLka[mE>ʖz ;6MC-m0eŸ.x!ճ-Oȥ3Pjm9|_mI%jbcIKF4[J 0JKH!B1Ǡs5sQwJdHZ0[ExeķhʂԺWҁSw~|?{;Mlk'UCv 䈺E3=Eo.gi7؁$9Rp:ޔG8rn]n޻=6J 79Zɓc1ˊ3%kG9=39ij YʟåڔVQZT x,*;+V')=(/3D.Vnjnp.P#LGPӊ !BOGP?0rP4x}7KB[in"^TIX0*3N_n:zsmjal( XڱԨ )'#Аy\'Ƀ^vaTr' zzZ.E Ưwxݟkц(z1;:ھ$v.VPV[Q x͛ƪ(5 ֝qIXց!|oMt)R\gOaŽ)RxBH4.,;PoND6Tg^dRV:C)1G4xӒdD_Qt92ݴ|eRQi%NC-:r o4(4k悵սIT-[LG 6PVRU4S*V5~glv6Jr]Ŗ핰U;{˅I;pP6sJ R^nW3|Euu;LW\-Xd-,.!Tuvʣ:$=>AI$$RN!Fv6lsx x{wݹ1cLE}/m}IcӻJ̔Q딍{FA1>U&wndGn+9KX=2xdV:)c]\ ZCg(|\HzP+KEtRZhx"IQl$w){ f9kպ9\;B6[RZB^JS8tR!jY!\͹֑pLnCCʚx)T 4.E /owۏwז00yw ϵΓ[Y~3mdpFA#Zjv+zAD'e\b&Z-CY˪;NiJ_J Pb}QcBb!)K2Z+Kg߼<xnbB<2!RmRe!%{96sjpA5RP 4}kd)3MC8RS !e;ӁJ^)Y;M!DԶB%kNЧYeN+pm n)봨iAxVk-K\ҽ5|؛ͪfǻ\il)Dlt֏n6mCp]Sͮ;S67+.HZ (5 !فřwTdɊum/ %(ahQYh҂ѵ7Kߝ5T-ilq2Ki*[ i^ⱄ'=lк-&t&HJ ^@e r! BJOՕRh emL4Ve-!AC<\; %J$+9]Z.#a658[ 7!A6RԤypٰ=+,mvIvێs鎱Hw񍯩,pYw{]6Y=rhלheVb웽ݻő[k`gh/`XJ 1l{=&;Y*szZYny R%ێ'8z3?zz2aG!?)t3?z|oRX0W\;>) NOɵ?)^}WTɾۦ<uUgO13*_0n׫,k$Sc%# (3~-6ncvd&W)ʋL_)?OY6tabDM5Obԥ+]s)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPka>=$NQ N2t8Ca* a1Y'W[dM,ڈ%GF QJ>T!S}M\'StjWwcu@NIq '͓Jj5mhl/ 3NDŝL߯ =WGCfKmeuĀʬr$T_d3N?/?z^>׼&y;; z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y> z9#ھf8t1V綯{ji<3ǩ[#ھr9`OCl<{ji綯;8y>('*9rQ8W}Ubjq_kxq*Bri섨 ЀGG=}4sW[8>D͍[1Zȴٳaj3iRA;RORY1N8隵zb뮋TUusipp-3.7.2/docs/sipp-03.gif0000664000000000000000000002477014525516253012222 0ustar GIF89aS_p,S_ھ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sϟ@ JѣH\ʴӧPJJґW/f=ׯ`ÊKق]s%۷pʝKlמ幗o߼xz l[ǐ#KpkV +.|yΚ x1ӨS^Z]n6hͥmdܺ _96?^{3WУKNeÝ'{9MWO,_Ӿ4w؃Ikd}߯d( d 6XU%%Vhfv ((҈(S,0(4h8<@)Di.Bh PF)TViXfQ*9dihl& tixg^ {*蠄j'v(F*餔L>;i*ꨤ驕),<몟j뭸票:i¸ꫭ&j6%2X+v뭏F&YnfKʪ en*Q6[CZ|0 C,PblOrVlr%|c.1?L:93ܳ9=%F3 [J-FOZomLW細ml lr^)r]T/emnl5wM`-Q6}?n.~]_"zu㍫CyPs-pp'zp zRg.ߺzܾ[Vٸ7=G/}okjOʽ~p~_˟ǻALv~L@&, rn̠Mc 8BpF'|aT( ġ GHІѡ!P?"]2= T(&XQ GYQaN"ئ)v:()F4gQHNLo]>(:*fʣ2R!61e|"azլHMyjX%75HĐthKFQi]\H.S(H6`Ē K S^pT/O^z %r%jqx 6w5=L$8ůu&/w(gIhD0cX :GPqj1p٬]i;qDKM- C%3%9IOVԢkb>wϠrr&-4Y:ӝ͍&( J+Z2U64P%(DK:-m@gKυN:-:R|'ӳ h=SyV լk^{W.)]*Dn5*L8GSq:_ԼZ鲧YC؆aS,U'/f¥fAzf,Z L:'iKujd-+I=J5ĭؖ'zRs Ky g8aZ;ʼMXJWTUozkFwzKJQ'oY5SlFYUȃ 6a޲z.M )N)8d_Ǻ+inQZVi2H)qwxJn]m;;N*kzH1X;_Ă5Fx&nd,s:3UVMsD *毚3U-P9Sk'%1\)I*v[c-<=W?i'Ut]2kN+Qnک|tgIB!nyIxx- Sb n < ߕ9D(OW򖻜~fN8Ϲqr11ˇNyOf]my.vKs~)W=،\qetuo_9ON_g|kp{ʭj Z3J>G Da챭zڪ!1;?anxd3`48G>Ek@+QJL۴N hq(˓J68?X/V{c{lZ!{giP: ges$!YxISs2EG/OgfxivUMUfK*_IReqeWqۛ<빥:)gdVn+e!62 C\ZNzZ[v6'ligƛ{Kf\;@ћӻ{[+sKn&CtHd+y2{zi5+=ȋ `XEq]k֭= l`;ˠay^@ך3ْؐ=ٔ]ٖ}٘٦R|N$@gU}Y{P`EY( c"W ل^]8#ۺۼۛ]7OKD((1x95{a')''G;ls0>=0}]ٿˠ&܆ ڪӬܯM݇ #r5~cnxbG|LLsXlgI^JߴN l*\X/]1{-yYw|E.\x>wxvv)I-ޅIm 7Z{ uŧXmNMy|^ CN劾-"٣ &>n얎ݠr> ~ꢎꀎK.nȺb,2n鿾^>ȎT.ﴮ~mU3.|խ(Ǘ\WNN=Nyk?0܌#O&(O(d?Խn$)B?]m+1.%)#DXO1ߣdxb7V>>JLNϐPOls3Z$^o`]=wik_._E?iTM^ {׆~dȕJd=Cevj#}O/bUGߣcfT%nPǩazp|_4-'7SRय़+Ϗ&؅2l |k{x|o(Cn,~">s1@@4<QDX@E)n2 GJiRdD*=t"̙2Ky̜/kr,PETRM>UT3^ŚUV ,aI%KX#U 8KRܳsn^pڍ \K71Ɉ2tqC'C&H9f͕Rx5&ո,F9լ[V 5j]>]:nۨiOl];eࠍw\r͝?Ut#mٵa&yxb /`OJΘ|?n_ @::0A;2pt0B '(C.-򰭉2l=3Kƃ1>;h?lG(P QmH#H$TI't j=DTlEY333l!TH1N͂̈I(3>OA5RJ*#REK42lL)16%2sdsGO³@B_}5PXuV[oPDr2K,]z030:VHlbq7slҶݖ?w܄Z Wu,r(ku7^y+uW*d+_}ŮKMto&Z?fa5y'V^!8cgt;7d)2X.9eea_at-枘A3yc׭c9i.f: i&j' &|jvi{Т+1bfmjF[h;(|7^{nkW6ÇCnbXipV#&JNr=􅣔v'ԅyWu)u~=D;rϏ0C_+=z "=ϹI~a߻tc=wWv[{ M["Q.YV60d͇eQQ[o;@Vŭt^2?ov۝=ޕ{ 'OvZ#7é`~XZ?Ud)D&UKr%GUtO\5 4ٛH¡oaP3ߔъdLkȽ/Q8! ȳf(!rҝ=cB(Dt uKu zbd'T)N9JJ9 9r$ueJ ^E"^V\r>-$&#RTI'%鉪c2q\kt`(* K X/QfCOa4\F{dTIgyUS3Li{=RW}\KNu,'Z̑%*n-Э\bjV irKr*^I/hRk3es:ab2Qh¦7ٷIc;_(}($vQ!bPll5eZ$4-ԛ⊔L!l[Hɰw|hX2 SjZV׺jj)ckBہt'Qa˦(*ůl+Ru}u!:nhE{·{_ 7 :#XUjiܴ*O+M׼!}5"pțaspc 05d&Ŷ}I+&WL[rFXeȸEi^Y+Y$Rd/ٽb̭*3,f67|v縅ЀzFh\Y]4lG+gϕv.ohL[:҉vR{5KwzhMM!: ٪MjYOճt-i1׿v=lbFvlfZד#uOWc$Wmnv iW6Ͻnvϗ(ճߍv[#Voϛ9޴}_;/x« p[p;|8V9⵾xƱ`ɬꔧ*x_>lnf́C:\<u/|"ՠ7']KWӵiG=H?:alcG;u]kٹu%niLr:ݕ ;}?Mfo75Qz\^xʧzjR+?zo_^vSk#_IOs;zþݲk^qo=__=U㍿_}}KKw}d~OM/:ݾE1n?J_J&??:c0?L@c;4% c t7u315 !+As-Q5T1A @A 5\*ɟ[@ 7O" 0B Z@*=9$IA:;=2"J=S4@в"107l1+G˟f:,/z-2072*1"7,DA<,!=0ZB?,1 č1CC<߻tI۔M\MT48,N :4BMSΣaΟEwl 5tBLdO eSOd PztCPPN-Q=L|=mQ}QŒѪ>QQw{LQ6"  )- $"5RVH:H;G(7) @,~TKe_$Jd-B*92Hh|PDQ453i6xGuAV&"YPUVH ׄu·YZ(.{],PTylӊS ٘=9.#E).$ͱY&ETPB$ϗE>ٴAEZ[x }UZGnL L-Omڭ1q<<ԡUBPETC31JH%ۚ<[M[f ;TۯeY#ծ ZeN׾嘿5ؙTdrDM<VMe|B\HG܂W#%iEu̎H]zk^E1t W]s}cYՕSޥ^N^^6 ˼ZUT&W `@eCm_=TK bDTF-7D\OG\`Y^?TeFUF[ a = .RrD5(L!=ZstY9D܂1\\,֩ޠ\.SNa2-EbTz'6 b-.,nbb/06@#c 34Vc6cHsc\81㜴=<:?BAN@6cD4E^ldGHddڄKVIPvOMe$R>UTVVu徼* YFZ[&\:(4e_`K`m4ffdavbn^}kl.mNnɚG_.JMK&uvwgmEz{|%S1g[>3[΢槂6hIiVYffhu.K /mߋnk<>"hN$iLZfi..-ښP~h=Yhћ&M6c6F5jl~jj.VfKjjKFbfT >kkvkx^&BLl5r^Ȗɦʶώ;sipp-3.7.2/docs/sipp-03.jpg0000664000000000000000000012430014525516253012223 0ustar JFIFHHC  !"$"$CmK" P !"1#25ARUs78QTaqu$3Bdұ4r6%CDEFSbc= !1S4AQaq5Rr3"2B#Cb ?H=w{-ǟ{0a8[m܀DTU䲫QDfH4(E6:l6URǭUW$jluv7&8$ʸH)T*gjx]#hio›]AW 2DԇB- ݸpllj"if#9ק[ܭ2j'g0@FmϷ-]eaTlI29ұ߿V#2!Hn/2Dy|fF˲=o sX3jCO5"" i@|\8OLF48Dp'vN]*ǯ ~8osg[SVmiK$,ٷ[fL$mƙ2n0- ;Ubo1l~]>&Zy$ h97%ش˗:"zۇW1ӑU4Di:\Vpuu4ZZKޙIGwZrL*6q ܪٸ"|CؐU zNq ǹ\UٞW،fb@" Oq&ZsM5p c:g+a' 8Ӈx8~ߺ/=6Z}͵5L#(Ю|Uah Y PYͦTNRqer#2]Wv+ 9@î5F\U|<41Uz;g[SV٬ގ4{ҍZݓBEmA 䠽[UG7 c-Ò ZqȘi3sL*rșQߵjq颪#X}Z4ׯF)v11{|4+~i]f6;Ui/+[Uͪ|QEZxf ٔE=CqVs U$qP!ϫ*(yRiTpgӺbq^M"f5:?>?g[SVѴhSu_׌]N #$EA7E'h~"L]=z V-\cJ%m-G(OXQ@ GwzҶ ׆;7͞D)\c5^/Yt6hz۽3Nޒܓ':DbvȪ/rU{\h[۰Ƹf^}yrۨHhf(B$ P&S]V^-xtNq1{.ό4xv¹+~x8~ߺ.:Ey0%kPIWq8N[VKO,.F_\Yt.zBɒ9ӕD,q6)UG oM5N>Xii'D݋z ZZ >pg[SV49#!Z5-0ݺ0qYeAHKL~Z}!g D7+V,`Zp- &7[5,봦Xӑmhp\Vput]Dm/urm[{G#}&M$GՍC Qr7q ɸ OW^REUvTp?&";v>xcZlZg,1//puuk;LN>/vt1_)=ŤhTj<.LԮ:VN~aG>m[N5cglk&/pu:c⻌inx[S8~ߺcS1 ?eoO_+~S?_N+ƞ/pu:c⻌inx[S8~ߺcS1 ?eo__"{+^:sN'ƞߋ}HkS8Ӊ1$_eou/Z?N{im[<]Dֵ8Ӏ8{[~.EVO_"+N?N'ƞߋ}HkS8Ӊ1$_eou/Z?N{im[<]Dֵ8Ӏ8{[~.EVO_"+N?N'ƞߋ}HkS8Ӊ1ۃ̕Db6O-[Hjm9} я!'ER*TOI=&ieǪ]AVF `9lj15ڥ>7ִM]c%rUSkޠޭJ[ E)4F攅 yUs;wc:N{^K  {F?EJR8R_aUWD{Ȳh[}FZNJG SqftFgՆND5Ncp}AgH.mp$ӑ:Y@FniHPW=ӵJ+k ohQ*.+MoHlQȈ0]SC%첯-ZQu䈨 2tCvtQeE88N8WKU3c<.߷4>R1566h␳*! PK rajbwy6]*dӾ1ș' RԨDޢGed* *a0x9T~/d;1Lh>KI9FES1"zu\\PmZ|p''bSe;]Y%ȶK$d/;-*N4IQs}U=$|ϧ6uH*쫢g*ц:nKLXMTU g=x~ߪѭ=y!ƚ'M")/{[㥭KT{t4~${bH`YD\_?ҩ9kUgU38ί9eE|:m*w(XԏQ-s$=y{Wp-;cQl*"c=U^>}+Nqj_Wg q'kKXD>eq3-=K3Ⱦew.dT;W3򸼈ɄVFxE֍!L뷯{|6)dn[IGO?f;.W(;(+gg[<ن3$Jsxa$E#h$JU)^xMxcwFƩǫNr-jۧ}Dr? bXQEَȕ5ԏv24(„j8_*W_Jj1w8aU+tr DNR q0⻇>1I;.;VZ.@ȗ|I. m_gc`J)p)U=(Mވ? UWOZ<|:_@3}Q% P՛L.\iƥW#M1{w{fKѥ8Z4eO4m"+(&]߯*\jfN:'Z<SEQ{ivw7ZͽOs4J+@HY!sMjY.H3>A 7SAQ6 WYFwESU7j1}yk׎WgSy^!k{a?{um DTLZ} 5.Vj0Dd %A'TDLg+$gaj0׽rL6oչaj4{DrG|DHK{/Ӛڷ ^rN¸+(n$\e~6aN{3j&'lOj#7sL -HTڷ ^rN¸+(n$\e~>mxGNNmYq\UujKv :8pEB6жrH+OU*gݵ3E1͕;Ju[cF'O?b}©Y> ^TԻr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0OU)|Իr lh0_])|Իr hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}R hE0{_])|ջ}Rﶭwl]"\\  `HHDU r7U8*(H?ZVzN5X=UŎoYQ}%i0d4 ɷo3b;ϲWrW kF8*(H?qnbj$BTּ+X/wZjꙚӯN13sJT4jцou]S[jrٲ.xZAU4eqUUpuEjR\Y.,(}U*^+kZr-EU#u[BUm4h~$|ϧG{aMqo/l7WEƯt+H(((((((((((((((((((((((((((((((((;`H23+Z]օ_I;kL|?RO?Y/3a3cznWE !j+ퟛ*QO+۟ק>Z<ysG/k͛yUMϋ^>gt Rjqt#I c@h%rNW:#✞| 07.cnvg=}go-]҃NGFtc9t7xho kP8N˻Hu}Ș4ƻmbI$Q~W8AҦ#H#fV.\$kĬ)a;e9;{{$%fFZ.,. iČ!a{9;e;;=*r|"}?Ac1' ;qv.O+H|n ZrѸSɰ2 a{Do9i;m񆹹w|Nv۳9yZDgJ9; gLw}汕+0#컽ttu}Ș4ƻmbI$Q~V84ٕ6=!`j+ XNNN^J#Lhs6l)7/0X|"}?Ac1' ;qv.OtNqw/ e< #"K)6i;m񆹹w|Nv۳9蠃NGFtc9t7xho kP8N˻Hu}Ș4ƻmbI$Q~PAҦ#H#fV.\$kĬ)a;e9;{{$%fFZ.,. iČ!a{9;e;;=*r|"}?Ac1' ;qv.O+H|n ZrѸSɰ2 a{Do9i;m񆹹w|Nv۳9yZDgJ9; gLw}汕+0#컽ttu}Ș4ƻmbI$Q~V84ٕ6=!`j+ XNNN^J#Lhs6l)7/0XJM?9>ZۚWTa].(WۿR;˼F<E GZm_-v;VZQd9Ry/*3bdҕ%Z4XwZ,sf {s/5]eQ!jQ!0{T\;6Vњo7k}hz<Ӽ'P|q%EQs;E!TVAE㴫eV9jүMʗC~1g-o#M5q6l &idXLxulۊL8"PRJk~ktalol S'NICu0wM\G6|Gr f ˖1DU<(<^Vf͊ +^]PdքHpܘVBU&´ʭ97DDkKjaSu8=V搅8я1"Dp )]/C]s$^̷f9%6KDT9Șnʺ>HSch\;vH}VK->́SG%7iU;asJæ\vnO[݉h=c&H?'DP Uh&!*" wEس'J I8y`[W. o aQQruDG~NڦIph]ҘW#m:()Pr[EZw5]ݹ:Cv^_&ߣv-(*;[lV:+c;=|'=C 묺%l]gcz9GZe=ÅܛsՊzW^-NYƸ[]d,r%mh"& [AHU5O1h4vܥ]oj2ӒhsaSTUVT9}+^C`]mU$6mUiLq: e~s[ q/4Ric NnqC$@ADhCҺ]١z?qV7s6%UB&AUX\7 ҡ$n%6JƐʊ&ULMUa-WskToPt=eʏ4BqBߕDn4!q91<2îpG Tlw7W$OW^zJǬKl+!\fFdu(R| ٖ<*UH}k<08s G& #h) *8 P%ܤxMr :6dSKZ Y8_Er!>1Eo;XڻU3TȷkmZ% ,` ZdT"@A->Z$.s)h ۛ& !ȯ`TypUi]AfE@4SR#@+g6``=*~bqrjM}.KZ\:.q2QFU:hHFCF"Sfx IPTBElΝlc6Ƿ9+/6$IE8W^ZZ= zsuø$3ϱ2˦Q:ҡ!|oָ35/ږ]u"Ƹ̊ӯ8<ۨnuUm8B**"xp%̏6Df~InDڸ ^w0^"jӫn7':a8fDF\-Uq2HnIR%uCZw>o\{<$G7Z2ټ2CG(:Z[YJdUC. Lm#"." މ0!.w6IKt*MK"]h1_,>?9ry6LjU!ĩM)IUЭZ\ݱ՚m7F:hwӖ*Sq2%T [zKo[8v C|qz&b i.p!zi}Ŵ[iɲqi ;PrLUDLUOhMkju$Pwt!qmĨkL^d8P^4\HzkUي67%Ԏ@U'f8k]Fd"(+@nY0pɰuAv Q USܟ*^=,SNͮl\qp@9ʁU<~K \HW[桁Rd,[JlLm шPm S1;`,ErJ[1EtPI SʞUʢa;"nL⫤kEiPܸz0r$cld&%}QxH0Ve4KK"g\l{EZD S̕eG{oCJޓśTXoc Bq&+,rm2)Z "cJiΩ $fHC!rKl l|(ʩ&oKۮ-sATIHU}u.P,yoԷ3BHh(BQ3Ȫ?|Qx6D&E0]BQ Vw Ta /UU>ұWF]uml[l-/'m^Hi"6|q-! rwuGnSMBistQ%6٨eEu4  9Lh7w[knjB* pHBELjr\}oF]S6,R*lel-.Ȑ)Z"E *m"e)*:yh6ӓew}HUDk8elrnU_ێKs7lPUYDpmm ޸ 7G"%kvb n#Iَb(`*lw$8]6*FPQWh䰊쵣V%3H/Ⱥ_!B1ji\s3@خe"wRzWtŮ95ѰQ^ppO5(vH*([9)J G}޹1yMfH oorexdv7"*QuZKpBI"vry!kj}gkpɹ]26F~豛']iwmThU#8_ PG)n3h`*DXOUU~E_WTzOpn6뾥FommC{m%E [m0ڎWu2G]94#ڈ;ELNr![\Kut8C[LH6¦DTBL&>J)J)J)J)J)JRO?^j?~*UTV+E^c\p^YUjU5 x9>!G{aM]}#ϧJTv +WJR$ Rv8H-ƻ4!}H5HEbM*\OUN]=ȅ6wxPmKPFQY$4PT@]ЅvnX櫏ujZRfa~vyp-ϗUT"b&4p`Åik(OVEj⫀$j{6kjkD{Y@c\bQ\ye݈! h,쪨v+瘡 `iҥRtw.TPIQ0+9xz7HУDc,Xն[N*"M,*}[\YPd[ `I6r,tC40<8cv޹ET@s¶43XUZ-*[O;FwGv]u7qmhn&DrBBTT,ef(m4yp[iX$j ksYMii;%Ȥ&UaI;DP 3¶oy>Y$Vvghox"voa0J^fp(7eȴ\Fs"@  !XwH/uA][coWm\x|G&^˰`9md"#E~5h;CGNX:scm|mG0Mƻ!E3"SgoF9d-6튦`-PkMIWO\ݜ67r+UjA[5N3ʁnU*YnԱuCm'0ytpHBEޥDä;BN<륹qP[My AqG[%+mC*%AՇLo/Ekv;n6J`+L*@)AxFGs[clCm}%sa:Xm>̈hiM+6MŋcʇvWuHQSj"v-g=P-ٚaI%n7m uB[}n12P4qs؈.L"dvICe'H"s/zڌ,Ćupcr>f*H%TEnU߭^mצ6KPa꼄*e2O:0_Ifd$GƷ4x18Mf]qPjҔRҖ6?O3Yͳ!n۔3e3S4/W&_!DBw,Los" +ʅyT knRō,G(lIo{n eLUW *$9OY xgEx_t7rs{gg3SZPa,[9]܈DRN/4{[yIEV`7(yLU^aG{"lϹ˝>Jb+[i[h"yKr9&Y"P`8rm*+I~)Èci[YDwtdu$E+ A14svr"ߥ+03Qt:rI1unutm+xn ^i(qZU4lUED0bW{T=Yfmpmn$# "Y'[QEBk4 R R Rz^w~m˔ [EKcachSqS9+:]E}U:OK;f. q^O+ő) U]$Ya:848ӍN6eED "QT$QQp&W V#6UW ȼGQA'eqH`bʎܔv( Q"޸Rm1zCn9NgJz!N@[Fw(*%3v"k .H M WOaeiۂ瑲 MƹEn,drtnEˤiOĘ⊛-mؘE$PҔ"CU%BVBLYmX\r:24ew!\I0gmt?7>ܜ|fy3堒;:9smYRdJia8,$C%؇ I%9k+ł+pT]Y5$M[q+0Nڴms&\ 2Bwve9:Th7 "4LqEMAUUilL "(D[]Mwy ߓw3kב|aSR?tb-cfmLBԢ"Ig{)m^t<;\oϷv|Yr񻻨IpF;֛cΛ E$ɑVնDqE8YM͈iʒl#t{ęeom~`oQ!iދ$$ ]Wm+x+gȴ˚ÑʹyhLGuH#0՟2~afcJn* b *5zRW2Fդ-dddؖK.Iv&].=p6orI2T^踭ZڻOuKOw\ڃ%"\""&UW&+VJRJR~?~*W)l߯JlUgUsW?$}9_Hk>ksG}C!G{aM]$|ϧv +WWOD[Ʈp`r:&Ty^pz+߫(*Sn词L;~@Ba v #Mc\o<cLG-̊v##v*J\lZoP-[ayq[muȢDByĂdގ]5dn=p5әFR&uv8o>@8gv+-7t`rB7MlA47ðDڡ:d햻5P{nT6HאwVvqQ-ߠjp %Ř4WH6UX"qoaev&zn)ZiщF6d&*i8bXQ- &-݉h\H.:+Z˂H k҃m;EzE;wctQ6E%$5ؒ&;pE7skB]ؘO UUZDڙq6L-PAxD^l"C6c/yh,iGUA"3ݧm xcZF35%HT@DTJ K@1BYbG.S1"0MdANꪫD[d]:m6JnScfn *nNm|oUMHrZfL`p#ʸA#iARä}X4ثh.؃fYp*|jB8"*zF{m=mzH"C7/DsXoHD6Pļžu$DoAΝlc6Ƿ9+/6$IE8Pt(3:e4Il2$KVe5Ȭ tpO(eZVS6m5sjNݏ-nu.q eUBL9}( hoHDTcjTQR^.Yu996k ]?ԿZ#*S1&1W]W PE?:'V~MvlP)x_6b|bLpDU=o-pm D ERT2Xt=m_ƸƁ{蕋lB%5nN"oވH(lr]t4Dr p8$EU,!MegDbQ(: 'PU؈WoӜwgZ+}u\Wx "I*j( FDMSrވ\v4!*Gʊ$9\eqUC2.<íW#Dpw 7 &SOxiލcIf5ґ~I**H*k@,t Y.8h EMNL2^UNE&!w;%݁Dm ; `S4hèlq܆rFC.a9l[N.qdPE hP-u;xͷ%kԔ) )fJQ]NsȖt},cpMqp6"*Vdi7?X8v`2[ul 0tI@Qv"Elj6vۦI` /cF qͨJ`%iՊF.?s@8QDz&Dd8}p(/x2oRz4j͕‡"Ӥɾ Вm> VpQzrg R1C\ڷbNo0jI(yG#|۶,|8QG78sznq+GNX:scm|mG0Mƻ!E3"SZ]sxUD$!$TBET!!T!!UBEEETTZMhΐ)E)zCbBGm7* "ec$%b =zs57 DHJćՁEaU񗓞:&6Є.168ґr U W*A]/Rt\_Ke3Y fm-}*ʑmPmSx4^%R-wF'-V5#lmW$: \8U*4Z6zks[DPZ @ۘ7-ڍ@mTw> ɔE\qW/KnӐ]l1p[~1imI/hnqHNQIk"sic&Wi݉CwA1yO(v+瘡 `iҥRtw.TPIQ0+KPM>Q6>8mBNVHS!Xc[nnzhT UpR|\HS.&ɅEV[\iۈŎ:YA(#`RɱRu0/ y F6ɳ-٨,}l_q.ClvLEE6U6!/m =Ha~ !9i%"UQ5*Dz7moMAr2WNв,'"& ;VYb0r3hMulUpS"zh/~[Ɂyl"2e3)¨?ԶDAŖǤZ(]2 U/kznlhd[M^djҍho (IlUECyrߺ*P L[c7+ptCɱ@o$/Ŷ,$׽#-؍4ݼG6̠xђq rcnMBDJ҃J=yM#Ek.ۣPڱ4! _M/!eIV iB}٤# `I0g'ǙR(ET\"m]ڰݝcQ7ш %UV! AQ܊9ܘKMP 1A[ۚFbYMH-P:V$m9M*H`eB%-*YBZ*Nbyڎ=*[Q,GHGr%AUR R R R R34T )M'ffzUgZ/K?UWƹ#9_Hk>ksG}CܺE۸0۹s?Ò_Mzz]mq c*ň2']Hv1$/0 }AVQ6iUBCVpATm5yf[QX ؾa*\gM)Gfݍ* /ED#HjpYI8n2" Q;w,|j9hXs)[:uh:y&ipܲ5,:W($f NBFW 8@MWzߣ2mL 2&Fq Tw DB"E$Yfh봋~s,C87Sr7A;D_R Z3ͨ5-n "4,}L A W6Ϣr-GGwX2 f*DYt^(́ѩوg!YxMUQnLFW6e5Y-62mɈʦ^gT 3E:"0DFqo| f `l3,*b'z>ֺf6"K1j;lT5.EhHyDv4]9Eya6QȒBփENj+D{d+$KrKTqmZ4yH;nD&UPe;'g[]cݥGxoچ D]&b*".ݿmзK^:Ȋ3q(Ra #hR&>WKӖ++X'`$~[QnjqHQp9%LET%[gюsY/6Mbd?T%EG2mMlCyܯچlA'.7m;-|gOn-%wGr.*%sQbŒfK1 yUm;c)YgJҝ e2 6k`dH+u*sm>7OXQz3QQpA9m[}pI].S26L C_BslS;ưXj< Л 2mT%Bm82a-ېWSb]<-Z~,=ThǤ.ڂa;!EbSMǛmq񂄣2U5ew̗:Lv/3N(De2(u3qLAܘG$]UyƄZ6L̨c]vET+wftXB/ޠ. &' "olL5 ֥o4wtt$J(S}5Cj3,wk*QE)nlP #:غ\m¯x.QqpfU0 r{qZ1'TEAztXZ7mEd6L  ZScoj`/ķ1liXyN)2f= rT!,1bJæN8d~]vqED!(khmqԚag&G&95!UJr8DR@޽j&̷f4 %};T y1yTXz=AgYbGeKgzL#F֗2#i2$LQ4TmA0)Ͷ{ HBQX<=cOܶٳvrܪAݳrv$Di!hk~u$ۑqwf[>_J $H)qvCPAan-ޡ\oV|XW2kΑ(ǗUL "Uz522e%DbR4(J.`\)& <K-;oc;Hgt Rj;~qm|i) mqeAlu$uȲVBh㉍WzPU4nn1}6pHU쉊Q`κΉ>R[S3e֢8;I2M`*J =Y25# b+УO4s<.ڮm)"lLc%3*aӃag=X}ߘw6eDGmrgVmyz<$m9ASj/ʘM +eA2fDtIJ3'*\ŕ$L MJQiC!ryF<(dѐEMSSjfvGNZ-8䩣Jn8+ƊngIv{mӑ IɈFI:ە}h[u,]@z~ [r4.^t2w:Q0"؍s!X:2ppjFW(.G&RQQB *At3/7gu\v\$f, eˊ:d))).j꛵K߿gpPIۮ5hyv'pߟo wՒ|6wwQ jL"h"esY2`]FC.ivKuVk櫏ujZRfa~vyp-ϗUT"bo>oKKaێ3N:om "h6-B.K-5hkcl0xM ]9ADت&.wF~t\nb-ۤ&!KG[dTKIIUxSR҂ͤu=l|޾Y̝3MkӟK?UWƹ#4z=zHO}5I=Tߜ[y{a%tZ|jJNǨ/.o\|y9f&ݵS8>hˑ"\}2q]5#prJUW*X\A͙+7Y$80hÆ|U|S)cY[Ƨf$Ia,[I|l@ul1ԕk4kX晆Z AHpu>A8wD=U=KZAAHdD|#7GIIIͪLK֪K֣)Ae]쒊]: ['bH6MAUETUD\~5tu|ĸI)f;qH^ꙨPIw˝o7G,۝g\gՕu0r +r(6k 8 +hHMۺ=($_ |rugoz}tP_{ LJkcn8))Az]r]: e7UWU\~RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJR~?~*W)l߯JlUgUsW?$}ק>Z<ysG-zh;O?{LJ>jWoI3{58>Js4c5 -qJdY-"h$HFQqO3Vj:<#o6Kۤ.4N2F.QQ6F5ywߣ5x[yA n"AUN\.T=u螑]KpͅÁ- "p*(k]Zm\jU_5jP 5(xxp$Xȡn4 攫XA5}e>3kU0n>W$r"B@ FiO%' -,#ꑡBQp7ILһ>fb{pKj \,6qxW*-z Fvk09RoҞBz{Hk+JD`(fԚ&!`Z]qHWOE$NXkzlb32QY 6 !mRQ$*T^491$F.WHsb 5X QӀ M 'n~:ao5L[}`:@ m2cȊ7%Y5qQU!,/ТHJ*/X񭵗‹++qYrňͥwCi֚F2WLxPU[W~ֶ֡<,l6dq}A5˨EU!Ni]IZIٱq#5<̆Pmԓ964W8TW䊪׭|w;tfqrݷ<<{"DU CR5/zuѷ<(ljUQ*DD۰TViSӟ$Œ6aԆ|rq>%VӴSnQ\e*V CV^8Rǁ)*pLTNH `m Tx%DCDUQ\,.0¢*ꃭvwSj{Z! Ii}MQ/TUq+ 㮴w..n M #[m^C j%"!Y2MJwMuiܺGsOZ ݣD!yNIN;TlQ6 k=J)dH1rS}Q&ad)rQ]&6.1AR_R!}b="@vg>l#s("B!":D.W@ulm&>63_P5A3U2Z5'.m7æ/~N3Cۻ -M-fz"6lyIZ a;.m*Ӛp[ܯoiZR}l44r}@d\* &7V[FDZ޻Ϻ.Hr9Eq ƽ>UƗ*rWPkD-gyvM8$1iƌp]}pI.\]T3mЖۅTNw$%(ȸF>*/o)H8+XŚ/W4ӱfGK!Jp4IM"e $UKƚ`"u.ŊZ`z qiUsR&W*JyqDIl;C&MӠmI**aQkt-Y^uLE6Es1"ȭ% [D5~\[)V Jl;Ĕºzcj*ETTSR@[L[p\B⌸JD9*T $Q\(*%I5덖 R)["`-HHDUQ_fglKq–Qqwa/"QRR%2֌Ű]ˤ]qYUMGp BB] u*sm>7OXQz3QQpZ5YbyW7B]/Me#¾̱¡8ށ0WDͻFоg[u|QӑשoqJGWimT )&)A??SIٟٿ^G+_goץQu_Hk>kӟK?UWƹ#4z=zHO}5I=Tߜ[y{a%tZ|jIJRāJRFwm+o二7l͏E$I+i2USj/$rMI D͜m桰2y@]d0TL*n b_#W GOGvhPDAqDKt#)|&vWoi[;4CAIMT!HH@h6mz:*yw" XBHe,"DgNk=;Q7$ڎ) ?6mDJJcMֶk 0:Md=#1P lE7e%{+>jըdF!V[VQ^nLs\o\ce,%3k 7QܭK4'PX5Q–Twa9Zn{L[݆͆pGe9!T[6t먢HH!nL"Uh&$mM̗:vͱ@IBpXPgJK_gRvI7Cxw'tϭ=Tuè4_LG:8`|LL(&#imM*̷vYp@ -pW+_I:[SE%NlPcKz.S=(''-GjݿYnvH>,w/4*ֻWU.kZaYxJc7tZp^nxAͪ pF#S2[11GZU)II?:**/R.t&tqI"UU"'Dե)@)@)@`Etv,a^Gc,BMە7& Ԟ9gzOHcKcTY;ѷ."* ,"Q([>M\C/e=ܻ L3X"ʗ9caBކ"Yޜ]P˹X&[σ  m. h5݄SH0ݙNN t)18QSxUUmb`:@hW6Bx ز](&eUEC}V0df'!BkӟK?UWƹ#4z=zHO}5I=Tߜ[y{a%tZ|jIJRāJRJR,t Y.8h EMNL2M3nm5bCn<Q GF8͌epe]쒊]: ['bH6MAUETUD\~uNfEwn 6Hw*yGSP_Z~ߪXZ^IqGE]^rUSQ7!r޷^.ز[ǚrbA6"`0 1 oL|RJlCf$% xupǑos|>lWţfbp."JVA9"ljtXloI-12ИB]ޤ%ׄOxGpݻx߻n?՞J ;cG=cca6qq܍ɕ؂’m.z@$ h9٘˜ÃЅw:PLF[ݳ,5M<$RQ"ʷcώNI[ڳ$MD%RRG$7ώ˅^ه7Oìlsy_+[}(;aZ־[a[zMAlrnѷ xni^Wϩsٌ:2,\BjzZ)NIa $CS(ڵ'ڧ7>:LmgΓnQQpBUOثA%`D_ѨMp(s%!i_DTʫLU`,%z\ݑ!'uR7 *DUUrJRJRJRiqt7 cSPߜ'H3y?ĒbPzxbˉn·hqi>ThI$ֿh(((((((1mqzeny]&pETUQS8^EzPY+6ZZը!ZZ6\7#qeQbf[|*޽^.C.urƀJ?Z֍` )M'ffzU5?SIٟٿ^G*֪ U~"18HůN},*Wy_*󌏼_נCjPyo/l3䮋O^)JW8)JP)JP[ zKOj9%EpQd;HRE^ʙG'/V5:[uh[\S9TVMe2D[)H{-uQGJ <2AM E%L 'uD]NPՈ]ʚ ys{^pѲ(Iw)Eh: ;l"TJ}m3-LqF2By' '"2i %&տ(̇ane!']L+`H)rc@]1O(nF\L Y,krsϕO,w6$=wp#^%܉V!qp#8*`#=I2I"}5rD"S{񸃿iFO&HȁvMuZG9<x8*Jhk;ꉷ*pTw>o\{<$G7Z2ټ2CG(:[n%iu},pA{TjQ#<"ӻP-uQGJ <2AM E%L 'uDPv{1mťvUj! [<8ʃ8 Wn]uEԽc {}0ǵL:s4GWCLxqa;䫤h1ఒ$ȴ.#h$7 Tr@wGHgx_(㉬R-.kz㐷ڤb ۮm O]DM&AK9Hww48+\CnErDQ"tr {y1 R R/эŸ@v#Ԍ41D(p2P7m*Nbyڎ=*[Q,GHGr%AUM^dٖf>tAd/g{ʗ!;O&"* JRJRJRJRJRJRJRS%l]gcz9GZe=ÅܛsՊ#ȗ)vD[iH2\ 'uUU"P[X- _ ]ʔnKO4@2 BnUoEZ]I,W7 MۑjK$Mª9! `1 R R R R R R34T )M'ffzUgZ/K?UWƹ#5ϥV+E^c\p^=~$|ϧG{aMAo-ϒ->5{)\@)@)A9X"]I;aV" %v]XRA܄;dۺfiݯDM=.e㭃HJ< *;.tŠ@$<4dṱHw *".%M0_sSKlj4-B,`I>\pjp9C^p`o~ƝDK'PČ 6ORmXA:TЋcWel,pͱژMJ-iTu1lg>O="Pw` }tҠGǥ԰ݽq(93OU h]p: -|>Q෺|N76X)&S"4j+{2[h( nZ",㉵s= ) JIW d1۴E8 h{&WvWUr42ޣiۻqf7 a3mm\{a5$mM̗:vͱ@IBpXPتDUpZ WӂwwmUD]^˅A9gn$Ant|WDL/re+u5 MBJh@ń,)q6AA2 5UEEr2#oO\cQA&y<8Ģ*ˍEB7>ƈٚsr\y1m2/r/*#D$ELfY,\pµ)Ai7Ur`ԐJP*áo0 e;ZXFqGuQvmkRCO0 WsE>]%8We?;vMɀ.pam_ DL̊TsNl[EmM"ءrg`s*@ϋ B^fI! 62n^j_oVnB{-ΦY6G7MeqǥL}ATe :0G1B-^0dgUбlF ]Ty/qaUQtKmڄCv%d0M[Ey1Q\M˲"G< ,[NvgX Г<*N㕭(VYkIم&.Uu]Ã-K^6*hm"`7w gt Rj;~qm|i%)J)J)J 'd龹,,+,#x$BϬ6($);9?/Lݍ71#o>${]."E#ŶݞЇpZp:y^eѮV6-թnҞyF YiIAUU,'2VRJˋG *-{ѦUxWn(+,GALvf$L"DԸT_̩Wk5[kKcRaiPjPyo/l3䮋O^)JW8)JP)JP)]ФPX  ڝy:ڸ!oLZK=0a"WU}uҐ: <(*m!wE:ۨ.W]_rti HL{esb8$DFյvVDEE,X *9芀9]"֍LIJ̍F]۹Y]6Brvw,w>V>}8;I{`r'Z]}sssxߌ6e*ET^Y=\ザÚ.(O1vERz"V)N=AԴq̧ad@\e;&]:#✞| 07.cnvg=}5tu|ĸI)f;qH^ꙬV\Kut8C[LH6¦DTBL&>VҎEB4q'yeCL;.] J!u;"c[K1q'|mEAVzZˊIzPȎn $XTT\*-kFF̭I]ܹ4I ;WXRvrwIJ̍F]۹Y]6Brvw,w WAtK7cvʸ8+:s;ƎAmS 6"""'ܕJT%Z Ȥ]'768/u60".{Q6kv1OQl64F76w<\AҔRRRoW-NS NVQG[DN"T)]A9kQЂ:KolۥHQck(80i;šWCuIzJF T6+U9ͱP9UUqUkއlF|xn#[fPgt Rj#}=O0&^g]Rq RR޲g^%x-8σ-4Dn8QRTwTEޏN XE쩬ǝ77 -AxQV.E%b+PPi8ј!ڥ$CVMovKPA.:<4̲ w]Ij @ou2!ymIS"!) c$9]$q:,:NAl ")Ȃyޫ6Vcye\<@ )%ORn!LHMb S"fM;2+\,;A1D |Sr+<9qDv.)7c|8,pp=dWENH77Eqlh]bݎ6[ iսjL->Sĺ[?÷vqlݟݎj:T}SsbtƮ.Xc0&[{7/u8bϨ|>{7Eivn8( S=m-Ǵu7-MsڹݔL%$+2Z\hm"cㅴs=++*ZzTSsEN8l8Bo+ֵ6ճlkvގ&sLg8ehrq3klj8n5 .$}HKۮ-sATIHU}u1kjR]]C S-AjV\ye76ሐ"nLlZ o7{QbIunx)&66FÏJ)h&ܪ4jԾ6%"dGdޟĸL0Ly 4q9DUMn}{5.Hb)Bld^^UP"GpH]zNmg4d+F[7Hwcr"qWY,\pµ)Ai7Ur`Ԑ@-uQGJ <2AM E%L 'uD][;5YNEV9B+rQTm]vTA>=:T5b3g+tל4l)]CEZ@ZoP-[ayq[muȢDBf7yG:*2^@26xCQg$$2V=wh*d10w~ 8I/9م+ {q+mܡftQ[E@,6L+FVٺeG D.r3-sd! 9,:*.0^)JP)JPLYt4`6Im"*ș!<|a=]4^ZيⓆEwh K2F>(LUa9b|*]ď"\bDa1m#pp(UWXOJ]| ToGMf_fv3bٸJh6܍[RY%nFrnQQUSZ<a:\x)2s#xl\q0(.a6 R R R R R R34T )M'ffzUgZ/K?UWƺ|#9_Hk2>^=}#ϧJUG{aUAo-ϒ->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A?$U%4'u̿߯KLd ?Wo5?М'[sieTSgq:pӀZ<ysG-z6jlf.QTS ^\p]lq7[Jqp{mity捧I.aE|cuL2\;gt l|g ۱ǎ ;vYS^>gt Rkߜ[y{a->5{9lvtll49.31[2XRM#ՔH;dXq>M;3Spv0XnJ ֋{e橴GdjR9YVXLy2 6{VdީHjR>XF9\p0UTc- : :u؍npDj(()֪xN4ƊF׉A%Mꂈ%DdO8ޞ^6y8ZYJڰDh$HTbU enޱ˜o Nz=i({[7:W6  gr~Ѡmp] jbۭI ))TTPp*vٷ -'EZ~X!LQD^@]j~v~p͏ݍNa;Qwӷ\=JZ`DoAZn%ȺNa7/ڈ@ 2 9mmw*[-fbG}ojy\p[\+iʸMk|&w0("(""""""62"KB{#.ѧJT3gRwE&y!ddϩ=TVUvjlZrFc`:w8k9 "gvDǏQKq"=o4Ж4#訉vwu,LIw Ff (]"K2ݬƠ`1|v8[TW[AETE!g4;N^tgun?t'eUP_>9|87p=ݷr3=xJz]r]: e7UWU\~6HU6“%:YhTsj50+zkKYKbda3ETEGd#2""!WQKg[d+dIɨ*(ԕn:*\vÑ4%7*QQIrD\Yn&l y7ڏR,+x># 'WS͵UwcbmC컢ʿ"DWŌ Q]#܄M F* 2[ Tcrj+\U*(.|肪,\V(oȵ@\[ij](%pYD\tLPFT7Oìlsy_+[}(;aZc$n>oBa wz`^</Y3vq~wݸV{T(69-6Ɍ. gEl㸧t\e0/bzժ%̄ԳhGc#bR$ʈJ(V:ЄH8Ґ0hUP(AfӚ6>,'.zKJMA:>㔣 s=X-Bi\gE}3U| HDƞT3tfieĎ]ec2L15 .W8RUt4C5A7\`Cwn"B5]%\ &1Ax@% ?Z}82)U~VVa @ 66D7EW<tJџppe v9#hsE2왥>9`kCTr7r"iA| xOϨ;?zP_IX˜K`.K$ ,#ݭp]&ă5MPV[DY cx9qr]ԛLj_.mx|CdN;˅,_εAthvmZI=zŏ,+IW D5BTASũ7op|xc+-8FHEw|‚RA/˟~՟O+Y:SJqJ NMp$U1QhVtw]=hp͸l)qa谓-)7U2jIv!"B:Ln.2DDO؉Ac¸uzg۷nAr9Ӟ&sZc=lsk.'ɠfb c BRA1ov̳6 JWG8*K >;U;&oj̓6mJGK(G+>;.{f 6kdi@iB4enԁ&lWE*BU]Z-A6%ãl+m#|H[d#jJT 7-C]O_g`lM{ŷa!2ED_ZT`:2X%ISoDLe^1p%A{v/**"aW8*aQ+F}'I԰Qt dWVק\mGuf2Ƙe*٨H ruky36WG5qg Նwyv'ZBFoJ a%Ύ-5&W$m|&T\QP#|kx}˫Ke!D10bY,h$ YC1_ o ݼ{7cnqvmL`z0qAȢg6J)"mmٖvcaWjSfͩ(Jư]#NdߘA,BU֟/ {]6 O3y;MW#UTUb^.C.urƀJ?ZNi..7D[h1 6luPUUe~ۄ[NsݧZZ*::b&'QU Kv[(n v$dQTUEQqEJP_˜K|X`˻. " BI9wLdLf#@dkt]%Na[i")x 5[WS_&H.y_* mQcP[k71eUW$UU^?>o/;~W렰mJnY@$,Ms<Ƀ Z |pQxLDs[Rt'SƮƨ!yw@WŅEw8 !S\PmLS*cE.:S\DqWy!$G_p*DW)R~j Z53Ѽ&ìoude;P"YBTvTT enޱ˜o Nz=i+{2WY)ػ.Ah(ʪhN@$ h9٘˜ÃЅwql9Sh! 8ԥ}TsȲ䰘eS!A1&oj̓6mJGK(G+>;.{ez[EjEUJ®ds;zJSZCZHL*9۲}F8kHk#BFh70>=_EHj:":TPP_ y6{`"/WW?$}zkƭbDD% x!t{O=O#}=O0&Ήv]Qyﰺڤ!aGD\?4x꺎oqx\cr=yw s1<9qчD~E&M5qp#)Tv9-)Ju)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J8-~MݴY.R\$7H%}޲"kqadzzK-9=_y",Y`$zdsw3V3s^UrV#b]abjQW7srqUiE1=Nn#Wgh3?pp׌UQW kaW5ه[|ϫmr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"wlaS=هNgնr[?XR)W~/fu:c=}}[x'(ճ"w}laS=هNgշү(ճ"wlaS=هNgշr[?XR)W~'fu:c=}}[x*r[?XR)W~/fu:c[=}}[x'(ճ"w}laS=هNgշr[?XR)W~/fu:c=}}[x*[?XR)W~/fu:c[=}}[x*r[?XR)W~'fu:c=}}[x'(ճ"w}laS5هNgշr[?XR)W~/fu:c=}}[x*[?XR)W~/fu:c=}}[x*r[?XR)W~'fu:c=}}[x'(ճ"wlaS=هNgշү(U"w}laS=هNgշҧ(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"wlaS=هNgշr[?XR)W~'fu:c=}}[x'(ճ"w}laS=هNgշr[?XR)W~'fu:c=}}[x'(ճ/!_HKUU\"'F}"6㜈wsXN>;JM/cmWQQRO՟Q~_nM\1mx'[(*eQpTS̔8}j_zڻf㍴i?k|?j:}XQCݝP"UV#5dOH}6M< )G(g/Z8\OnUG ]|)4asipp-3.7.2/docs/sipp-04.jpg0000664000000000000000000015664214525516253012242 0ustar JFIFHHC  !"$"$CmK" P !"1A#2RU5BQTaqsu78d$34r%C&6DEFS= !1S4AQaq5Rr3"2B#Cb ? OimirTuh $ř(i!J (CcUoRB{\CV鎷 .HJnmZڠXMNKu.a4H[䌔BK ?Qa˙F7*&igu.+L{385PÑ>ܶuXmCjJT.cXWVUn"<"t(49Yk+ROuv oٞ赆vd9D؊ڐCPD@rhE۰69YRl}#p"8XC'Yy⮕EQOc׆7׋Vx8uom[jQ `6?rԪS6}$ &AգƿN*-RSȃNj)Rui.6tEu8kï1c#*huVx8uoᮦ6֯0iiQm6NI#JfJN>BPI {#۰7H1Uў/a҇[RJHA+C!`59.TW08Z&>ë~BLbpӏo8wVx8uo{m[|WDY!Y|rV*+{e쉍qF:鎸`)W NxVq颪#X}Z4ׯF)v11{|4Vx8uo᫖p*ͪoSNZU %j_[JJIJr A:xkDEBq1TpHn)un.16@H[M.IH2yRiTpgӺbq^M"f5:?>?4p]km͍ͬX1uXDBЖJHeOyH'|1vi[kƕKJ\1ȥ$#ʐ.Ҷ ׆;7͞D)\c5^/[iտ%cm:χirvV)sJq+iǟ VGn+^oJ{tL۱4SΨm%!IJ|PV^-xtNq1{.ό4xv¹տ/[kXj6^ܯm̙%66-BL5%Y:qE*VXbdjuusv TӋ2HAZHR-rSbI1vaaT]冚{4Nطp0pO56Ǡ9#")NLL7j1,qyw*UrGJ7Zn:HN3REnA%U-$(لyT :ٯ8.g]4x=_T:qņm5ES8W/[iտ.n6;M;oxSc܇ˋH}&BVPKn{P[Rkǩ!ul5$T flP$ݕ7z퉈p݆1dz֛. 8h9wV[kTafRwnئB7ON+m-f i W2-n7ppSվ\V8vƸnx[i8uo>U_Lԟƞտ0CVUZt-8{[0CVx[kO_Wi/⻌inx[i8uo>U_Lӊ18uo᧌տeU2N+ƞտ0CVUZt-8{[0CVx[kO_Wi/⻌inx[i8uo>U_Lӊ18uo᧌տeU2N+ƞտ0CVUZt-8{[0CV'[zeU:Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1Huo:x/[8t_Ӊ1QKm)$@n>%rNaQIHOo^н;͍\ i&gDiѫqmXd뽤MST?kqR!eB-ɢwRovԫ|#OiiJJ';Q.+Moh!vz~a貫)}זHu:AC$B7an/{eETL18Dztlr&m]/ZZZUVFt~(ZƩ-(1R岬uUcc˳ l‹U֐S- B*< q*YXa'-JIM(Ae|s   |Sd ΂;?:|e|V34j8wv/w 2&QX3LDNֹRӢYi}6qoZBؑqzP\"( \<_$8hUTAEQ#>N:μ]tLb#ES0TxdńEXpqׇ3՘iN=ϯU[7Lae!`*۶T™gW^ٲ6Dh;܍ E7w#掀)¦yY 뭇8{vKZ$ mx$@m|X5_pÎV<@MUȋ+9v\).7pU2{!,;Υ*+N̦xK\^@}B{ |0Bo^zmFrb/Fay5cspo:(*ggSy.f-3$!R*RmËPJARQQJVrJk:45N=]pS8kV=Xuh[?"$2!U+jJRRqasS^}鼵5,BH )d7(WҬq9NqaSS𨈓ꥲ+N. 8]8T;qXeoJd .$~o'H )D%##d\MV7z")VnQ]<j}ϴ~DۭC,Ԏ.ŊW}+q+lz4%V.JyO .8R _8-smkQ8=kYLMF(^եߨZfmj}tTqiD(B66QVI7,$U!uEMby) I ;Ωa侼5EVk__*W6Vp*;]mOO?b9?p5eNYK.O'1?V|@l'5.\1?+hhr~:Zi|Իr+hhr~:|i ʝsR˺O'/[*wK.O'1?V|@l'5.\1?+hhr~:Zi|Իr+hhr~:|i ʝsR˺O'/[*wK.O'1?V|@l'5.\1?+hhEr>:zi|Իr+hEr>:|} ʝsVK|}_#/[*w[._#'^|@l'5nT'+hEr>:zi|ջ}R+hEr>:|} ʝsVK|}_#/[*w[._#'^|@l'5nT'+hEr>:zi|ջ}R+hEr>:|} ʝsVKҷ2TRRC(РmNhIXc3r}-PPOJ?5mӍV1uUGacv11EsDa_i[fQWфOf&g..Ci Nks6+|{5QQ*ܶ⎖O@Te('{ϮnX<*1ڼgq|2FC1;'4 ڔ*Gtǧ.A-*R'}Yoh)`$ӪJCYSl: J G]q_m=7!5ӰC?{QלuEM\S3V:u&uunq{S3uZ0íntm{9ݴg*f͑P%,$[JI6szےwP>cHUۺFƤSݣnDy PRMn갷`Sis{"AB`6Ԡ*u2nocb{_$27Oühsy_KKf`IVl&߲NMNH:7 ϒKl \fQ`w療UۺFMLFSݣnDy PRMn갷dE=0HP (> tL؞}59>LW8Row G'%w쓤1cxi$|EaL W[=59tX89z9en;pgmWz1ңxi8܌, }iOK=59Lso.'9٘y-g O+vqhs@B 8Ԣ)t[-qr;!]ejum%.Ŏz52T>&CJClaC+ƔY8z8MY:ԉ5$NNKRqAA*RД h`{ampVCU8/@AR _ DPr*IIl9VAɪaJzsNN2ܚ}ҷ,KD(ZmȒT/~dzu~,$bCkjVvCCϲ\*=H 7FaUVݠ3j3y:sK%(K+?AW ȔFKٔZ>KT,I/EZm(:V(Wu\(HWիq"S]ukOԈZ]ukRBvҥp@Ǿv*F 5f n7)ʗ#y*P$  ΚXܫ0ӌNJ\B:E4REnS!RhH ,(2#wY򸆖%%iNHQվ&NWwT TD4YR]-8Tl¢PJX{vSuU1$H~:mn {Jia! -$ ՀR⣱aSvjCrFi1SPp0 MCQJm6JkomɃ\5Qꂛ4[-Uم$%V .$砨i\ffNns=7Yg{C¨u{G NzX^{f^6].U5d6aQ~qqT-e $S lu`9P4Ak̴2J) H8"{MmHDd-Y-9(m& %wқ/]ol\zeGQ4@XC%$E{\>[a-6u2ð,̅\Yɥ& Rĝ;.ʴ@i%SPnSћiԶTrlح%5}JE&=RE_s:)Ң6Dh(_fTu<ʛ){rNARP'Dךʑ^vmFS)Vt*H<NJWh(Zj6,%EWF;ʀ̥eO0ӎ f*]Qia)mruQ 5[bR[joVṘdImETԶ]EBd ,Y)M:NՍAR=M~)4xHKnF*-/K HA6= KVărS6coʖ3JTd)4H'UL}MN,N;bS"+oN!7rCl)B]],v 6KpϟWEx9%4] Py`qk"i4{Ta9H 0jP|Z麙 77۱=R)"蛦VZYԤ"yYBmݍ޶(#KUK&vj*%QtdTi,RҟA_&D[m?3?Y/3a3c{NwE&kp(adyR=_?ïN}e[<+^cTHλٷkXaDlh#>N:GOtZ3^m䮋O^4CNjVSk\BQE26sh 9h4!VQzk\Z+$O 8qgH9~CkJtC91dXmG erIo)H@ ҼFn*f۵ *\<) em%#0P7 {WjREJ m@5AM2ZzK QR\ b!KFX B+[^VWcʡj Ɇdr>TsQU9#[\fRꫭ;ZR*ȕ$V"2R/2q t%LnEPѫmW*1wH.xD6S.΀Sa^ogNSύP:Nx\$.[ T^ (NHG_!4MM44MM44MMYj;O?UMt/gUi)ucjy)գCK7vJ]Z UE«'$=D΄ثFj{5HjPYӭQyC8t!A8'8!5'\W*uN zk{+eDnl魚ghQ 3K&\5B+&TpwJǐe}/h;n;F#UڞzV&tC җ]_" 7bU(BZBVY=T㠶p^T+?>H;Ln>A8bEWNC؟[1CJX1 =P6LGZb+ )xMqUӉ:k, :r)՚j)_sh*KB=֕%8)TiۧsSeKNUxr&[Mq J77?FNj,؍*#[Li3\첡ؓ{/7uZI8j(iI[M;BTA,olۛ< 0*1Ph둙[\wqMR%'bqTU٣=Ef9d 8H|Gͭ;sJ ,Wz7&lS8b,-jմ<Ȟo T)D1Pm yX9%(ɸhAsûs;G7]q-!F[bSbAHխV*IZΩHBiv\I D\outNU6Ue2EB V5n\Z!k%)lcb,խ]^V/GU~Q?ҟvPRSxX/RJJ/bEU蒕.UMI eeRJH6ߨk, _$U`W*q*rĵrPRX7U7=Ⱦ;Hu%ĩ.:Yjl1e:p9?9r}O7{7\1d2i0@9y1 ҧWr!Obӹ6#{L$e{,v$b+\T N%>N\X2I VHʺ@˰,S&56oB$  ek{m{E9 SʩN+&tS`(k+q6ZăPA'pWʜJ|-heܒ t p-4I7~ʝS.Zc6awI|\z\JQAMŊcEm4N @BlMԑ꤃6ʟ@N+qJqT8H4򔠧[! 2{ccsdmU$vj &1qxpSYsIKw5{Vʙ)ݖ6$tJe^!$Bb-L:-M҃H I#ݐXю&6cpi͒:JC K#e SxeMAf7<-L(ɧ|3Ih,v# ƫ0eũAZJ)>` )7IX9>Hw*ӍN-FT `Ep7U;ހ߳wΗCũrϺF}R:JI7?@+]fw*"QO[}ɰPڔeH+.&B\R qu;pAܕ'SKN:u Y˒[)E)H$\*ihij]VisC 7Nn-A)$rGrm]N{>vR{1!#>`Jl&_Pz .ڬ"M>3K@Yi\V'R&j&jFhnKi}|C%\m8JJF *[Znً@څ^U䘔ҔƂ-bTR R\J §NL $u?:JS+Uǘg9;j*hGKj}l[!\mY"RBNh9(h50j,XL̚h("ˈNƌK eKH@p/ {ZȝcԨj[z SvJa1|4ҤKy)(PRR  _L%Mtm"B۟4ACKRVRRA>AΝLc6Ӑ%]%hBJ PId/kIw fSLjFsVZ~ĖBnA/ I씤~HתmӷNƗXcƟJel:fN HA펂"6ڬɣQ1[A_q_7W"N+ҒF >[nD'QchehZ$:3iJXRcd($VnKk-BNiRHQ${m(b˝Ю<.Ĩ1)mV Io9f]PȑLͰ_}R zkZ]j:PcΧ@HL HS~Td ogV%*<%.8ϡEn8R u) w veԑ0vKm8^RadW*o@@NTksPG"Jr$K@_E% 4KwU*v)ԥGShq>KAqqHE@Wq <SќSJ[KRZu. lqRIJ }AԜmYF5f4cR%hn!EWu%#܌l*=}txBؤw%8Vx~) §NL $u?:JS+Uǘg7QyR\d.}Idҡp'_Py=!BL,0+):TTZBS{*iiiiiiiiiiiiiig7Tv ?ٿJlUg*_IZG9n򿤭ySa#^=~~'ߺ{I'W_hP+Pyo/l3䮋O^Nm< tzFL*|GٌLXu.E8\7$mQTjJ)RLJ̅)AH! 9 _F]4ȦAr,QXCM- 8-'3pHI[s|Ԩ:-Q_dFyE ,6ؤ-ͮq ciۉHb0+ +v*hC3XHP?i P%|gO:Jm!wZe n5~j^MmDviryY[FA9A)e?̺qP J 7$01dIT[Bx)EVCɥYHns1#ON2fM[n>VZ8e++6@&>z->$tDfJQq[yd4,T!qvQ!pgR_q67:ش -ySWd_ae)BmyrlZ").MMe7"rMԵ)k$n(BB@uӷnM2 D襹eAmdq R}{dOrzNe-e->G3m0mAĜUe)=#Tfj OB~)-O *`Āx8R;%2|mEHOG%HYh-cJlESiTmmv";HJ_L{sN!Z1¬f|$mvŲȆՒ,$$o}Kv&d0 s6O;$_ͪhihiOjRwth|?f'6,#,n/kfWM&xte'׵cmFh,;ZMŔua< (H;!¤T8]!%{06y4hήu958P xaIpY!了Gj.j@UNܤU#QU%rP@% c89kdn&F70`C^zÜ@Xls=HR!lqQTST1BXpჹ)Գt86tm._QS~6 ޮqt )♻&I9P`CY&z#ҁYl(JՖ߻p({v!٨)R:P)pX $Zy8&^<*.Ci$I.K)MGiNƕ㚂l.% qbHfD9TqSD0[S\7m_R?)X:REӄ!ƜmJi)U ) RII  ؍{\ "5/ŧ`]SKZT% ,oUvE\_D409|-n7RTl= }50F96ÈY*%N{(m"\=.[Ȑ㮺(nIQ)nR*Bz89-rBAmO U{e/n M&Ib0 0けKoH*: HBӐ%ANSw#ZǥSñQX]J,]ҷХ Q2M#l˭@nD.C u2*Qp$*mem[0hҨy2|QZCu(AR o̴)JZs% msnftS)Q܈%a-W+uJmhά9:t FqL2qidJ% EG_U.1֊VZJ`0&!)B$0H*8S]"Y5OzuRKZT1)̰JZB'ʼ+v!4zBkTJcMa gTC-@\O_2JFbf9)S&-ULZ!ҤĨ<9ظSdn. `Huq Q !z3mS6y4hήu958P xaIpY!了G*UeԢFP?ιfpe B1$q=25)r(NFd+B9 ,CfQvjYMCN˜U hp*IX^(J, " 2M*)3>6 `'BTxniiiiiiiiiiiiiiyMf^Gk_m?3?VεP^W1$~^YʷWy_VƩ0ïAiG}xffEQT#S[O&J\Shm W{[Or/asUۺF8LFSݣnDy PRMn갷dE=0HP (> tL؞ׇA9>LW8Row G'%w쓤1cxi$|EaL W[A9tX89z9en;pgmWz1ңxi8܌, }iOKA9Lso.'9٘y-g O+vqhs@B 8Ԣ)t[-qr;MĚE=0HP (> tL؞>LW8Row G'%w쓨=4(tƧEC}3q0r%]l;tX89z9en;pgmW=4tgJCa03 r28T19=.;Lso.'9٘y-g O+v=4hs@B 8Ԣ)t[-qr;&Oj'7i$!FmJ]7S!{v6'NO#t;Ƈ7ffn ]$"LjtX47>I-07qSG/AUþ]6N٢NCt$JSH?IYJÊ!)`=Q׼hl4r\fanFB >4}DD"$e}Z\i֖R֓pEŠCI9i<Y'wH8Ԋ{c9H !jjQ|ɺY-V۸bM"Not$H(BڔVnCwMlOkCFwo+i| )7̈́#I :cSFIm˜ 9z :gt,h|g 36 :cG^qq X*ҞCI9i<Y'wH Ԋ{c9H !jjQ|ɺY-V۸HF4 Us=h''鑺~CZ_3 Ma7HtC5:,to?$٘)*aPzh'</Y3,n {㍿*C;:Tu 1[%)qPzh' PzmCG;32EpaVIb55"st#Z_%7nKwU.G{C領HF4 Us='鑺~CZ_3 Ma7HurEѼho|[f`n>"0^}</Y3,n {㍿*r=Q׼hl4r\fanFB >4} PzmCG;32EpaVIb5b5"st#Z_%7nKwU.G{K6"?J]q1ڃdƐ<R6qjQ,b¡!Q[Tv֧Y+PHR$~aCFwo+i| )7̈́#IEѼho|[f`n>"0^}A頜:gt,h|g 36=Q׼hl4r\fanFB >4}A頜CI9i<Y'wH8Ԋ{c9H !jjQ|ɺY-V۸bM"Not$H(BڔVnCwMlOkCFwo+i| )7̈́#I :cSFIm˜ 9z :gt,h|g 36 :cG^qq X*ҞCI9i<Y'wH Ԋ{c9H !jjQ|ɺY-V۸HF4 Us=h''鑺~CZ_3 Ma7HtC5:,to?$٘)*aPzh'</Y3,n {㍿*C;:Tu 1[%)qPzh' PzmCG;32EpaVIb55"st#Z_%7nKwU.G{C領HF4 Us='鑺~CZ_3 Ma7Hu~ TSfX3rې+Czs4߭ͭC{vW3Of*ճTu%kj ?ק>W1$~^=~~'ߺ{I'WoiRuIv +WWM.s4  T 1R-h+TrQՈ68$lЫ2Tk9蝹="]TF.8 Í0lqsămF׫Ņ!I2HD_$>XZB%,Y2,'[ۆ*LL*.I!ė&Q@$pI.䄐+B+Aѷm3yǘZ}*l"Rݐ%rB-e CH{QFt-Te0DևSq!E( BPW&A1ʢFKa1gxZJԅAESq 5|SSyg5C*UHH`u!&JVĤ0;zh;tn:2CSw ȘUFb@!d+ʼd$HEofI[J*$;`NR][W E*9HR.K0K݁ -q4HHjk;QLXDRJ+Si*t>T+5$)k:iiiii{(vF:ڤUDk\ PJE\ܛkWA=EbLӨ^HjcW!(u/2TUljr"{5r[4XEeِ$ 9)) sXJo頼ntX[*&gM9k<2u’#)M%°;h?Q~N7&徥)_k%$kRk š§ @{9͵qBBnPL7vqʋÛxhnrTJCn rڔ8 أCR,zXal#M52}BIHHpZVw+#PNGjHQ$8 GMBQ!$Pn! $VyTBJ#zfڊj%3NfckEUqe&-#6Eh:QlN7lEZ+tYnRomh,T(S3mf!ffbTxPD9 T $XQO4Aj(Ii +y i֒kN91ꕑQIH]44MM44MM45fe/u/Ag[N@-ڴINd<6ԱkX:r,rl%l]CZKJ5QֹY$$1ō-s*SfƤo8AuF (HSV)( ~ |9e~.,2㕻^ݵ5#>N:ߜ[y{a%tZ|jJr>驱:TEeaȗ(pq6[xntjp5e* ͣ{vBlR~̶s{eBd.3 "J y +:N%(mY$cE6KUd"GY;k/9ͬB !JH:ʚxKCW+^9[HSQ]"\[iX`ښ)oG]WUHCRVS. RVeIP˱hZ5Tɦ>LS4CՐʹOqcʚxKCW+^9[Pzh'#MDZvVr majh׀H C_P_=Rp8'd&5b6GKq PHr͎WEwBER\HLp -DT?d<×.rrnur~ӾCԛ+i O$n\Zv.|in !cih%՘8Pzh-S{HNZ!FEKj(@BR{8,d#MDZvVr majh׀ZR㻦CFk19Ϗacq{^47MNQ>z7Z9pOnM=kmPF4h+roW);X ImM57bHUL!.BCWQZKm6y4hήu958P xaIpY!了G(’zBc!m"&as{sMNoO 6nX43O~U}6Dj+>4%MmEMwKS)n p:ҥɗ.% 'rr7MMf- ;>4CՐʹOqcʚxKCW+^9[Yjb-2uU NB5EY|ЖvYX'ܩQRD0^DRm҇.Q01SQ]"\[iX`ښ)oG8b>C\'6 G I`ATujĖQ.'KϹհ dҜBShVvZUEcoN+pz Fm) +XpvOIhB=[HTrDUe^<˗lrb,4(’zBc!m"&as{sÑVNV=9զ,eL@.Ca M +)l\-?*fÐsqVe@D!Ŷ6GR8:.purF驿:,šbƖpڲ9\Y,tSSOh|>r\Xe+vjM}SbtC.Ñ.P᭴,0mM#tu1hkSzUG#LFUcIn=!Q)V{._㵈ԕKw3?lR[&Q7Fa˴J!JWNM+k@s͓ a†W~{Zݱ߰l59?4Z:w8(pٺ{`=WIS~tYCύ-ᡵd,sm-XGoV,E5:H@[ Dmsс9P%k]Hy^,Hd?IqiWͭ8]iW2!%818|8V{v>驱:TEeaȗ(pq6[xnujƩ3ڑR=mB! ql8n1F-G.6WɅO12S#ȧ˗9\@@58]G Zp޲k=!6HqB=[HTrDUe^<˗lrb,5\¡l21YeI)ISq{^7?=K|c~^8ߵA'qT$Q%BۤECMl9~+&Y'ޟ- ;}8lIhfЫKjǁi]I S8RoRKť/؏2Mv޶K|1| ˺Ty=ܲ;8ѷFH57E0\VB6K0q=Ŏ*j~)]-ÎY_ xn׷m)qtiR7òd=$4*}x)-X!V)DftE7pKT#'`r3Ɉ_&2>驱:TEeaȗ(pq6[xntjp5e* ͣ{vBl[[k|MΛRl嗜3>i8K˲رpRQ+,%;4IA[k)%$mqh6n*z1 A[~ʽyM/qXi'qT$Q%BۤECMl9~+&Ph''ޟ- ;}8lIhfЫF驿:,šbƖpڲ9\Y,usMOJfn=. Ch.>-RLIͶ{`,EiŨm- V! [Cm):m-<ҶGlZv=:.֪ WcM u,$-KY浔lDa*5u*@s!fVmJ4$%rP$gUJAG=om2Rae} XJ,||LD- Á6tjLO;0]nFEq"wJr4'ūiיid6Sq˹ETϨU4*3_Ch(̱ {ޫ{ALnx~G KaF)UV)-o5jS:Dv [vl(yj}]$P5p*C2yICkJ˥AnRHUeRSYSƣUJc>L8.fPjp2VBw kDRڦL5>nڜSW+"A {NvMɦA",C-5ܡJOls oӅUڶRzx f&2$pcRJKf7lljU$KLB[ m(ҵ}SmvqGq fQ)nR*Bz89-rBAmO U{e/nڜ{MOFkm磱ޯ5BTTR"dqUx}R\4wI6>Ft*Kg#lMaY)6m1 vHͲ7WU 44MM44MM4IJnMsN5㕍k^ڌ*oReR3NmԥB. 2ZR3i> iJ4lZ~ly{$=25)r(NFd+B9 ,kh6Ie1cXϒKjp##qRF>}X&ﺻқ4|n&SZ.ceE-.)H8(A$7F0Wӱ֙*IA46W+R /H۔ YTJiI3j,PCsYmIV! SpoAJd:l'i)ˎ/R%@X'bI8UNNyd!OBOpq-+CdAP964jk\1 dQJ],FuDqRXlM6"še-zT):c)@cBżZ$9ZBrZPD %MBL•п̖Z-1q.yR2Y7VJdÕQ#MT\3K8VVIGRT8AYWw\trljeꮮ-ee-$< DF M@S;dҢE*U}tQ%LS[OT[..ߙeIBUS P.&cb9 UC"$RpF;(P̬d—> ?)ЇJʓ )$'{ql\ngix*B.{etWL,:ũխj *7V VӵԊZõRЇLBCqZiJ Y)IB*iiiiijNMWk@cM/;̆6n [f&¤եȪNa2>fj"-{ \]6>]*pC9V!AI6 bb-ü$"iQz9˽CaS);iJ+WQ頜S2 L%1p`XL -dd?8r76 'X5K* d +m".8лbs7efEQIƚ,Tj,t]xel)j@];_4Tc2k?!.ģyqTJ6R UZvAV%VֵO,B u˭#AmW|/fNsc߽n-ŔgҖԅK$TR$Pu}hol#*.2zsq5e' 8wN/b ڸVW'iR]#KkKBIˌyl% Z.qɕ1W2PQysd^kRR~uf8k;:GHɨDÞxAxuѓ9gmFܧbʧSHMDAQerKnJ VVWL=jdHRҡ6]S imnZХBJl-%OޯBܴtB)M6̎.CE-^uyȵ@4KyԖinn2V HX`흥Pb&#"s> !deX;)MؐУΕ-C vn"-H]͔R"I7"\=.[Ȑ㮺(nIվ)nTۉ,yqۏ4hui$Td)iբ5Ip8^-ۺnln.tihihi~SiٟץQW3Of*ճTu%kj :U5Ozh;O?{LJo~'T_7m.L!,Z;qqΫh'DW\n"sZrrɂ`c*1$o&tY7Mqprc1eࢫ!zZ*+8 9sbZ()Y,_[=VNOx<07._K8 ߾^#))r(u"+ !AE\zqϩiKqˏ& EstNh'1XiUxC1#lZHS`Hn 7 ;\rwIUF FU۩d9TmldxV=9;\wU>+h(u1}*ӹ$ѓk(:HU:*N}KNV[\y0X,{W>LtJxe}!X8$؂ ux}P1SOjlee&t>̷8 A>Obs+htȡ:f4Jl;tzjr) M`lRVJ?Tkie4bԚE]ʞ CuFY'Ua㓽t߰$*(w]`BܚENakͭZbR%F:dxHE, zͭ26ʨԊu< *l`*ǽ'k|m/Ow62qQeA&WY5YT䅩[fNWgtfk/VUvrzb&q*DyS؟Y$J*qu'>+ -.<,F=ҫ]:✞\ x9\82%<9k^޶[E}?AC1Sͮ%6T{YG >DW\n"sZrrɂ`c*1AΫh'1XiUxC1c#lZHS`Hn 7 ;\rw}51&FU5WnQV- XqV|zN|FN*=T [ES=DԴaǓ"ǹUqcU|S5˗魎=A#))r(u"+ !+ -.<,F=ҫ]WJT-Vu6B[S$- I) ~(+;9SG}};zkg>)uǃrt{ݤy[E3.EEa4a8݇5.縲Sa]b 9H,9=&BȔE(u_*ўU2BÏCDX̤>T#G [EG_C?SإW;C~86hũ4<ꍡ|O`õ'{`zWz)\f,^dA+>^$JVt-0$$Y)[RdmQQvx U[b U{Nn'+ q_J4mw)d9N"$%3.#ǐN$.ժtSީKv|Jt!5S1Z -.8JvFHU:*N}KNV[\y0X,{W>u[G9<x\ܹ}.N/~{ۧsSeKNUxr&[Mq J77?P+htȡ:f4Jl;t+hu}JcX5񕒇{b=51FM&WrQ0o,Xvc7#lHSr+{rvw}59>V_OPbT=askIM'Qt+htUǡ4`X7J; }tzjs>)uǃrt{ݤy[E3.EEa4a8݇5.縲Sa렃S%mu}OLpVkb^2Pu AXH)*TX6W1$~sG}CN:ߜ[y{a%tZ|jJcmmq} &EA)Jէ ED_.ml.,8(\A ⤒A  eF\些9“Ä*RF8/նwFQܬ@~k>դAs ~5qK'2r_7d8[at^45TC-=L8lRAJN_ <4Tkl6I1nF<﯉`9wr%p6Q(BmԬYI HA>wiTufRr1E 2 ;ָȸ#&,DyNT"lc.\pq7l  ~Dle"s)C#%]jIAN!a%Y5A\YPdS `I[nYu(l,!$$z?* SQuV^..)%))Oq8hvR_HTYS)7TaOYMo8 JrhZe-i]؅cq>ɐPiУvTOR7Az.H%:'VL8SS[;));Tn(S0v\vKN|VJSt*)אhu?VfLu^k'uWr,uvWQV^ʮ;]ؽmAHYC:<88WHAR|Gp&~LDOҪT z=1腇}+~cis .mjxQcM>J1W6T0CKd2AG0PG]7"=2\iER%βvRL[3w3Rm6!FZn[kXi.9x #!7snftS)Q܈%a-W+uJmE.Bk.@* J@JRH JR $ 6&v.4(% s|wPEm~D)P*=MC8۠:ӈSn6@R/pA]]RԡvKS$:!ܾWBԏ` Տj'JѦUc&0+JC%(O}zOu!ylm:AmJI}^UEtb-Ai[% VӨv XK !$IojU*T4*&LĭkQ!‡^m$5ܜp 7oA&bShnŸ.)8̛qbIUaVv3B>uH[S` CjZsE7ّ Ήt*A-!a<:aŭeœ( J.INKv&d0 s6O;$_͠VS;1EN!P-6Y q+% BZRf~Uժ^Q0dPiSٳ2HF;bm$p͒,Ut{R㻦CFk19Ϗacq{^5'*i5æ/<98V6{j3@M4 4@Ԟڧĩ\,%vKhɐPJ[i)DH6%XQA)LPb*(P2)SKBH&Dh'(EZVj2!cS`bOy(90V..T%:evCIlIJp-JB2(J{E[i9ZV%1 06xRp{]>,HGT(JexK/62 qrZ5tģ0ibJq ie%֗|,(BTStBQEIqQ )]*8)$bd{nĠ?V)$5QdTI-Jqʵ('))n^s1JEvC.c /He B҅'.1mT%j(J꤉tu9Hi-9)ŋظAJ2m#Ahihihih;g6zUݯ5?)loҨ[:A{YʷWy_VƩ0ïN}e[<+^cTHנCJٟ. cU0KaYHwZZ2in$I4)Lʻ$&PۀBuKJNa.(d kz$KgSd-;BYA SzlYq)YG,F+e(X}sh ,l䪼Z*/!LyIJUr%!E(3bvS.Tx̓ |H)eնٺB$jukvhQYn!aǡB,cRğ*{#k{ S\xxǏ =AwE+kˇ H. j:Rة-$,zkƁ_-Mk!LZ*)Kμ(*S`+ cn[WGBÉj\@XEH:"DILˈ2ӭ,m&I# 44;Նr}TSa*KB[9ڀ%jKg$=~jIKRq%  ;s3Yz⫷S5Tr#ʞHNީ ™!-R{5!%E)'+ @d96X2TM;"%9Qd-l4PI Kʾ@ [ڜwu?7Mr'R?Pu8+̣auڃY -2ˮMlB1xӉB^(4Q`]*H'NWf[4GΨ՗KxiMhPZpt-8s+(+>9S:K?wy={\W*uN zk{+eDQذܑ73G $-jRX}ҒR@VV[BJUv'*+Tp^<@T٪;Ln>A8bEWNCFgE#t>*3^ *rGclljU$KLB[ m(ҵ}SmvqGq Il F;A]ytd8ieu\+@![<ri7~TU\ĨI˞SևAJ`WP"N;*\vÑ5|bkܜAFQ}5\=.[Ȑ㮺(nI-M4N{>v.{\&U#0yJT."PzڤϗJD@w\7bPRM ؁؋kW@M4 4@M4 4@M4 4@M4 4@M4 4@M4 NMI!KTîd|ͶE Z. AiܥK[_iGVokM=MWM44Mk7Fb*}rT.Hq.-EJY-BK~K=KڵM1. SbӥJ(JC ,JRewֳ$<-": lk$6+2RDMM44MM44MM44M` ?ٿJg6zUݫgZ/k?V+Jט??xuϵu%kj?נCJ`K&䨫jATZeub ͼud+o>IdFD-FHUʔА 8_dS)=1VޕZnTMy ZYim . bp谶- T*M Λ sye$GKHRJ aHA)KJ>n+}0E2U5ʣl욛1%eniq.*O]*EԖeւu94yJȬJBŢ>C-D)@!zzh&7tdF(j RR*KϦ!µYk;\Ŕ.s4  T 1R-h+TrQՈ68)zh:Ƥm*QzD ȞRi.)AEAƦ즳NzH!v&3"fC|$1)Xꅦ\5^h)~L1</K!±uI@Mr*M4 4@M4 4@M4oeڎ7g[TH sKasqj H ;mjihi%fWbXj_rTU Qf*PyjJ2D~+y$Rid.i4K(6maaoKjMM44MM44MM44MM45fe/u/Ag[N@-ڴINd<6ԱkX:r,rlihihihihihi~SiٟץQW3Of*ճTu%kj?ק>W1$~sG}CC\'6 G ml}yX(f$%xgr\Xe+vit؝*j"˲K8km8 SE-7:OI[Hj BqFa9N6$G %I-ohȋ!)L5"Sb&:i PQU!@ Spš}O )VNm۲dpqT#э%G(Z ܤE[Ul~+"[-mf{}NE %,-q%)JI ;^(’zBc!m"&as{sMNoO 6nX43O~U}轧\E ،̰dHCKR|*.7e؅cm*d#4 mpCo% *e*ȱo*$n\Zv.|in !cih%՘8O5?.,ņWrk۶Z1OS%/erR孝t5j?Q^͆k(^׵ʹ1MMҦ- +DCӈ4R ss Spš}O )VNm۲dpA1qT#э%G(Z ܤE[Ul~+"I;").G&8B"*moKa9\7:A9?thhKfMC4]'7MMf- ;>4CՐʹOqc=4Sw'qωL-E}#Q"е !)KX2QMMҦ- +DCӈ4R ssmJI#5gȰ{\_Q  C_P_=Rp8'd&56GKq PHr͎WELIU aIr=!1Pk|[_ɹI7CGN_G7Ro`t*>urF驿:,šbƖpڲ9\Y,tSSOh|>r\Xe+vjR{jTDIr-$C&C)A)m{"ؕb}SbtC.Ñ.P᭴,0mM#tu1hkSzUG#(EZVj2!cS`bOy(90V..T%:e4CՐʹOqc=4*j~)]-ÎY_ xn׷m#MDZvVr majh׀Pzh' n.- }C}|8oYJshݐ${8ۊi-Ǥ*9BV"*߲Se69\vA1'qT$Q%BۤECMl9~+&Y'ޟ- ;}8lIhfЫ W[CjXZ sf'MO4CՐʹOqc=4*j~)]-ÎY_ xn׷m#MDZvVr majh׀Pzh' n.- }C}|8oYJshݐ${8ۊi-Ǥ*9BV"*߲Se69\v*m^,ˬ 搷L>FB@'%)8*ȡ+9mj3ӝZbȖT6ZiJ0U3PF#! n6% {ܛdjsz~h4tpPu& {B[KQEIqQ )]*8)$bd{nĠ?V)$5QdTI-Jqʵ('))n W[CjXZ sf'MO4CՐʹOqc=4*j~)]-ÎY_ xn׷m#MDZvVr majh׀Pzh' n.- }C}|8oYJshݐ${8ۊi-Ǥ*9BV"*߲Se69\vA1'qT$Q%BۤECMl9~+&Y'ޟ- ;}8lIhfЫ'& MճLkǣG{zp9z ^k\шyyMf^Gj֪ U5Ozsg*_IZG4z={HOtN?=ꓨ<ŷWEƯt(5ȲK۴$-4<@X)f)65/GZTW*s.#% %) 7LUUn) eʓbeʉ dJ[erJ)&W [RAKjuRyIK{ԁĂFjM3)P\Rik[xŨ*SR4%1aS p5ٴ%k*it>YbKOf<܋h8*Oh68mJX# &FtWBKm9<7r{5ksŋH,يDA lƀݯd;kcT[3ӮYBLZ /IJ\ I ٚJ}.IȪ7Jz:1}j)!\s*I SiC$y.HLf"Q-zJq :lhJuv-1;*.[_ mO)VR֧ VtNLj!-hu}-W 9@kmDzȝ1ؐ)zN2y  (/u& 9D4JNK]c nj:ȡAi%%.$Rct%i RWI;zCMS!N6꒜d ad^Y5HT()M:X^RTOd{ċg)ʄПJ ^eYTNXXRA.1T jTFD;9 4jy>616w \^mXw7@DECH2 ZlkF( 'RSBAfz*COLS ,6hi-4wʆ BlRG-h ܕ5)i%-NLBJp *'hih$WM&xte'׵cmFjs]s2ȧ6RpA4MM451*S$\UQu8ddR +ZMˆ> ]w'46!dŴꩋ\:T8; ~pb*j1T`$ڜLӢjJUv4ؐۏ)R"BԿZHkYJ&hj\z&Qb-ǁQ:AYCn) ; >iiiiiiijNMWk@cM/;̆6n [f&¤եȪNa2>fj"-{ \]M44ME)HeI\, e‚FX#lHSr+{rvw>V_OPbT=askIM'QtzjrDW\n"sZrrɂ`c*1Ӫ>)uǃrt{ݠSmΔ:Luv|cMc%mu}OLpVkb^2Pu AA#H)*TX6]*pC9V!AI6 bb-]^=T6oj;zoSڛYI4x]-O,؜Ơ))r(u"+ !AE\zqϩiKqˏ& EstNh'+ -.<,F=ҫ]궏rxs1񆹹r\5Ƕ8^iVLKC9Xt7Xi7af1 ,v9z [EG_C?SإW;C~86hũ4<ꍡ|O`õ'{`SdmQQvx U[b U{Nn'+ q_J4mw)d9NMNHU:*N}KNV[\y0X,{W>u[G9<x\ܹ}.N/~{zjr<җ"\r"nnϚbsY)r) M`lRVJ?Th nRإĎ#m+7M&ަѤmыRiw*x, Cf"UkN=~ӕiee41MMM!eaN)W! bP2R[E}?AC1Sͮ%6T{YG >DW\n"sZrrɂ`c*1AΫh'1XiUxC1c#lZHS`Hn 7 ;\rw}oTkzX*5Y#GF,(dX[~mmIUF FU۩d9TmldxV=9;\wU+h(u1}*ӹ$ѓk(:=59"VT諏C79-9Xin9qb0n\vm:c sr:kclpAgJ\qȊæiHq>k1]qdñH:\'Ԧ8+ 5]JY(w QSmыRiw*x, Cf"UkN=~26ʨԊu< *l`*ǽ'kpSm/Ow62qQeIE\zqϩiKqˏ& EstA:✞\ x 98[Růq`}ȱ˷&ψ¤ǪN}>Vj"Pok =E"$y3#ȐM4lԼ\ftXaϪV|NŚyNdk-䡂HRWM&xte'׵cmYanJ%u.9KSPb2h84,]i̜|ȅVIGh8یu#$8R.RM2U2&2gҥ_:#MBT@MnAM.\D\FqTK'r{;rH ʍ-2bFDlb]+1@[oPnd<8m-⒧]H-)@"Ro}$^ƝD'pČ$-岾T@2qTη?o?aI1'ɷv,YFgc4#T%>k-D6e!'4XukmVhS& 4nKn;y(QS.yUp%^EyUiʖ?F"qi/G[( 07ΥвtnC8ysR*k* ʑ^-iyoKn&_rT)D JQ HI|xpFσ:z^,3?&Y?';;EV]RY&+5%=LPp RP8  ]wJ]prco_O=AuǧJX¢쩬ǜd/8ڳH*F*9&"\y%=[ǐۭ:,AnUSOwx%1*'& J͖|ku|wtի?z_y,/kFiiiiiyMf^Gk_m?3?VεP^W1d~ӟk?V+Jט?2?xAiG}x=V?=ꕨ<ŷWEƯt6\ڽ^&>#&,DyNT"lc.\pq2'T%M4jSHqF\O`N(-(SQZST@&=,]&%TPR&C|Ӆ֜pp#ZϗJD@w\7bPRM ؁؋jp -Ȅie:^e2PisRŀqUCKm)Rg"-V*CzzڄB㭨9]pc8[z].m^Q c"d<]KGN61ŗ.r8 3*ɢS ̟RFAS-w.sou]4ȦAr,QXCM- 8-'3pHI`T=BEm73.JiMEũH-IJl"*н~%n~ z+e6qk߶]NN BT)-+1’ԔKmV`, ,_Gz̜q<1{*ݴ Nڊ "ON$QЧLxy0˛l<׿Q5z}2 $) iP.4NuNYhR!y %6a'<3HU¿;^T_>\8cw0AͰmǵ NMI!KTîd|ͶE Z. +"R5^}8J>VQŔ; n’@Rr|)lRG}oqĶaoSuR|&w, J@)JR JRRNoxtۺ;lF|T.)h^eaqL]l!jE$=@&ߜz^T+?/ћL~.$ B1J ZZ2H)rvDuՕ-ŨܩD$rN좃MT*). x :PmD% hӞtTDکIa|A交)+i@>eIS~W*p ĵu%%.bPR?0}㻣N>>3.fKK,ub; M*Eڛ'71֜f}Bߝ 95{|{&^gY[n~UjMR309=B5E[1[]=B^;kK^ns!5-pŭ!U*;Dݻd)X"k_(+;9SG}};zipWIXʜJ1-hyܔ uMr/ʠmir=Z.**ؐ`FmAZn@7$Xᓔ}I+xr׊_0+U?3Atlvi[I=yZウRY $Z,:lAIRcZqKP.: _̐2q?GZޞuNҜIUw%"`S\QrPDiɠE9E92[kyA$ )(T+Q#ĔS_G[ȹJ`7 ?DQ;*$̑ |~k-I%&IѪT'g9>:Lnۑ.`I6fMQ2ǎ=cr{-~فCI9i<Y'wH Ԋ{c9H !jjQ|ɺY-V۸HF4 Us=h4jDiHFbwPq:n-۬bruBժf͉Nq `o+(mE( Q*ooR["vmB.)xܪS7/CДtTl%DP,wwWʚt/;ŎSPo5ɺlHMX&]j:! r$$mrMYNm.ٕ*5VDZhLH mBA$Oca);bN_N=K T? tH@.'XRW YX%*]:!h-ؒ mp PXOȥ@TSkC.䐕dln;mESi9WM]f&3$Z|4m8 Cx [UU/^$c Kɵrs^Ԩ-_[m$%*\i:A_/ʟ~կY۾?KAaӶ 8ݜg$\27P W ( W.$4modQKPT|e82t+˘IY(I;w3aDsqUrJDe,6/d[ݥGtj;2D%Du8prA$&ރh1nXF:L.\I-`R\(, ez+<-9a{Z튵X&]j:! r$$mrMY֎r'48\Os0$Z'dpV*#XR)ь7M"<@qES{&duX[nw> 4{Ta9H 0jP|Z麙 77۱=R)"蛦VZYԤ"yYBmݍ޶Aؿ5?hi$DiIaǮ,JC?+-[C|kGd(61ML6P>M=ڑk^tD w*wAA&`$~kBmaIxHHDa*$~]Bt{QG}ԝRuvNۢ&]n%䣕h̀nsiS>,~6=bxsǫڋ՞Lj*N8GSێr]4ZNM4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M4 4@M42q-~M RTl!s2"!!Qc^[涇kѤTUQ)y!AIJ 齮/q;4bo7`bLGid`8ad=N0ï6jxHS3cTC`,Y'+ |Sa#]@OiMtaucv5ZTgjX&)`y[ZWS7srqUiE1=Nn#Wgh3?pp׌UQ̊K~uq>Ʀ}? m>n]5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5wL~uq~Ǧ}? 9V7I5laHW~/ϻ'WzgݓәmtV5w}L~uq~Ǧ}? 9V7JV5wL~uq~Ǧ}? 9V7I5laHW~'ϻ'Wzgݓәmt5laHW~/ϻ'WjgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~/ϻ'WzgݓәmtlaHW~/ϻ'Wjgݓәmt5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ʀ}? 9V7I5laHW~/ϻ'WzgݓәmtlaHW~/ϻ'Wzgݓәmt5laHW~'ϻ'WzgݓәmtV5wL~uq~Ǧ}? 9V7JV5w}L~uq>Ǧ}? 9V7JV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5wL~uq~Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtV5w}L~uq>Ǧ}? 9V7I5laHW~'ϻ'WzgݓәmtVIG/V*I&}!m\usX}LvZ=6IIPWA>_E5S06s7uejXIꮁ>7ٽΌ9s[RyjN^ܮǤd\8OLF48Dp'vN]*ǯ ~8osg[SVmiK$,ٷ[fAgjn0R [JBH;-˰[$;xvxQmmIN2F)Rkb.\l"qׇ_n^8c0NGUT0r?g[SVƚ4dTxn{} ӒdңaF[[I*OEwdzv=CZ3#>l3ZZPj@)I;΀kNr\aTpL}3V:lx4pg[SV~:MOҮgj%9)Vzi'%X_'L)ah Y(Tbi>#S29.v |+0 l!:K2ʨ᧹xpׯޣ8~ߺ/fGaKN옭:ڑ!rB^[$<1-Ò Q[P-8dLk4:)W!\%;+Z8tUQL>tGkף ;y=}\wVpu\vfrj^-(3Rݏ+@a)'6 A5sֈbWPHn)un.18@H[N}$ ̙%6֡[ĸ뎶QJhV%#[X,6ˤU2ΜYG FRIk ,44٢tŽe--cMqf?8G+~{C"-NLL7nYy8JQŹG+Ai9.Z5ܬQ[` jU-nv?`5)JߏsCCrn iIi HpP$e*n8 c<1Hͭ6\-3prg[Wpuuk;LN>,_b=in5GT?:8 O WhGGiw(ZmviBm!)m!@040U%i '\ u|_N*,g=gC3j}Ҫb4LcFn~5m7oqEI:-lV)!;UNA`eu֚aEVH)l\!GjNҮn݀Q;NKRԨDޢW<) =.l9GPG5κ3$_/4DUƍcNݭŒm.5L"|:p.V6^ZCNerx9t$du\;Gҏ.KlTj.v[q3%N4s+v>'5IʑUWD"4U1 uGKLXMTU g=x~ߪѭ=y!ƚJN1}>V-oX2ۤG-^ HJЅrN*QuXS1Skӛ6TWæҨ{aH|\"`/xMl=(yPKھsa4BJPW)$zWϔyV8MF||Wz0Ն{B;Z]er"2˅%*#p9ufaw.d_(;L3򸼀Bz dg]hܾrλzh<25,ۖ[/}Q% P75#%ӗ8qzpOjmk 2^-jlպ˒im-S Cq;^+U65ݨtOy,&jі.ե߸Zfm}v\qiD(B88WjY.H3>BSҚ{i8+Ҭ;Ωa侼5EVk__<!k{~mќ-)S%C'׋QnRlSɜKHH SR1t)Y#<#Qns"gf V٠߷"iGʾSj)P*JךڷR9nuqWÅ9n(dWd p qVq1uN&,(0Eڈ¦iAK؂*P2HڷR9nuqWÅ9n(dW9k8psjkҬcݑ->|Svr"S¶ 9P=@ն?b9>Tџvx>*W6Vp*;]mO lXr}©Y> ^TԻr lXr}[c'J|@l'5.\1?}>R/[*wK.O'O?b9>T ʝsR˺-cmOU)|Իr lXr}[c'J|@l'5.\1?}>R/[*wK.O'O?b9>T ʝsR˺-cmOU)|Իr lXr}[c'J|@l'5.\1?}>>{ҟ/[*wK._#OO"9t ʝsVK|.}auG])|ջ}R XEr=]d/J|@l'5nT'}>Y>{ҟ/[*w[._#OO"9t ʝsVK|.}auG])|ջ}R XEr=]d/J|@l'5nT'}>Y>{ҟ/[*w[._#OO"9t ʝsVKڵݲtrrC(Рq :En=}C-H@Tu('J?kVzN5X=UŎoYQ{J6d2=ܛpjKy]R ;5QQ.[cQGKi'J R=g_7RNxU?cxόdLcwG Ol (VϭWH)e@S9I$\/i ouIm'HT5('J?kſ֐/GMsm}z:}; p4:GyW\^35c^bgW[v37[zahգ :$ w9d8?[ml,袭3fzmsoo\]QgCG{^&ݤ|ϧMRkߜ[y{a->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A~}Ijdgѿ꯷ځjtz̋%_Y ZSU?YsF>M'fzUY/3a3czn٦hMkj+jQ' #z|s5k?UW^c\pͼ]m*&gDw~߮>gd&5u}=OPUso/l6WEƯt+H(((((((((((((((((((((((((((((((((;gi?37ҨW3e4TU[:Av*_iU5 h^*_iU5 hksG}CנCgd&5u}=OPUAo-ϒ->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A>?SIٟ^GWq)l߯J֪ V+J18HCz絟V+J18HC^=mH̋HF4&J\Shm W\cOR3Uxscc38wn87\ۏJ#}=ORjPyo/l3䮋O^NG*:%fn;X*}zSё֐,v=Xq>̑8+ >"\A1ovg9m-BJ/9K%|u#msz"AB`6Ԡ*S2rsCzfv߇X򾖗“')&,vƧEcc}ImFF@ؕuu:PNx;/|ûv8v:n~VzR=Q׬ll49.30#pP;ҞJ ;dͬlpSHfaHNPVtXZ-ٌ5M<@qES')d`cώ$u=(&$-YMD(mAU*d7ώI;dnìlsy_KKf`I\VXNuPtc5:,tk?Kl 270wzī<ፏpݻx߻sn?+=*vgJcca6qqN Aޔdu Ox68\Os0$c'c O+juH:PLF[ݳjDy PR8NRo*ǟFH넛E0ީHP (> L7w6930p.+{ G'rIcXi[f`n>ё)6%]}8jlN8fpݎV#Fc\Ibi`-p߆B[冔wЧ JTچ3V c j "spSևܠnX9VT9=HeMd]EwZˊj$AYp;`gMYe[g_%]9Q|9 gtK%%!E(ѽZeo*KKxn2+lrU ꬃ fwfz$,02|G䏢M*,u`BܚF6a;p6 t(-Qê**si$n%k%^T~Ac޸KrN3)q F\BwH147 ?h)UK޴ݕ|I"wYLQW_M'jM:,TyKzkCHHJ ˆ@S \ffNn^3vq~uݸ:PNG*:%fn;X*}zSё֐,v=Xq>̑8+ >"A1ovg9m-BJ/9K%|u#msz"AB`6Ԡ*S2rsCzfETT.RK1`Ǖ'1=)Aм*Fo>ܹû&v7zjc¸wAl\;xZsс1wWjUj9Ę7Y2`NgY mJA=:'F.t&tqTeJ$拵&l؞6sj/5n"JC6:-N%עEg)ʄПJ ^Ӭ*lvH;wu=H[)RY AmNĐVPH%$qv$`9)2ր7ԡHI.cy-XNv$ r0b<.KVӑ҆lXY+r5mqzeQ}F=.TEYN=4zI1 РB3f:L' ,mFisYc'+ImM8B1A.`,dmmc$PqW#f'kFW{tRʇSY"DOK$<8뮬n-G%J'$h$-YMD(mAU*d7ώhjEjXIf#R:=8`M'fzU^j?~*gZ.Z<*ƹ#ӞZ<*ƹ#sG}C'5I<ŷWEƯt7coUxpB-kHCη NvhZ92]I"AKmRPW+H#o&{w딄 6e[TII8fsHbKHeȩ[fCN7y8H6ꕸ$ ݫ5D Drd4h4< #PJ(:ӠnBؗdq1MaCp%lvLdyRP u=Ndϕ.Eܕ4-rR)6}IJ7yھ.eWUIih -6KJd Wd6erK6eOLv/O4gż\NIEoChj{[lpHҗ9dJ<RRS$jO!봏=IM]HO}Ԛv +WJR$ R RLuJe2ui֔gJIHQ G*U[\뛨V)nHLt4VI[$'q # ˻^$jܸ/>{aRҜBShV-[U7]e\DȰvC>)j+Idf2|9TDm.wnp{~G˒SiQqjR mny))ZTz摿ip$\u,6Oͷ"-J2G'#%=g*^XA-SDeƎ6h"?yyzx)@ܐ w,W.|ۊvʗHin%jkYq }ӓ`M'fzU^j?~*gZ.Z<*ƹ# zsU~W?$}AiG}x]|ϧMRjG{&^g]Rq RVծK07"E5Ťgti*q- ]3@FEjXLvJeҠ9)V8 PYn麖zrh@iCA,RtȔ+ȴ P8ư䉦i\aJ_K-wvLmSn%#jR圃nV%Ym@5&\Cq( GJV8BP# rSFAҚ"t"#k:f2۫CjuAJiI[quQq!UrtnEˤiOi\I[lZVa Ut^n9vRP9#)Y\;ۯi 6Um.:FFj/nX"ܠV8Z`F .ҔR$V>tf ͫcɭHSrov8HSpBRm33'7E5t)qM-]q=HM[ZU|!ˤ llR2Qڽd}sΖ9qx DU^E2͆cǔ28 >+-ѭ7; mY;OV]u #냐,:sFok,>qv"Jxp7/Nۃ$B[zS7qjRYhNBS`zI?I5ՙ2&:m,:;y~1)9 s ޜ+nSY73qI#`[0RAESxt<-#!Y)@)@ -L]%'N9hyV\PKny^mzn W,{8NT&x"PZfISe;`RAۻFY2M*)3>6 `'BTxTrs{Ls,ֈ2g:M\cϑ%/TqM 09{nk3vk7"aݓ<:}$wMz^6*#*>mӗ cGj8oRųIT7^ ո&Y{--9nbljn:X# ZגlcvѰ`TNttm{ xCq=@+6ZZը!ZZ6\7#q2C(Vn<͌%8䜝8DF)!=8۫vCqHJ2PoEdK0rU řpckL!#-JR ;K}r=%Ĵ-A''jRR>[2桕m"ǗjK OPH}IZw$:$-!KMV%%K/ # o'buNNFM}ssmێi}sw:ܓ!\&Iw}7H`ǶA@n,B@[Nk 1h0*&]ij! 8R{ii@(G/aJ BJRJRJRJRJRJRJRJRJRJRJRJRJR}?~*5~?SIٟ^GUTk?UW^c\p9gUsҫk>נCgd&5v#}=ORjPyo/l3䮋O^)JW8)JP+jpj\0e7:FA Id?a5[g=ydMJ}ɈZ|\RR1u:iSz{Q]ܹAm}Ty?MjƼ]M5s72f7!i|yܭrs'鮍tqQ;Omc7 x~>5ـB$%{YQa/ )I$(;V˴a* el[R}F-Bkmi/ZFK(p(j #| ;wg3Yݟ~Q״.eЍQ*M\Lf[8#-AO52p) )V7r sez$adGy m֝<8 `5|_.wN =Zݍwg zIiisPn Bm P]J"eN'3Zh;o[ihbRT:e<҆HܢGiB1( +jָ mwHdj4ajBNFGL[GDS$Č;Vs~HqbUոڤ l(1Th1bDcZ3\BZHZ#ܯ-PEC68- ǔyTyq_zGY9tcDs}K.G"B.0ۅ\=.[Ȑ㮺(rIU[ ))- -:H8;TRH>j¹zhc@.NulS. % ZuqҔRTmSٍ6l([fs\K n㑎?MCkݏ#4ix6̤<ʜ 9hcn卸 PU^]޳3ez9du ,6̔2GwEu2<$qi q #m@$%#Uj%6c/yD^0Cc+*9 [eKx^..[ƚฆ#4"$;>-Ú^{ܤ˽]g\!.BX@$N2I5Ǩ/.o\|yɷ;wm#8}5a=`15v*Rݐ.(H`2!o?hӷH6GKr\G*JVC%JShasS)i2B[[!%E)'vHj zzLxsr۱ɈQ_m"uy•()D9ۇ%nξ>÷qO>z7'_w>;*yvٜnv3R|_[[?vgg'޽{WMQ R R R^.Yu:ˌ\YJ ]@xܢĉqΙ846BF@*T[4)0n2ڂ%W*V9Th7;$ȱ\lUݗDkRXsd|#d9wLuBK-HH*$$~Z5л`6wFUtgc d ꒭ RO=RRRRRRRRRRRRRq)l߯J_goץQUlUU~W?$}N{Yj򿴪󄏴59>!봏=IM]HO}Ԛv +WJR$ R ʔfV҄]Kh Q HI I5ht&4CGcGK'(`doFWF4e$̒RQJAS2HV;̨IzT2O#Jw!9 Q 8!?Ͼ6{SňgʊvS%ļVA=Ԡ)t=Qyo.ӱwwԄ;ZDJ00Gk]NiLLA?"M8LDOZZ!(Qt[N敕maǔARSDz$d2LG]v8ZKuTTHN>:D$4ȭp;qc #.4t <”!^)@)@g*- LE>uHR yVi"jm[hp.S26Q! G!|+ ga^9Vlnr[ah} %mmޕ%$oA€P# 涝W|iK*$k. ( IH INdSt˝[uJzYC–#Ha<ld 'VhyZߪwWž.dܢu!C!?|\l*ijK*)4'pܒS0ʰ;-+ӥlj*LI֥PPK $0w*VZ4ʻS"Ca%B@!HBT*WT}5nsw&\(<y6q87<-N! <+]%%}K'+szVNN*Oo{o?lI6gᅮ?^RRR1f}uH[Slqlr!nF;ӜL`w}VvmJiӅTJT1ȼ"h]R!ӑZ@ei[jR#.) + ʔR5>ӗ$"%]5,e7 &kp,]BbٸU51ۃoEԖTSiJN$9I$aC1amGvpM[6K2TwJUW(((((((((((((;gi?37ҨW3e4TU[:Av*_iU5 hkӞZ<*ƹ# zh;O?{LJ#}=ORjWn>gd&58>JsLuJe2ui֔gJIHQ GVծK07"E5Ťgti*q- ]3@FE6ՎsuxbkqBXt񕎅X򱃏C Hb5Pv S$nsvĥ;JK{͏9\=痗ݻF.j;rbuVŮOm*JJ܀ ?ztNFߺmIgŮZx)r j"QFXiCR#jPRUt2,qbFTw&DERcnK)8[D$^s1jEvC.c /He B҅'w6*JeJ%Kݽ7[̗).kr4rV#Ca <蝡,-=p.2d[)Ci=P!.8V(6Е!KOzj+,|-n>Iqr RINF3=1@)@)@)@)@)@)@)@)@)@)@)@)@)A>?SIٟ^GWq)l߯J֪ V+J18HC^*_iU5 hksG}C'5I<ŷWEƯt+H((((((tӚ6xYd@ypSŰㄇxޒp"SuN$H:<5ȭ35!()H$1M=Yzz=9 .=[!jK)S;|y!*s%"U^"DOK$<8뮬n-G%J'$kZi[MԸuo\UR<Ǖ@)J)JXbEg)ʄПJ ^Ӭ*lvH;wu=H꘍u4[1Wvm lnk`yܝr1'頒7f0Wӱ֙*IA46WjԽ%SKO5K}pj۫i";0!N9ȥ$:8ABP^R6M*,u`BܚF6a;p6 t+VxܢĉqΙ846BF@(&5+6ZZը!ZZ6\7#q2C(Vn<͌%8䜝8DF)!=8۫vCqHJ2TwLuBK-HH*$$~Ylzb;Ϸ[A!i$!iܐp-5[^t lww,p6-9895.sS)i2B[[!%E)'vHj zzkd 2|CowOߕɿ;t۳Z/»ו\gd&58>JF`[^onEqY0܉yAiǛ%d ZF[*F[mJ[!]59Up}Vʌt-eR%)$%\ҕ$.lӻ?q-n%Ze]QB|ۃ X KB)N' ^P:8tSڦ2Q*r/7McZq+L: -^68Bڷ# RBN`IKPBv0f;;o;Yqԥ4€ma*%*UZZE݆n5!O2"CIK -[h)R@Ql1|#.[#x"pBbX\[|K!N m)Leqpjc-ދ-,ҔrINrH†c((((((eDLĈò$?SIٟ^GWq)l߯J֪ V+J18Cz絟V+J18HC^=~HO}Ԛ۴=IMAo-ϒ->5{)\@)@+,DTS-ZVl-iFz=zh1RN8u^ιuo RDICEiAXw9^0 |KVˉRw!)%?oյXx;ezkfUNL wha8kPc▢Fc'|>_.KN<"v PDx̹)6 ’рG^iERθ.l{J˭"(Bԣ!Dyr2S^rѵk>$FXQK\hj6#2 PP}zrk cfBR am}\gֽi*_XmQpar^ )q m=J \筳yڙ1#n%IWZ|mZϕ1(.; Ky/cj6Ōz EVou \36#4^' H$`5%bj䔱m e4`XB_JTڢ'Pd]c6= w`` Ǝ6VVK☃t~E3pqZ5BP$(l!H\e7*46rjRUzR.6hxPZzVlo#U-czT@fZcSɈ Pin#= I }59'TșVc.2peƑNpRSt].)jӨCZJMd@+vBVٺeϮ7 .r3,!sja+er°pJypF骭L3h~LD".1`l(uV7g SOPr婭٧:ȜnwD;K "?vԺT!m8>c%']#_~F*DKPˌ#8%Bc`;N&>%j1W6T0CKd2AGxR^O'V }xE.lX YnSq)-*Yq̹)(V6$$"5 ĕ&ZˌRC(( vەNv?-ZE^])Ȏۑ![HCKN!mc _^ze*j<W{EL|ތ ]i-)~D۵wdN=;:jIhp(!;8JRReI/oڮu˅6>]qN15'CV!gݥw뤹>wIDn`oW^(NPT]ʚ yjސT; j"[ ))- -:H8;TRH>h7v#o6y>8?/x_oͻnn߷nzg=+VJRJRJRRzRZ|5|;rq/ng*3AgE9zsb+2{Hqm!Gԥh(N҇FN6riÓ\gۊQ t?(%XlN$2&\dGZA@m.80;uFܪVmQGỉA[ȝjT4[l)HlH9@)@)@)@)@)@)@)@)@)@)@)@)@)A>?SIٟ^GWq)l߯J֪ V+J18HC^*_iU5 hksG}C'5I<ŷWEƯt+H(k9ȑoqiJKk#mhWL碇P3Z#fS/9(miRt!JU`?A[znޜ.ZnPdpK*tq&8RVڒImiZT VKOK}j"C.7ƤyIZV>P7r ״W *f;{ ":RRTldn՚n 4Hpm俰 A녴- ڍ׫mٱڊ̭!PER@zoby#5NZ,nPoKB[-0cr V ڗZiJ Y)IBS Hxzȱ֤)vq$)Pʉ8JG)6"LрOPDg&*>LH]YߌU) (^pm^2[ >9IgKwuN8TI~*`֯Yؙfh}vK1x ^BHmzNs֛|I,H'.ju9m5HǟxkPw%<[ X^8Hy' m-=)盎d8),TP'!)J=$frj6\U͇<N) Fޙ-e)L :pORҔRUj.N<+KKqry˨<e6=7ba \*B|[F(-{N$0 ) #njVF2Smryë %<ea6vf뻻қ4|n&SZ.ceE-.)H;P 9I / ١(?ej%4uCP@*u-'ڵ/hI~ Rj|ȎL:Sr)IP'+W!C_M,X1mP#-[rSZCmn6 J:N"\;MspS9y;Cn }sf+KZ+TkSfˆx:He֕j֭ǝ@'WH݅52bunn:Bռ _q@ 轣̉}]J1. mi$e)QP9$4gq4oTV4[@$J@JGz}F[2QDX-Iai I! NT)iݢcDamWNɠw|/|n~~^mqǍ6vcjd>-+~wnf1wh(((&$JڇLv1t3n.-X$i);roieL_iy$J)/v˖+/KԈc()FP䵸 v-d- olmkE#;iѭW e<7* 8-!HHܦ^1"Ń2u G+n:$6`zvȉMN;*KPA\v7%Y% lC]`M'fzU^j?~*gZ.Z<*ƹ#ӞZ<*ƹ# zh;O?{LJ#}=ORjWn>gd&58>Jsw ϵΓ[Y~3mdpFA#Z`fsטJj$ԯܘʵ%(#G\c(7_7ݻ~nqSzgQ]۹@m鈚} U,ySПE_&ǐٵ5CiObT;F}̂:ʰx׭|TKܯN6#D;,$ ʄT@IܭJn]eķ]gC5rbBCVC PϤ5]x خmrĄDDJiʊL&H +#qFh,2ε)Zv0RU8 ֍P_>9|87pk{v7mN3zp+-'U϶MA5 -Bu(\BRU;4`dߔykݣ[oM5JXfRJ#rl(TZ79#Igˊq 93n5Ox3C dhZ e! QWVj:`%Sʼnŏ jr+Lqaj R8I!kgrXC֙X෫o |*kPSϥgtMnGΖ, h6<HSnHsr$Ko"CSʖrTzI&VYl.,8(\A RIJA A 飣/t91!On 0+ikpJV9BJRJRSNf6XڊͰmmq,/;;F:5W\Iv?tnҮdž:2*p((塍6(A_u40WwlCrk`!ڝ01[r%:dxHFڀI JF=諣ӞQn7Ҙ[ . j h" \wKۤmXbqV o?}XJG2-m)[)*1بm;Hr{w딄 6e[TII8fw˝o7sy6ng8ϣ&:\/J[" P^B0#vS v"R&rnT˘RSJdX mv Ӯue9-:PCKkt\ޤ$$DVWT/Un[v91 +^:/4XRe#rWȗ5pݸ9ݹ8v8ۏF61x 'uW.ݼ3znqҵjOKsw+gnׯj2JRJRJRzxܢĉqΙ846BF@+Ym}RAR (zOwȓU-/-Lax̰"+Xt'qPZڊeX7rގWX0 >i%,2 K) j Õޯ{ܤ˽]g\!.BX@$N2I5WM.s CWkr-T(/!k L)NM.JRJRJRJRJRJRJRJRJRJRJRJRJR}?~*5~?SIٟ^GUTk?UW^c\p9gUsҫk2>נCgd&5v#}=ORjPyo/l3䮋O^)JW8)JP+,F*S1SI[J]u-p7)D%#$=$*ڵELy/8Bz|!ޚ6c4 q9Q[RiJTIP$}'|zte*.WeMe$z:gw-A #6;eRӧ c)V,35|?.{W 18cr.+m*RAR#fNFn:Tږ6!G[+y|p)eK(vHS/+SSm=6YmIeE8ܞFJsF3VGpxezt%I2":Է iqݻ06SFtWq r#Hl!S)[X²מJJoڮu˅6>]qN15'CV!gݥw뤹>wIDn`oW^եI-/s>Fl}q+Q R R R SQZST@'=p hզ|U%2ᾇshVA6x-poQZRqk;:_M,X1mP#-[rSZCmn6 JRRRRRRRRRRRRRq'`u9cXPy3ZC|m+sjR2JGZavt/?Usҫk2>נ:ŷ-flc k>6MҜq"/6;hoZFfd]5E52RF=ChZz x;/|ûv8v:n~VzTi3{T3^m䮋O^NG*:%fn;X*}zSё֐,v=Xq>̑8+ >"\A1ovg9m-BJ/9K%|u#msz"AB`6Ԡ*S2rsCzf j]:lapl͢Ix)b6^ⲥ,$$yzxN4Ej#jCl67+*=~>em\vh-'RJ^&;l+7{?ݽ s==#Q.(ouW6&bdy^ wu9}uAaV)ŵ0ۭv$҂DH(qx u6h賕&yJ02>GHiY*_*\ß.y;$h;!:vr0zcHZ ɰ"77D5r.umDR3@0@kӖk7y*9YF܌)~wr’@Rr|-lZbG}oh[Jq} rOsٮ!!)H%)J@)J@JR@62"K {#.8̵dzj-1)uc4[ZIA$H>޹_ /7f)C{HFԡ m.Dr-dHyjq]YRZJORI9$[(o oKbD6θ%ae$QpH³@}E'wm|t8'a@3fFjĄi'wӏׄqneH+m'RIZj1olؖ mGSj.@ZP딤zzhzB+lؙM;ir{ۇbвP IHD&KXRwjUjO\n#MoDLn.ʼ2se/zS*<@0NqF>;1; G+JՃIOC ZVk^w m;\ )N4.F ]ڽκYKj A9]szwz͕-\f,2PN|t$fo^::[dw\mM^)Cm2RIBkx}˫ER= IIx-%JxT@Z**Ꭾ S||A-xnݽ6X:z6BS5UR?$"mmٖ1D ! 7CJq8 )Z۹pkKkѨ.Ѯl̑8+ >"ƴ[ݳjDy PR8NRo*ǟFHzPLI[ڳ DQRNTo)ϟ o/D6԰F%i'qe Lt9:zqJ3ZC|ZIRXqN_|3ZC|\h=ڜ6HGr|c5y~םzJ;䠠SiL-? WW?$}a5j KǢBGU 󄏴?!t{O=OG{&Ήv]QyﰻfR^J9V,<( @}?Ni? ws]sv--G}Myw s1<9qчD~E&M5qp#)Tv9-)Ju)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J8]&mA%G:+""7i)?|%3ZC|שH;mj=R"Bz)23q;4bkjĘ(!,Cvp=&# }Wn5W368IiDMa~{*}ĜUeoLjgSZ25F{jŮWV3FuܣVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmt5laH]_̟u;_̟u9V7I5laH]O̟u;_̟u9V7JVU߽SSmtVU߽SSmtVU߽SSmtlaH]_̟u;O̟u9V7JVU߽SSmtVU߽SSmtVU߽SSmtlaH]_̟u;_̟u9V7JVU߽SSmtVU߽SSmtlaH]O̟u;O̟u9V7JVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVU߽SSmtVIG/V*I'}!m\wsX};D-Ml`~ fɲܚ٘b9I'[);VH8>EJ=V.9DI[Wl֛q?gحoׇz][g6,(!nl8!GN$?$}Yh0 KRSO Jp*jTTpX:U˜Fsipp-3.7.2/docs/sipp-06.jpg0000664000000000000000000012333414525516253012234 0ustar JFIFHHC  !"$"$CmK" R  !"1AU#25QRqs78Tau$3Bd4r%E6CDFGSb= !1S4AQaq5Rr3"2B#Cb ?nz"[?!m Sy).aG*=H-(i!J qPǐ$dʇ5[:;Jl2\PH!)7r &LB$nR] . ($dmJ(`yB7n9igun+L{&͹묺l8RR+w~8~ߺ][Hȅ"2 5ejXIꮁ>}No3{-s?V"涤4(]O1rʓgtM?1ӄo:λt*"5λ?eoNg[U,lfߩnW)N>ai2Ն`$;H;[[`ϸIpv-wh<$ڢa ڒ2F)Rkb.\l"qׇ_n^8c0NGUT0r>g[SVƚ4dTxn{} Ӓdңa#q-Ĥ'HT=;]ăq!@ -(u ^ g@59.TW08Z&>ë~BLbpӏo8w{+~էiWtNos+=Td4/#qXn>keO DfK݅f7])u5F\U|<41Uz;~g[SV٬ݜh5ݒ )wZݓQ"ޛR$.HKdC~!ܒF>Ŵ>rV*+z쉍pMt\E0J+2ShB3MUѧDƽz0NC3{?eoNg[UJ8nͮo3nZW ťj_[h %$Ҿ"lQ7=/ hN;f*{5ϸVKD$!g.M1: ѫ};'j"&c^ӯpu;?eo]Ki,ƍx1UX!kqKP%n2H X$t'e6b%[JsY*,bԹI_2c%ø$#wZҶ ׆;7͞D)\c5]{Yt6hzmޙ'KoB'4N~VGNRA*kK{vLӱ4ST6 ZH>2 (dzN+]/U:'Li={lmcgUQvNjp+ފg{\h *U$,ӑ iMc*HWQ'=GN,#@u4H[ZRID*W7"IRu}z?~wh &5-޴Iuw'!=<օ)ljSM83:#LL^]j']"j 1t8>}l -NVR抒xIQҥ\ & P{KORT)?E]ܣEi-mg_P첯-}זHu |!` Pe鲢ʊq&q?:?˶qt96---* f#D:x]oi|oV\vTrVm’[OT,;uQu* eQڡrS`TNӒgT*#%7m! A'͐':fFs+戊ѣTuû{Q2Ʃb"tO^HvÌO@###VkhQr-ʟmEes;TMX Q9)ϕr>߄&5yR9J*F4a`wI L'ӹU54g18IB[=On_,jQ#/%AhBVR9JIϋZ:)ux͛*+iTOvý0YQcwR>l .j0^uOl=(yPKھsa4BJPW)$zWϔyV8MF||Wz0Ն{B烂vr"e K>lFveYܹ%R23/>a=|J3.n_9 g]{K$6_ #8k' GX;:#p;1i! KqHK-A)KiEG*Q*%U)^xMxcwFƩǫNr-jۧ}QG9`S.%)RY)JAَV0Ǘqv%o楈I pBC}V8MF?ާ 8wv[[ꥲ/N. 8]ß(T:XekKd .$~o3(n8HkRF{QW ?~7( VLxtg~DۭC,ԎY.ڱ*Moixd[٫u%<[ Cq;^+U65ݨtOy,&jі.ե߸Zfm}v\qiD(B88WjY.H3!wEMm ^#j@N01ഫ(質jF3y/"MqUZ}+e)l>$2[tg JT P[m[9ĥ2g"RTgp VH9.aۯ{m٫sնh6/DuE}𸬫6A}9~mqn#\G N¸);w!@ tϢ+$ͬScڳ*4um1aD*,F7sL%j ^mIR$G\տ_Emzs#a\ q:g\ͯh)zͫ9+JvF`MËLwT HR ڳ$@Vc Vh1RٲÇiTl~dOU+' ʝ򳚗]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6c > ^T]m1?}>6=a > ^T]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]u}>7Y=a > ^To]ծˤKKDȈiE`T+q/ o9-1I @G:mӍV1uUGacv11EsDa^Ҵ͙,LnM5%ռTz9'kTTDK;QI @G_7RNxU?cxόdLcwG Ol (VϭWH)e@S9I$\^MOԐjPO Y+-洇y=_jkkmC6k˰C?P =|59J0֚:ftӌLnfoU<,1# a[WnӲ!VڭT͛"ǖKHXI+ !)qI8s_{jKYݖj;Eⅷ(u5r-EU#u[BUm4h~HO~ ̚۴=ɪMqo/l7WEƯt+H(((((((((((((((((((((((((((((((((;A m$g Tp3W@D=qfE֒˯-)U~|?3437Ҩay #X,η6t5t[BmͶ\ Q_m~.RA8Ip#+۟!N{Yj򿤪?6miVt~7>.#v>'5IhC{Rߜ[y{a->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A>fzU^j 9M'?jYj򿤪?N{Yj򿤪59>!=©Uu}=O0UAo-ϒ->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A>fzU^j 9M'?jYj򿤪5k?UW^c\4z=v>'5IhC{R;~qm|i%)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J3437ҨWgi?goץQlUU~Ǖ%W?$~!zsU~Ǖ%W?$~!AiG}x]} MRjG{d&^g]Rq RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR?SI?~*5~fzUVεP]JWy_UysBGק=JWy_UysBGנC߄&5v#}=O2jPyo/l3䮋O^)JW8)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPv)ٿ^GW?SI?~*gZ.%Z<*ƹ!# zsU~Ǖ%W?$~!AiG}x]} MRjG{d&^g]Rq RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR?SI?~*5~fzUVεP]JWy_UysBG絟V+J1HHC^=~HO~̚۴=ɪMAo-ϒ->5{)\@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A>fzU^j 9M'?jYj򿤪5k?UW^c\4z=v>'5I\#I4ˊ1 93\洏zq; k󸜶6fsw^g]҃NGS:RXDVY !<{c NCΐ%isԦ8MVc+r\m:Tie6bԛE˞Ł!h`,i :dq:Nz$*Z. ][c0#!Siҧ]N#'jL(HU:*Ne/ -2<#)VGA΂9iwxq9lmnE#))r,w"+g,wx=1^OQAJ+Hw>14ƺmJ[:Ch?dV84lũ6=CwFY,`tuzTęeV`kEݻ!ˣk`cqzq:duV:+H,w6?Ǽ4Jq;TzaGpdAҧ$J*qw'2ӗ FS#NkH];cnޛvg=wz( SiΔ;iHq/'S ZE?c?)ᦶ5jUܡ*A"1FM&wr`Hn 9xEN=S24ʬ֋w=CFX `tuzTZE|!c1=wrSڣ ;&%iN;qiKq̏ň#Um󠃥NsZG8||5N[v۳9HgJ\Ȋ48zba)wyAҧ J(9ϩLp/ 5RVP421jMbѴ0sX28pz=$%3.#ǐN$2!j~LZ} +!Ml4HLwIްl9+jr-ٻlM=ov%$ݏ-AvžP- gI ڴ HL[N-%4BԌ Q $y>A9"VT諏c7;4lX0zY:sZG8||5N[v۳9AJ+HtȱMCx!y=Fw J(9ϩLp/ 5RVPt4lũ6=CwFY,`tuIUf F]۹@26 7`+GUcCҦ/R4˱R-@X*\rQ r|N W^uWf>'͸rϞ0`)'rK*!9܃=Ş:T-IMlya-#'.%40A' ]C%_rSbKyU\m: n|^uDmroIiVƮ10:J'pۜb״Sjq1^ލ *R񸖶E%fzU^j 9M'?jYj򿤪5k?UW^c\4z=ͯX\:f3ji+u.!%R+H@d>].= o(C pA+9pYFxzsHQXxB68Vѷvǹ|w>M/ȅ$k*& MpTTB֬ߜ[y{a%tZ|jK&~Qigo^ה@ _[ҭ3٠mJrm,zT !@iu\ib;+ - ( ky`mZ}W桗m-$\-e&Cm.oDeM%I%iQ)7$0DLĈò$E*l^@*V:nV/ru\3YvR ~<4`]Xԥ-6;;θWDw.nc!2SE6 ^10ʠQ!M!v{dֆ%ݤޓ el'SO-$%N-m PY+R!' ? (ҕ)I.JJTIQ9$PAҤx+>x?Fcڢ~X6y¾>u@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@um.%:yi mmX )C>gP)JP)JP)JP)JP)JPv)ٿ^GW?SI?~*gZ.%Z<*ƹ!# zsU~Ǖ%W?$~!AiG}xQt(댇\i%z⒔zHHʼnwc rTl2ZB-i蟭I KT]5>:Lm4 Ω’A|o&j Sov6S~p%(<ŷWEƯtn@A[X1%62J# ]`k -x=ˣvTWoIZ'SԔjkWlLa>%wj sџ!Fb ;H]VjR%M!*RHjw\b2}1 ,:GB˨VFҕ$ INT'be<smeI ^ !JH5'v׳n=Ӷg)h}XeADHu†0P@PF;j+̋Z.!o,o$6$mR#)R &FԘQ\l0)e{PҔ@C!.6dgLFrx}pb,O:IxmG[iKi@ڠ(( Xfv^'Gõios'k>k䝝HJb>.BTWSPUG%.]^@VFX0czj&nRYԥ,.:Te`,f.n,D\bPn+nAe@odZX[$K [-% H^ RB] @)cf.˜n,K\n( IyDw =L=hep"-qmJaQRB I,zzXJ]n%*u;K%%Hj}EA[SA9t"헑9ۨխR]埔RC!.%\E)8eA!901γms;w7oS5V53S+{s !Yp-,(YlZ-Z鯮W;ӗ#JiB eL}@+q:8+FR:`(4N&eqyva8QZ䀢H xN1G7Z/7 ;!:}8Ciu)nP݄u)Z'\$riSm*l{K IY;q[iEwr[:xaAH m*ܢVzG4mE/7l<''w7w86²69hWavv3B>: -^68Bm[!'z0N#z{zQzy,-/sfR?.!vrW'r r~V2iJNh8!@q^*hնER"`:P;J2zի7nثe&fa[C tm)#hm{ʶUPҷ4|b$io}/ԝ>"zI8V g# [5S6Z-rde$9!%' H@!aYQϊwmcYx[9t}+w:TFue)8JQD$( xUDYAiɱ o%Gr\2xJNGÓg+n_ہS)JM9) a){8V(411 ꎜMds.n>W!]Sky;Ӝ-/qQ*8]8e(-@RNmݻK/S}_viR_GnGu-ԒXYl/4͸ i"GB]2<(⏖P:j.hG؇T%>kW r0$$F ޜڻmf4Ma7g(q;rHo'n*r壑&d0 s6O pC dqʼUwh;V`պv:$PkZk&tsb3JqpN'^{$ĽZ[d-ĵ.:YA$25J !h{2;:"ɔ0XC 8 ARA$6@ʇoh5QqX7),R`Y *HeV2RHw {% ّLWYiaIJTFR|&ԎX:Ū"\wĘRm|h ai(RBRZOD1h~LD".1`l(uV7g SOP!avd adJ-rK`CM79%ހ0:A9?Ij\]} ymgk.6ڔ8P %DCJ'E.CM%PmU˶F8t8Vㄅ(IݻYςV\'BS.Za%NH Rvl53L\NEy%hӨ-^YKNPRr;VPH+l,q]KnBWr:`FbxʄiǥKj3)*RSQ HJIĝV٪b_Ube\t8gŬ`АQu3QܜӖ-Ӫ-rxu(q*rt.;C+U3QjJ}+ *9l(>sd"JR F[\͊wyx-}y?&׻|ޅI ^.-ʶΓfMJx>H*R҇Re O* l[ŸeJ۔[̈́GĒ@l'e8rd/U˕pB$++;cOܶٳvr$qfB)t},Q0rA$(uնڔIShAJwh;V`պv:$PkZKn&PJR*R@JRT( $MYoe$U]k%MGf88%lT;ʀ5Eӄ!ƜmJi)V ) RII  T>ҮV_Y햤C77aoq)?8tS᠍Ӛ6unX$;0RzCIO R9xBTQPFV1/L:a_+J"2_6ZKA6@T6[Ҿ7m0gHȫf\n([<=$T7c ێǪڣcJXsKK3mIJ• ` OhHՙR"l-YyyBRR + kZNƳ ! ImOŲCj܌-I ;тw25w^4͍kGRQ<fzU^j 9M'?jYj򿤪5k?UW^c\4z=v>'5IiC{Tߜ[y{a%tZ|jIS[E|U=/T-+JC2ڔ`Äӎs=+H&#Z-ٌ5M<@qES')d`cǎ$uO@!]EǓZT$%@-x(60]"[8vSeA#„JJI>OJ;=f4r_Znq8؈}%l\I*蠁}$Q=<7۴w.B$@ChKhJPUA'-ѭ79۵we :GEˮ@|MHF?l`NSΐҜmRb3' q㸅=0Gum.%:yi mmX )C>g렵Zu]5Csn!Hzsɵ!\)pK๹R`)ǨmY[5މnt[Nm 0w傤#(Nmݔ5_HGNlܧ堺_}mR(<)Nzg>go$+aXF#XC/ zԨ)2ly\~9Z\YSim嬶  u (nG%A::ɛukj%0a]SqGO;]&$C\َ -a3JTd)4H'U#X3ϑoK}˃q آpq<[{.}6c3;o˧a]ZߡDL始p\}L8@RT:.ˡiWv)!S$Jir\1Zq(K1mM}gP@jBrDЙE)ҒPX(%8P=+Uݛ3VnHXqhË|J@;IU9FZRoeBel)䩥煲J6 ł;>\Z3\ΊZ} 2TGc:6A26 M>d6PAApx$yVY:SJ ,Ewz7&N 0> ] 6/.&.m$68eHgQRZI a9Y5Z6&4C3DzBSQZST@'=p h҂l2mDuv6tܠ9[1TR7 DrXz5YeImiC%<`TBεTXjHm m҅$$>Z:[Nv(Mp) )h @N: PKpd]a2m1n8kq[𥥅 '!5sȚ4VWr"a֗ʸT<+s% c:S3yz΢s5|*X;?UdcXKjNh:'pBְ@'&NֻG4K{LFsnRRFPk;`#OξOuPHLrM>Ku SVDڧSE,mEwfP6渖 #r~zwz͕-\f,PN|JA'BiJ -eo/} @JYN6s˭ٷ[utw!Z5p%،2V\T Tc벣X]ϻuGJII2k-S+ne^|.Ijr4;Aկu \Xa[CJFI$Ԝֵ ۷nBvZs9ǘzZ>].=4ŨG2I+V,̧nCRPԔPC'p `Uߣ-jpUkzlכ!uP/xޢ :ʔV=mwzOwWn&vޛtLСK+3$_ ,ҒxVA%+P(+v[ Fzyh4E~lu<Tp+)?uwuWƛw9~Wݍzm1ұuNJ.㨮$B_#qŰRry˩-6hZ`أSR K{ uR  Wmml"xDQ01s)dBA[ &q:*$̑ |H5IIRr1>2nCs.~$*G)<Ҝ`fkFn_KF,;kIqИ7כ-;TYX X2ޫTM;"%Qd-l4PI K|1:Orɴ^ZW1{ s*rH 8V&dǻ0e{o>ͥǞÁޮ(PFRRRRR?SI?~*5~fzUVεP]JWy_UysBG絟V+J1HCzvt~G{d&ݤ} MRj;~qm|i%LYdi7EdRF#L8Izy2a\A1FM&wr`Hn 9xEN=S׹zi9 E6c2n(#T3(k,"ìP%X>}4:~,yt)nyiBJ)P@QJ烌WKԚkE36633"W&Cju,&dks ɩA 덶Nэϓh@پ)aJ!$Y SBt]"+M6 J _ dy2.x |nU maǔARSDy HM#0SI! Y.: %%G8dImt,Shyha !ԦC/$< '7ưk,AOD6"DIfS\BBw1É^A VĴ[Y;wn۞ߏMljlUwKdaZmBLm+hiMNAUUZg&MJTD,u(Jy@ NUpZjg7!ar[uq Bs¯*,c¬kc޼xw]wqvsj3En"s1 !aډ w]Nr4oKD!>I<@I |FOT=i}[x-T< +§]#ş7Yu%KjV:b"EᲹ\dFRP ǂ(c)a\[Bj}MBK㰥8 IKI>'N@\H PNYפN:OL-ƒҴ6e %%ָ{:ͦ\ͪjS"p.)[ [Ll[ܑTU 36WGYqkk;IA;A+?]/WReޮR]!o, BAQ'$ i֍ č0N)-rY+~9Bp8vXR)Ju7׵Kl6-ɘm ҷ6JPGv୎!E*PqAXR5ޔeKn]n"c^N}hp8rR(DiYo&XXWfSےAyeԮICz ɫ҃ȍdm#̅<^CmuXiL!8kgPPMy,)Zrhvp>#HuQʷ6d_[WhWIvpe}l>୎!E*PqZdiYQv{ .DŽ X뫧ڶ3zkcLǚ[l':ִ"ìP%X>}5iFimjԋ]щ٫YieE T Z6zks6Z!6.(uR1w>\4y-aז [AQBJ@JI'>AэLo'rpf؋[jT䄒8iB8PBջz&ln?N566+Jh2S% O!a j$Sɬw$\hq+} i*YHQH'jrq֍FMԈn >jϣnu0>xq|HI+g.keͫL[z~sndKj+_VV8OUU ulm&_ 8 YN吜 3ԐIh,ݫTug]FXް3mnvy;#lݻs.nLcHMr_RJܤZޗT- aXv(5:<:rkJ3)@$(@_miHw+ƄG6H~d"8nKi[TBJ"Lqp"|ɺƌR.hN`sҢ!qm\eKA);Oܕ JHPt_J+W$.s3Z.pˍ+t FmAH p"@] i6;tW:KbPi*IuY s*Tމ96mrR!eoSBVZrmHgZۂJ'Lm8R) ' $(:6u뮶'S({x7"AHqHt䔥+Y( cxސi}=W+F5?Z.(6o`Q8NE 5ﶙKD!>I<@I |Fh5[{FJ7n-2um r7h_7$7m8f8_%ٷzfRR5ؠƛq6XTʓ%Jw!i݌zVLh7w[k[ah} %mmޕ%$oA€P# F1/M^bwӱ6<$4RTP Y*6qN 黣99B䷸7Ai,iYK'$J讞)@)A>fzU^j 9M'?jYj򿤪?N{Yj򿤪59>!봏=ɪM]HO~̚v +WJ+*Dhq YY2e/$Nyu5J ڊm-Ǵ*9BV*³Se8;:` ٸ4M9+Mn}FCʂP0 8e-$UPoiſۤR5) 4ۋ-p(8 'Ȍf^^6qmWw0)v*mv6.0.$TMJYqRƗkD#rS!$BǍJu$%!*Һnޗ5Sp.q_R\íB'$oU -"e]=+i^Licj`9<=8gu #d[3,:򸅂r.{-ae%%m˕-S5>-[PYVݘ7n:6u3*,xrw"<VChPˏ(R3ԡZPX{B^JZtHZPوK) JUV2bzn;q\`G7&?BGu:t:"-;p6$xZz$yCoi[RƸ9rb FCi쬗R2S+*JmgfFnqPMyI}VK QR| RH5[=j&{Vwo&-[-䡶xx:쌴EKJW7rjPtnӵśTXoeR^n3(ł..SKc)b)zR[d9wtURN:íDm6Pl(o+n 9UBjUt]d>Br iN!]2OP3\+*Dhq YY2e/$Nyu5JPu "fęڅ;tke@GRK,ᗕqTrjM}.KXݒBnoe?|R猥 ZJ,t3;E;3- 6~הյI۽ Hqg EDioݭNc\),=nJEd#m-{J 75ôg&5tKl!n!+}TR-J$$ uMJ݃{v9*œjKA)aW I?(NIm;(:][} 5$aL#|7ҵ,L4@R*iJrZJWfiwS#%p[RR7TV'/Tk9* 1{tb*ȍKQ`%HpxYٝzR}KzLsqzk[jڤބ8n"5(,MQ5:.XjEp|VJU[A"@[6Q-(*,yTPx|w*_hE,Gjcc!r׹HBpBk#k-y.aow4 %҂񭵗Ƌ+&}Բ奈͋{ӭ4#yZXJSA xwٵ6sB"\j#Kt e%JS-8{vrPt{E&ul_$4!eͪJRN**篑εfΩHZPCץ^')@xmvs`҂àAu.|֢6Mj/:WYCiړT9V=|R=[hTrDUg?,˙pwdtJPuDksQn56׶.`4 Ԇc,S0IS[XdNvx[@C)j+0῔p*Am)R`,JPt]#ʹSֱ7hѝ8)Ÿ^I VlʟpjNࡹ-3+'j<]sz ARÅEɩAt]4X{.ȼ!Qm#eR(ANN7gz$ez}ԣaӹ+Ɯqj_f'g YK I(҂.T{8Lքyn)Bf0K[v`TݻFHst̻%5w"Q%ۛa `Ax'n=yUVХAt֧Gf/08b2BҕcrQ[DI. 2AP 5J S!ܘћm)O Ԛ"ɦg#q!)rsX!E SMk4PJI~sC͏CISkKsr:,Ud~-ؐtwd˩uJq-BBӄ'aQMQAx^i) [~ hJzBPygd4׺{-/9rU*uP|N%'*ڠGF;M3ؘeo7nD܄^HB\#`A *I;FUuۍ[!DYBRL܄Km%.lmJBJ)J)J3437ҨWgi?goץQlUU~Ǖ%W?$~!N{Yj򿤪59>!봏=ɪMtۼx2vL:TƖ҄-ITo!Nxae=@hmm ݠ#r2JXh!$h(<ŷWEƯt*Ґ-7=K9ĎȬKiMîIppT)$VЍ% /x7kCd3i$;TrHĂJC컢ʿ"DWŌkq's"BZI(Ue3f5RQ(IirpaRR(T홭[j^ԋ9C*8ZH 0:ۮj)UNKAޥJ&d<3N o)[Q!)iJu#}wmjv.6."hiцRR]:R?ûյ:GÒR;hӷ4{6R 6Аj+FZ)(\c)!U]4(QD\$;B{ :d 3-6kɂV N-Jֳ+i@P+JKI R78Gg+Pfk_y Y/FZ R,xBr1O_vtRM ɱripeM(P%4.a,uSaM\ZNP#;FlwdK]2X\B}%q$+zg!T UDmR^ڶKnd -z~Swa*ei;Zr;{Em&΍:ڑڏ;VzUeU I".,g_[);qI@2+jz #$s Sxkcw%8Z$tŽ~ȭZ)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J3437ҨWgi?goץQlUU~Ǖ%W?$~!zsU~Ǖ%W?$~!AiG}x}.k& gTۈI GMT0_k>n3|yUP=izޖqKǓ:36A dڞ*yPWVCjT:<ŷWEƯtL:BoLD m`#ž}Un]eķ]gC59m1!m`(`uNqvcD8&B^q=r2z7gZD=+FpSHuR4rP7( r-P/8;X2䄫r²+,mSٍ6l([fs\K n㑎?]_4֟ӗosؗ$˓we29\eMS8UNp찴-T+MΛ sx2u’#)M%°QO{.}6c3;o˧\eCsz WP96j"-p:oR) RI ˦V.{yV &uH2OU7mܠj:RaU!):nOoJRTwdPGhk[Nʗ.ݨM_[MqdTrUv4dEqVěX"FAKiPp)))qJO:=Xt']w#yfU5#-L$|K++05D_TQuXⰯTzRuu .:#1&덯DXt h)!R0/r% -IAN㝵kݏ#4ix6̤<ʜ 9hcn卸 PWM*,u`BܚF6a;p6 tAZڧ.r"-ke)N$TWxsD=68]7nVTI-ߌq)$H/:;M85[pvZڡ C|6_3\9>gpI1'ټ+\GûaKę0=$-85.xJ:T̹9۸:0 ʶ>Cʴm׋,q棇-$->`0 :P74 aldS\UnvkYq* I+^0q4h4vz]n%ZaZx&B|,%̸xoȵ@\[bZ˻ @#ʾܻmjҰ݋nSS.c,ItZApJ>OfhHչv☱,!E- VT1|(V$)k /Γ/a1㺥Nv FN: ֭uM`խ.4b-^I: \+mߓG UL5fE5cd]~zтC@ +;HW] Oy0/0"FZS2ܒ -pJeN~MY J|aE Kd85C48.9+='-[Z-7Xz4}BcڣbDVRZm h/SG@s=gn̑O*",qo C!.28o^@S8ޢ[Hx(T,j,ڭ?k2'_}riż\N)IEA^Ns=rxpx&F-덙PT)Vm{9;7#<_k6QIF틴m-eJVRv>Hhp$$ԫ\2ݺÝuY o6TBe-K,*x, ;n;1ۭQ_1ci*yYԉlGW5J@Ch˯nRR/[sf{ ///ѻo v:f}Sb1]w|4QC J]Y* *DIiWe-k ^#q 2Va7< a9FecUÇ-K2eHhp$$ԫ\2ݺÝuY o6TBe-K,*x, ;n;1ۭQ_1ci*yYԉlGW5J@Ch˯nRR/[sf{ ///ѻo v:f}Sb1]w|4QC J]Y* *DIiWe-k ^#q 2Va7< a9FecUÇ-K2e!봏=ɪs1<2î":)$6rPz'O;H83ehXh3u<|XiI%YRS{V dnJ^g]ҥՆѣ/X0Y.U:(ڧ~o;Nb.NrlFCcrcFn;c>^2rORjˤ5< Ndp,%^al^AJ( H*\YOFqM)m-HQiԸAڤAWPI@П.˻Y-i\Fqn M]ik FK{!^nlVHW݉ 'Gz*f@K:T!-8HIRvSam6ˋSD<:7)> )9I`gλ% Ev[[lIRBBA;S`Ri˽KO|Է|8odK)`J p !i)!=N<5eq$J* q\THBWNt-{ (V ʊX-3cm96Rl!Zϒw,2H] &/;>)7'."ru]J q!-#Ի>zIN2÷71I2R%BT~XA($ڭ3rm)"\8h))VFT5M^dٍ٨Pdm*-^VxWKKuʕ"5'9i(Map Rp ֱF[FW +Z%snRҲZo_Jj)><@Aʴ΋fwu)uвVRRJF( 2kF79POg+sSZzkwҟeX,!FBIJ1@Iꉽ}3w&mqIo{yL&2ƌ]pI}>$82@fv Hpfs]ٯΏ 醐VRF<'>D+սe]쒕.umI eeRJH8UOxo&:u!Lv9a#rW,!$8Co 8}lY+֫|wfm6P*^[ {d fїK3sH(-O,TJ-;N+ؚLC])qkr Ò P-N@)*R*gvv]P)ȲAiLe\Ip+ EЅl.Bz]r.ur ya 8'ute%ⅹKZڔP RF385VLz&M[ж[r;cCldueiƊco( yPe(ZvDKseGX[+u$B"d p‘%N`[qm񶱃>DwIs4FD9Md3p%T AIP )*J!IRHRTB5eWUIih -6KJ$ W$JM6Wʼnk%jDfWPPF┓* ohh{sLY wCqG1@nK--!iUMr_RJܤZޗT- aXv(.H9$)[]ڇ^BFBVr@;87ĕ.PU5>U#ᦙVݘ%7nzrfĕ٥"3rmICcJv $!+AuεɍOqp-'9A;HR N H R3+ vdK[8Tzn8RD g7L˲XYny-UB] V _YvNGk^vϩl7o0@Dɐ B^)!\͡X9<>&bEPAmNs[kLԕIi([{RIR 5eMa#6T ;Bs`  ߨms;Qҗ$?h}$I}MZIHmJԝEW 9 A\IH@[Gi䔥{ R Ԥ+ ^ Knmb$a7,c9Ԍ<Pe.D-NYm޲Qa*vԓsGO\O8 iĠWs ;E,\`rHS;)( ('>2vq$oڽi͞*d#4 mpCo% *e  qWfhLɷ&*J8m*W_\I0nd^lMY-8 p&9r%!)) \e"W=K ~>&HXPPPX$(((ky'|kQ5|`#i”uP񐟜q[]M2erv0u m*’PK߯%j=6}B'C.SrCNʎ[ $PS.%'9 k\' ()*%IP$)*I JHP Ajx6-eÒ[ir)ZsğaJcĜ\{& 㘱" mqHJ*Rqe[A`\pniܫ4UHq.(? e%$-> *U[iPX}ꐥ+N3E$:c PBs(JHgF8 gʻuB42B*۳@0N[ؒ4BdFnM1RPqlp)RN#p`!ֶ1S.2}T('i BII Fb~!N̟)kuwU R59vKk-"E*(K6A}K Nz kճN:mH2A(K$+T~Y+' Hj7֭ҘXbKmiڒ)-8 ojI*V[W[ gj:RP-/4SZcXI) IZe]^n|"%ymJ"::>Dd*VitnsiHRCAINr7j&nFhطR%hWmj򴤤l^HجlPYj&sm-AS*;?N Ҁ' 2GIu54^hif:AaH~UN W6Ljo./Zjg7!ar[uq Bs¯*,c¬$+0И@[v >Y-3drjsYjnc!ۊSZÐCHWNqJqŭJ*NFEw5!7Lu*Ah,k:R]w (aKCӕ$lZBHI$Yu"tyv CoerLm^7'{jZpG&;P ͦTn4A)l+rU*JRJRJRJRJRJR| 9M'?rOTE[:Av*_IU5kӞ%Z<*ƹ##sG}C^2pR*bjnܕ1kkG[.x+ Zz'RBz>MOΓ[M?~3mdpFA#&Iw1nk䆛ݻ Tߤe$)CJ;~qm|i(ۥ}s.d8IiMFR UL E`tU.2!nBۆ[CyK/ ڔHNԇ u.,}tf1MnJaVλ|MupWEda@$i_[[q5%H.>$r^UQ N-[䰗zVm!(ꫛwv<-Cj`ZR. 竡% oև$jgvAEqp(\TJ8RS@@z Eziq{i ,6BuR8PvDv4]\R -ʲIy>a8 uSV0ipJ|(:)R6dzyT= BunTi-lԤNA dg^%*<%.8ϡF@ܷ)BJR ʔ:;fNrD{|ksKєF Z=Tz 77X!RZ+!WBX8~v0 DQ6lHU2c>\)D|+V64HU3A,9MŖd6YZx, zN^n.CYnDe:yQR )UseÉ53ܫ޴ss+Bmoɴ*,ة0 l';A+* ֓1f}uH[Slqlqڷ# RBN`DjG;cdc&>vVHJwpωR)P)F;hWۖFL%rRpE<1:$g*Pbh֝XŨJ @ۋ!N%d[+JT'b[E)|6h-g;Br|R@$ WkB$\76l̀p]-+ @O# F9 #K~}$RI°g96~)ӻf䥎;ɬ1Si%a%)IIPIj Bf߹ ҊNT[*.=,p[%#?% '>$%YG%&Jݬa\I?p~}Vm[,m蝡2{jrncI Ehyn!@eT )GhwV.aʷ}OjYShp)%y @ 'h9 ={ΚHkDYIW %mJT,-A@ Ta>,VcK:$Cq )FHK^A}zr0ZCHCnl\ BJ%IHteۺVdL^t䥲X C(K2ZQ>#' sm2-=kl1ԙ a 'aOJiyJR9#q]~wqX`ȵr*/!)I .僐հ[؉-F\[~jv$#3* ]BѧR5*ur]_ø7=p7-mJ1Z2H)rvDuՕ-ŨD$M p1&(dJuġ+ .!%'mDNvkmj;*u_"mV0 qy. JP#@a{ 5 v`Bd.͠ y ُutiҧ7e̾ԸoPVUtTVUvjlZr cbu ;Q@랉.:D{Mpi%HC8Ӏ>~1PY+L f$)pJTv ncP_؂ _.mDCK H8ڲR|@h.v޼ b.1~xm.v!Klyմ-[2"P#dZ˷L)S$%T>,@J/~_.wN9-opcv8Ǟb^.IzιHBiv\I Ddh7M&ntͰmG.ZS9Jv^$g8NzI-gN--QxiD1aH;! /@Q^.IJeζZ jv$A)%$d+jݪu5TvEw"jbk (t[[,ɏ&w{QH އzD~JWGi'v6 ^=˺)$Ez\XοmRw<)M,$!e[ תu2ܸ8EwRH Q2Rό08P5kj|ķNJĵw$%[GP1ASvܿX_KK30pRs#]z$('$XNOImFF!*֝lNO㍏۱~uݸVzT(69-#dKmy! uH20j_HłTK kh-i Qx2RT@9Nq|[֫[sID%.0*J!$(a@ENhl`=.*膚8|($OG^׉18Gg+Pfk_y Y/FZ R,xBr1.ַ0̸[f0/ۤ}1FŬlDѺ\'g9>:LnTeJ$͎ ۷nBvZs9ǘzf'k.ivf2F0v0$TkE1橴G5(Js, xdI[ڳ DQRNTo)Ϗ zP*#Lٮz=ڱ!iier$ V wwjF6lKwwF[[| ڋPT:) 7ޞ%v&Sj\^ؠ,j'̤T`"~L61wjUjO\n#މ;:]xeiq^jTx@0NqF>rXTwVͫēЬֽ>edwC&Ri\d{t6 r絼+Y[#5Ya$t$fo^::[dw\mM^)Cj)pK|ȵż>"٩{RXäe<, 㫻4;ofmn1Sm Cha%3\(e[HQ%#'Clo.̴Huُ" aa+)Ɣ@$konʊR½,qFDFӬM8.8)'j$?P_HY˜m OZkjd8Q$`'5x&]:! r$$qN?YӖ]3+F\oWR}mBA$Omۼ NsӭZ-Bx)S+ JHQV`췋Rnڝ!lJII P_˜K|xKZwrBUYHuM$7vȘG8H e,K8iq8x 5_&$ccQشeu+my>z-zWZתob52I8JT$ַ qw~\m|^v_: ݤi 1vqtBf̒Y[Mqeho@B-d$)_8xirȳ—pTX|2W ( +8 >uNv,(j+[nCiS\(pz)q:*$̑ |H5IIRr1> Z53ѻ&ìotɅ8˛ԝ!k”!Da`t X1mkΈ%]PP{WW?$~!a5j KǢBGU$?% ]Bt{Q봏=ɪMw캢iasqIy(XyPAQsIp[>k|nq=aNs&'8N:0zY;/ݨYɦ2p=%)ZN)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)A>N%u{Qڒ :WDD$6n6R0dz:K[iz^"YK0 JUpe9Fw3V3s^UrXC%(tNp0?HHC_jmF۳ a"͎-RZQ?ԯA 7?$~! p&:0cĕv*ko3~4V#I B6^h,>~u[W\]IyVYƮ~|9 ]U(1^1TkǿF^ ȧsZ>sq=MldomtQg E*5ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~R_S=>sq}OldѺNQg E*=ٓ7fOϫo~ROS=>sq}OldU~R_S=>sq}OldNQg E*=ٓ7fOϫoNQg E*=ٓ7fOϫo~ROS[=>sq=OldNQg E*=ٓ7fOϫo^Pg E*5ٓ7fOϫoNQg E*5ٓ7fOϫo~ROS[=>sq=MldNQg E*=ٓ7fOϫo^Pg E*=ٓ7fOϫoNQg E*=ٓ7fOϫo~R_S=>sq}OldU ~ROS=>sq=OldT~ROS=>sq=OldNQg E*=ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~R_S=>sq}OldNQg E*=ٓ7fOϫo~ROS=>sq=OldNQg E*=ٓ7fOϫo~| Ԕ|%֠R$ɽ_ijuq" SG'))*>H?ٯl&vfg<)1uiHU֢(2e85mO+[uWVͧK =q[(()QzD= 55 VA& >o3Ԇ+Jzu8|rj~sʪ8\,gXu`ƪLa xsipp-3.7.2/docs/sipp.rst0000664000000000000000000000622314525516253012036 0ustar Main features ============= SIPp allows to generate one or many SIP calls to one remote system. The tool is started from the command line. In this example, two SIPp are started in front of each other to demonstrate SIPp capabilities. Run sipp with embedded server (uas) scenario:: # ./sipp -sn uas On the same host, run sipp with embedded client (uac) scenario:: # ./sipp -sn uac 127.0.0.1 Running SIPp in background `````````````````````````` SIPp can be launched in background mode (-bg command line option). By doing so, SIPp will be detached from the current terminal and run in the background. The PID of the SIPp process is provided. If you didn't specify a number of calls to execute with the -m option, SIPp will run forever. There is a mechanism implemented to stop SIPp smoothly. The command kill -SIGUSR1 [SIPp_PID] will instruct SIPp to stop placing any new calls and finish all ongoing calls before exiting. When using the background mode, the main sipp instance stops and a child process will continue the job. Therefore, the log files names will contain another PID than the actual sipp instance PID. Screens ``````` Several screens are available to monitor SIP traffic. You can change the screen view by pressing 1 to 9 keys on the keyboard. + Key '1': Scenario screen. It displays a call flow of the scenario as well as some important informations. .. image:: sipp-03.jpg + Key '2': Statistics screen. It displays the main statistics counters. The "Cumulative" column gather all statistics, since SIPp has been launched. The "Periodic" column gives the statistic value for the period considered (specified by -f frequency command line parameter). .. image:: sipp-04.jpg + Key '3': Repartition screen. It displays the distribution of response time and call length, as specified in the scenario. .. image:: sipp-05.jpg + Key '4': Variables screen. It displays informations on actions in scenario as well as scenario variable informations. .. image:: sipp-06.jpg Exit codes `````````` To ease automation of testing, upon exit (on fatal error or when the number of asked calls (-m command line option) is reached, sipp exits with one of the following exit codes: ==== =========== Code Description ==== =========== 0 All calls were successful 1 At least one call failed 97 Exit on internal command. Calls may have been processed. Also exit on global timeout (see -timeout_global option) 99 Normal exit without calls processed -1 Fatal error -2 Fatal error binding a socket ==== =========== Depending on the system that SIPp is running on, you can echo this exit code by using "echo ?" command. Contributing to SIPp ```````````````````` Of course, we welcome contributions, and many of SIPp's features (including epoll support for better Linux performance, RTP streaming, and Cygwin support) have come from external contributions. See `developers guide`_ for how to get started + Richard GAYRAUD [initial code] + Olivier JACQUES [code/documentation] + Robert Day [code/documentation] + Charles P. Wright [code] + Many contributors [code] .. _developers guide: https://github.com/SIPp/sipp/wiki/New-Developers'-Guide sipp-3.7.2/docs/statistics.rst0000664000000000000000000001173214525516253013256 0ustar Statistics ========== Response times `````````````` Response times can be gathered and reported. Response time names can be arbitrary strings, but for backwards compatibility the value ``"true"`` is treated as if it were named ``"1"``. Each response time can be used to compute time between two SIPp commands (``send``, ``recv`` or ``nop``). You can start a timer by using the ``start_rtd`` attribute and stop it using the ``rtd`` attribute. You can view the value of those timers in the SIPp interface by pressing 3, 6, 7, 8 or 9. You can also save the values in a CSV file using the ``-trace_stat`` option (see below). If the ``-trace_rtt`` option is set, the response times are also dumped in a file called ``__rtt.csv``. There, each line represents a RTD measure (triggered by a message reception with a ``rtd="n"`` attribute). The dump frequency can be tuned using the ``-rtt_freq`` parameter. Available counters `````````````````` The ``-trace_stat`` option dumps all statistics in the ``_.csv`` file. The dump starts with one header line with all counters. All following lines are "snapshots" of statistics counters given the statistics report frequency (``-fd`` option). When SIPp exits, the last values of the statistics are also dumped in this file. This file can be easily imported in any spreadsheet application, like Excel. In counter names, (P) means 'Periodic' - since last statistic row and (C) means 'Cumulated' - since sipp was started. Available statistics are: + StartTime: Date and time when the test has started. + LastResetTime: Date and time when periodic counters where last reseted. + CurrentTime: Date and time of the statistic row. + ElapsedTime: Elapsed time. + CallRate: Call rate (calls per seconds). + IncomingCall: Number of incoming calls. + OutgoingCall: Number of outgoing calls. + TotalCallCreated: Number of calls created. + CurrentCall: Number of calls currently ongoing. + SuccessfulCall: Number of successful calls. + FailedCall: Number of failed calls (all reasons). + FailedCannotSendMessage: Number of failed calls because Sipp cannot send the message (transport issue). + FailedMaxUDPRetrans: Number of failed calls because the maximum number of UDP retransmission attempts has been reached. + FailedUnexpectedMessage: Number of failed calls because the SIP message received is not expected in the scenario. + FailedCallRejected: Number of failed calls because of Sipp internal error. (a scenario sync command is not recognized or a scenario action failed or a scenario variable assignment failed). + FailedCmdNotSent: Number of failed calls because of inter-Sipp communication error (a scenario sync command failed to be sent). + FailedRegexpDoesntMatch: Number of failed calls because of regexp that doesn't match (there might be several regexp that don't match during the call but the counter is increased only by one). + FailedRegexpShouldntMatch: Number of failed calls because of regexp that shouldn't match (there might be several regexp that shouldn't match during the call but the counter is increased only by one). + FailedRegexpHdrNotFound: Number of failed calls because of regexp with hdr option but no matching header found. + FailedOutboundCongestion: Number of failed outgoing calls because of TCP congestion. + FailedTimeoutOnRecv: Number of failed calls because of a recv timeout statement. + FailedTimeoutOnSend: Number of failed calls because of a send timeout statement. + OutOfCallMsgs: Number of SIP messages received that cannot be associated with an existing call. + Retransmissions: Number of SIP messages being retransmitted. + AutoAnswered: Number of unexpected specific messages received for new Call-ID. The message has been automatically answered by a 200 OK Currently, implemented for 'PING' message only. The counters defined in the scenario are also dumped in the stat file. Counters that have a numeric name are identified by the GenericCounter columns. In addition, two other statistics are gathered: + ResponseTime (see previous section) + CallLength: this is the time of the duration of an entire call. Both ResponseTime and CallLength statistics can be tuned using ResponseTimeRepartition and CallLengthRepartition commands in the scenario. The standard deviation (STDev) is also available in the log stat file for these two statistics. Detailed Message Counts ``````````````````````` The SIPp screens provide detailed information about the number of messages sent or recieved, retransmissions, messages lost, and the number of unexpected messages for each scenario element. Although these screens can be parsed, it is much simpler to parse a CSV file. To produce a CSV file that contains the per-message information contained in the main display screen pass the ``-trace_counts`` option. Each column of the file represents a message and a particular count of interest (e.g., ``1_INVITE_Sent`` or ``2_100_Unexp``). Each row corresponds to those statistics at a given statistics reporting interval. sipp-3.7.2/docs/tools.rst0000664000000000000000000000175614525516253012231 0ustar Useful tools aside SIPp ======================= JEdit ````` `JEdit `_ is a GNU GPL text editor written in Java, and available on almost all platforms. It's extremely powerful and can be used to edit SIPp scenarios with syntax checking if you put the DTD (`sipp.dtd `_) in the same directory as your XML scenario. Wireshark/tshark ```````````````` `Wireshark `_ is a GNU GPL protocol analyzer. It was formerly known as Ethereal. It supports SIP/SDP/RTP. SIP callflow ```````````` When tracing SIP calls, it is very useful to be able to get a call flow from an wireshark trace. The "callflow" tool allows you to do that in a graphical way: `callflow `_ An equivalent exist if you want to generate HTML only call flows `http://www.iptel.org/~sipsc/`_ .. _http://www.iptel.org/~sipsc/: https://web.archive.org/web/20120106005622/https://www.iptel.org/~sipsc/ sipp-3.7.2/docs/transport.rst0000664000000000000000000001343314525516253013120 0ustar Transport modes =============== SIPp has several transport modes. The default transport mode is "UDP mono socket". UDP mono socket ``````````````` In UDP mono socket mode (-t u1 command line parameter), one IP/UDP socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. UDP multi socket ```````````````` In UDP multi socket mode (-t un command line parameter), one IP/UDP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. UDP with one socket per IP address `````````````````````````````````` In UDP with one socket per IP address mode (-t ui command line parameter), one IP/UDP socket is opened for each IP address given in the inf file. In addition to the "-t ui" command line parameter, one must indicate which field in the inf file is to be used as local IP address for this given call. Use "-ip_field " to provide the field number. There are two distinct cases to use this feature: + Client side: when using -t ui for a client, SIPp will originate each call with a different IP address, as provided in the inf file. In this case, when your IP addresses are in field X of the inject file, then you have to use [fieldX] instead of [local_ip] in your UAC XML scenario file. + Server side: when using -t ui for a server, SIPp will bind itself to all the IP addresses listed in the inf file instead of using 0.0.0.0. This will have the effect SIPp will answer the request on the same IP on which it received the request. In order to have proper Contact and Via fields, a keyword [server_ip] can be used and provides the IP address on which a request was received. So when using this, you have to replace the [local_ip] in your UAS XML scenario file by [server_ip]. In the following diagram, the command line for a client scenario will look like: ./sipp -sf myscenario.xml -t ui -inf database.csv -ip_field 2 192.168.1.1 By doing so, each new call will come sequentially from IP 192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.1, ... This mode is generally used for emulating user agents, using on IP address per user agent and calling a SIP server. TCP mono socket ``````````````` In TCP mono socket mode (-t t1 command line parameter), one IP/TCP socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. TCP multi socket ```````````````` In TCP multi socket mode (-t tn command line parameter), one IP/TCP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. TCP reconnections ````````````````` SIPp handles TCP reconnections. In case the TCP socket is lost, SIPp will try to reconnect. The following parameters on the command line control this behaviour: + -max_reconnect : Set the maximum number of reconnection attempts. + -reconnect_close true/false : Should calls be closed on reconnect? + -reconnect_sleep int : How long to sleep (in milliseconds) between the close and reconnect? TLS mono socket ``````````````` In TLS mono socket mode (-t l1 command line parameter), one secured TLS (Transport Layer Security) socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. .. warning:: When using TLS transport, SIPp will expect to have two files in the current directory: a certificate (cacert.pem) and a key (cakey.pem). If one is protected with a password, SIPp will ask for it. SIPp supports X509's CRL (Certificate Revocation List). The CRL is read and used if -tls_crl command line specifies a CRL file to read. TLS multi socket ```````````````` In TLS multi socket mode (-t ln command line parameter), one secured TLS (Transport Layer Security) socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. SCTP mono socket ```````````````` In SCTP mono socket mode (-t s1 command line parameter), one SCTP (Stream Transmission Control Protocol) socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. The -multihome, -heartbeat, -assocmaxret, -pathmaxret, -pmtu and -gracefulclose command-line arguments allow control over specific features of the SCTP protocol, but are usually not necessary. SCTP multi socket ````````````````` In SCTP multi socket mode (-t sn command line parameter), one SCTP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. IPv6 support ```````````` SIPp includes IPv6 support. To use IPv6, just specify the local IP address (-i command line parameter) to be an IPv6 IP address. The following example launches a UAS server listening on port 5063 and a UAC client sending IPv6 traffic to that port. :: ./sipp -sn uas -i [fe80::204:75ff:fe4d:19d9] -p 5063 ./sipp -sn uac -i [fe80::204:75ff:fe4d:19d9] [fe80::204:75ff:fe4d:19d9]:5063 .. warning:: The Pcap play feature may currently not work on IPv6. Multi-socket limit `````````````````` When using one of the "multi-socket" transports, the maximum number of sockets that can be opened (which corresponds to the number of simultaneous calls) will be determined by the system (see how to increase file descriptors section to modify those limits). You can also limit the number of socket used by using the -max_socket command line option. Once the maximum number of opened sockets is reached, the traffic will be distributed over the sockets already opened. sipp-3.7.2/docs/uac.xml0000664000000000000000000001065714525516253011631 0ustar ;tag=[pid]SIPpTag00[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.7.2/docs/uac_pcap.xml0000664000000000000000000001151514525516253012626 0ustar ;tag=[pid]SIPpTag09[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[local_ip_type] [local_ip] t=0 0 m=audio [media_port] RTP/AVP 8 101 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[pid]SIPpTag09[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag09[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.7.2/docs/uas.xml0000664000000000000000000001044414525516253011643 0ustar Content-Length: 0 ]]> Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.7.2/dtd_check.sh0000775000000000000000000000047414525516253011652 0ustar #!/bin/sh failures=0 for file in $(find . -name '*.xml'); do if ! xmllint --path . --dtdvalid ./sipp.dtd $file >/dev/null; then echo "ERROR: $file failed validation" failures=$((failures+1)) fi done if test $failures -ne 0; then echo "Not OK" >&2 exit 1 fi echo "All files OK" >&2 sipp-3.7.2/example/0000775000000000000000000000000014525516253011031 5ustar sipp-3.7.2/example/fortune.cpp0000664000000000000000000000463014525516253013222 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Charles P Wright from IBM Research */ #include "sipp.hpp" /* This is a fun sample of creating your own extensible keyword. */ int fortune(call *call, MessageComponent *comp, char *buf, int len) { int pipes[2]; char localbuf[SIPP_MAX_MSG_SIZE]; char *p = localbuf; int ret; int written = 0; if (pipe(pipes) == -1) { ERROR("Could not create pipes!"); } switch (fork()) { case -1: ERROR("Fork failed: %s", strerror(errno)); case 0: /* We are the child. */ close(pipes[0]); dup2(pipes[1], fileno(stdout)); dup2(pipes[1], fileno(stderr)); close(fileno(stdin)); system("/usr/bin/fortune"); exit (127); default: /* We are the parent*/ close(pipes[1]); while ((ret = read(pipes[0], p, sizeof(localbuf) - (p - localbuf))) > 0) { p += ret; } *p = '\0'; close(pipes[0]); if (len > p - localbuf) { len = p -localbuf; } p = localbuf; while(len-- > 0) { if (*p == '\n') { if (len < 3) { break; } *buf++ = '\r'; *buf++ = '\n'; *buf++ = ' '; written += 3; p++; } else { *buf++ = *p++; written++; } } break; } return written; } /* On initialization we register our keywords. */ extern "C" int init(void) { registerKeyword("fortune", fortune); return 0; } sipp-3.7.2/include/0000775000000000000000000000000014525516253011021 5ustar sipp-3.7.2/include/actions.hpp0000664000000000000000000002012114525516253013166 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. */ #ifndef _CACTIONS #define _CACTIONS #include "variables.hpp" #include "message.hpp" class CSample; #ifdef PCAPPLAY #include "prepare_pcap.h" #endif #include "rtpstream.hpp" #define MAX_ACTION_MESSAGE 3 class CAction { public: enum T_ActionType { E_AT_NO_ACTION = 0, E_AT_ASSIGN_FROM_REGEXP, E_AT_CHECK, E_AT_ASSIGN_FROM_VALUE, E_AT_ASSIGN_FROM_SAMPLE, E_AT_ASSIGN_FROM_STRING, E_AT_ASSIGN_FROM_INDEX, E_AT_ASSIGN_FROM_GETTIMEOFDAY, E_AT_JUMP, E_AT_LOOKUP, E_AT_INSERT, E_AT_REPLACE, E_AT_PAUSE_RESTORE, E_AT_LOG_TO_FILE, E_AT_LOG_WARNING, E_AT_LOG_ERROR, E_AT_EXECUTE_CMD, E_AT_EXEC_INTCMD, E_AT_VAR_ADD, E_AT_VAR_SUBTRACT, E_AT_VAR_MULTIPLY, E_AT_VAR_DIVIDE, E_AT_VAR_TEST, E_AT_VAR_TO_DOUBLE, E_AT_VAR_STRCMP, E_AT_VAR_TRIM, E_AT_VAR_URLDECODE, E_AT_VAR_URLENCODE, E_AT_VERIFY_AUTH, E_AT_SET_DEST, E_AT_CLOSE_CON, #ifdef PCAPPLAY E_AT_PLAY_PCAP_AUDIO, E_AT_PLAY_PCAP_IMAGE, E_AT_PLAY_PCAP_VIDEO, E_AT_PLAY_DTMF, #endif E_AT_RTP_STREAM_PAUSE, E_AT_RTP_STREAM_RESUME, E_AT_RTP_STREAM_PLAY, E_AT_RTP_ECHO, E_AT_RTP_STREAM_PAUSEAPATTERN, E_AT_RTP_STREAM_RESUMEAPATTERN, E_AT_RTP_STREAM_PLAYAPATTERN, E_AT_RTP_STREAM_PAUSEVPATTERN, E_AT_RTP_STREAM_RESUMEVPATTERN, E_AT_RTP_STREAM_PLAYVPATTERN, E_AT_RTP_STREAM_RTPECHO_UPDATEAUDIO, E_AT_RTP_STREAM_RTPECHO_STARTAUDIO, E_AT_RTP_STREAM_RTPECHO_STOPAUDIO, E_AT_RTP_STREAM_RTPECHO_UPDATEVIDEO, E_AT_RTP_STREAM_RTPECHO_STARTVIDEO, E_AT_RTP_STREAM_RTPECHO_STOPVIDEO, E_AT_NB_ACTION }; enum T_LookingPlace { E_LP_MSG = 0, E_LP_HDR, E_LP_BODY, E_LP_VAR, E_LP_NB_LOOKING_PLACE }; enum T_Comparator { E_C_EQ, E_C_NE, E_C_GT, E_C_LT, E_C_GEQ, E_C_LEQ, E_C_NB_COMPARATOR }; enum T_IntCmdType { E_INTCMD_INVALID = 0, E_INTCMD_STOPCALL, E_INTCMD_STOP_ALL, E_INTCMD_STOP_NOW }; typedef struct _T_Action { } T_Action; void printInfo(char* buf, int len); const char *comparatorToString(T_Comparator comp); bool compare(VariableTable *variableTable); T_ActionType getActionType(); T_VarType getVarType(); T_LookingPlace getLookingPlace(); T_Comparator getComparator(); bool getCheckIt(); bool getCheckItInverse(); bool getCaseIndep(); bool getHeadersOnly(); int getVarId(); int getVarInId(); int getVarIn2Id(); int getOccurrence(); char* getLookingChar(); char* getRegularExpression(); SendingMessage *getMessage(int n = 0); /* log specific function */ T_IntCmdType getIntCmd(); /* exec specific function */ #ifdef PCAPPLAY pcap_pkts *getPcapPkts(); /* send_packets specific function */ #endif rtpecho_actinfo_t* getRTPEchoActInfo(); /* returns stored rtp echo params */ rtpstream_actinfo_t* getRTPStreamActInfo(); /* return stored rtp stream playback params */ void setActionType (T_ActionType P_value); void setLookingPlace (T_LookingPlace P_value); void setComparator (T_Comparator P_value); void setCheckIt (bool P_value); void setCheckItInverse (bool P_value); void setVarId (int P_value); void setVarInId (int P_value); void setVarIn2Id (int P_value); void setLookingChar(const char* P_value); void setAction (CAction P_action); void setCaseIndep (bool P_action); void setOccurrence (int P_value); void setHeadersOnly (bool P_value); void setScenario (scenario * P_scenario); void setRegExp (const char* P_value); /* ereg specific function. */ int executeRegExp (const char* P_string, VariableTable *P_callVarTable); void setMessage(const char* P_value, int n = 0); /* log specific function */ void setIntCmd (T_IntCmdType P_type ); /* exec specific function */ void setDistribution (CSample * P_value); /* sample specific function */ void setDoubleValue (double P_value); /* assign value specific function */ void setStringValue (char * P_value); /* strcmp value specific function */ #ifdef PCAPPLAY void setPcapArgs(const char* P_value); /* send_packets specific function */ void setPcapArgs (pcap_pkts * P_value); /* send_packets specific function */ #endif void setRTPEchoActInfo(const char* P_value); /* parses rtp echo params from string */ void setRTPEchoActInfo(rtpecho_actinfo_t* P_value); /* copy stored rtp echo params */ void setRTPStreamActInfo(const char *P_value); /* parse rtp stream playback values from string */ void setRTPStreamActInfo(rtpstream_actinfo_t* P_value); /* copy stored rtp stream playback params */ void setSubVarId (int P_value); int getSubVarId (int P_index); void setNbSubVarId (int P_value); int getNbSubVarId (); int* getSubVarId() ; CSample *getDistribution (); /* sample specific function */ double getDoubleValue (); /* assign value specific function */ char * getStringValue (); /* strcmp specific function */ CAction(scenario *scenario); ~CAction(); private: T_ActionType M_action; T_LookingPlace M_lookingPlace; T_Comparator M_comp; bool M_checkIt; bool M_checkItInverse; bool M_caseIndep; bool M_headersOnly; int M_varId; int M_varInId; int M_varIn2Id; int M_occurrence; int M_nbSubVarId; int M_maxNbSubVarId; int * M_subVarId; char * M_lookingChar; /* log specific member */ SendingMessage * M_message[MAX_ACTION_MESSAGE]; char * M_message_str[MAX_ACTION_MESSAGE]; /* exec specific member */ T_IntCmdType M_IntCmd; /* sample specific member. */ CSample * M_distribution; /* assign value specific member. */ double M_doubleValue; /* strcmp specific member. */ char * M_stringValue; /* what scenario we belong to. */ scenario * M_scenario; /* Our regular expression. */ bool M_regExpSet; regex_t M_internalRegExp; char * M_regularExpression; #ifdef PCAPPLAY /* pcap specific member */ pcap_pkts * M_pcapArgs; #endif rtpecho_actinfo_t M_rtpecho_actinfo; rtpstream_actinfo_t M_rtpstream_actinfo; void setSubString(char** P_target, const char* P_source, int P_start, int P_stop); }; class CActions { public: void printInfo(); void setAction(CAction *P_action); void reset(); int getActionSize(); CAction* getAction(int i); CActions(); ~CActions(); private: CAction ** M_actionList; int M_nbAction; }; #endif sipp-3.7.2/include/auth.hpp0000664000000000000000000000271714525516253012502 0ustar /* * 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 3 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 */ int createAuthHeader(const char *user, const char *password, const char *method, const char *uri, const char *msgbody, const char *auth, const char *aka_OP, const char *aka_AMF, const char *aka_K, unsigned int nonce_count, char *result, size_t result_len); int verifyAuthHeader(const char *user, const char *password, const char *method, const char *auth, const char *msgbody); int getAuthParameter(const char *name, const char *header, char *result, int len); sipp-3.7.2/include/call.hpp0000664000000000000000000003007014525516253012445 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research * Andy Aicken */ #ifndef __CALL__ #define __CALL__ #include #include #include #include #include #include "scenario.hpp" #include "stat.hpp" #ifdef PCAPPLAY #include "send_packets.h" #endif #include "rtpstream.hpp" #include "jlsrtp.hpp" #include #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #include "sip_parser.hpp" #define UDP_MAX_RETRANS_INVITE_TRANSACTION 5 #define UDP_MAX_RETRANS_NON_INVITE_TRANSACTION 9 #define UDP_MAX_RETRANS MAX(UDP_MAX_RETRANS_INVITE_TRANSACTION, UDP_MAX_RETRANS_NON_INVITE_TRANSACTION) #define MAX_SUB_MESSAGE_LENGTH 2049 #define DEFAULT_T2_TIMER_VALUE 4000 #define SIP_TRANSACTION_TIMEOUT 32000 /* Retransmission check methods. */ #define RTCHECK_FULL 1 #define RTCHECK_LOOSE 2 struct txnInstanceInfo { char *txnID; unsigned long txnResp; int ackIndex; }; typedef enum { eNoSession, eOfferReceived, eOfferSent, eOfferRejected, eAnswerReceived, eAnswerSent, eCompleted, eNumSessionStates } SessionState; class call : virtual public task, virtual public listener, public virtual socketowner { public: /* These are wrappers for various circumstances, (private) init does the real work. */ //call(char * p_id, int userId, bool ipv6, bool isAutomatic); call(const char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest); call(const char *p_id, SIPpSocket *socket, struct sockaddr_storage *dest); static call *add_call(int userId, bool ipv6, struct sockaddr_storage *dest); call(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall); virtual ~call(); virtual bool process_incoming(const char* msg, const struct sockaddr_storage* src = NULL); virtual bool process_twinSippCom(char* msg); virtual bool run(); /* Terminate this call, depending on action results and timewait. */ virtual void terminate(CStat::E_Action reason); virtual void tcpClose(); /* When should this call wake up? */ virtual unsigned int wake(); virtual bool abortCall(bool writeLog); // call aborted with BYE or CANCEL virtual void abort(); /* Dump call info to error log. */ virtual void dump(); /* Automatic */ enum T_AutoMode { E_AM_DEFAULT, E_AM_UNEXP_BYE, E_AM_UNEXP_CANCEL, E_AM_PING, E_AM_AA, E_AM_OOCALL }; void setLastMsg(const char *msg); bool automaticResponseMode(T_AutoMode P_case, const char* P_recv); const char *getLastReceived() { return last_recv_msg; }; 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); bool checkAckCSeq(const char* msg); /* This this call for initialization? */ bool initCall; struct sockaddr_storage call_peer; scenario *call_scenario; unsigned int number; public: static int maxDynamicId; // max value for dynamicId; this value is reached ! static int startDynamicId; // offset for first dynamicId FIXME:in CmdLine static int stepDynamicId; // step of increment for dynamicId static int dynamicId; // a counter for general use, incrementing by stepDynamicId starting at startDynamicId wrapping at maxDynamicId GLOBALY protected: unsigned int tdm_map_number; int msg_index; int zombie; char * realloc_ptr; /* Last message sent from scenario step (retransmitions do not * change this index. Only message sent from the scenario * are kept in this index.) */ int last_send_index; char * last_send_msg; int last_send_len; /* How long until sending this message times out. */ unsigned int send_timeout; /* Last received message (expected, not optional, and not * retransmitted) and the associated hash. Stills setted until a new * scenario steps sends a message */ unsigned long last_recv_hash; int last_recv_index; char * last_recv_msg; unsigned long int last_recv_invite_cseq; /* Recv message characteristics when we sent a valid message * (scneario, no retrans) just after a valid reception. This was * a cause relationship, so the next time this cookie will be recvd, * we will retransmit the same message we sent this time */ unsigned long recv_retrans_hash; int recv_retrans_recv_index; int recv_retrans_send_index; unsigned int recv_timeout; /* holds the route set */ char * dialog_route_set; char * next_req_url; /* cseq value for [cseq] keyword */ unsigned int cseq; #ifdef PCAPPLAY int hasMediaInformation; pthread_t media_thread; play_args_t play_args_a; play_args_t play_args_i; play_args_t play_args_v; #endif rtpstream_callinfo_t rtpstream_callinfo; JLSRTP _txUACAudio; JLSRTP _rxUACAudio; JLSRTP _txUASAudio; JLSRTP _rxUASAudio; JLSRTP _txUACVideo; JLSRTP _rxUACVideo; JLSRTP _txUASVideo; JLSRTP _rxUASVideo; #ifdef USE_TLS char _pref_audio_cs_out[25]; char _pref_video_cs_out[25]; #endif // USE_TLS /* holds the auth header and if the challenge was 401 or 407 */ char * dialog_authentication; int dialog_challenge_type; unsigned int next_nonce_count; unsigned int next_retrans; int nb_retrans; unsigned int nb_last_delay; unsigned int paused_until; unsigned long start_time; unsigned long long *start_time_rtd; bool *rtd_done; char *peer_tag; SIPpSocket *call_remote_socket; int call_port; void * comp_state; int deleted; bool call_established; // == true when the call is established // ie ACK received or sent // => init to false bool ack_is_pending; // == true if an ACK is pending // Needed to avoid abortCall sending a // CANCEL instead of BYE in some extreme // cases for 3PCC scenario. // => init to false /* Call Variable Table */ VariableTable *M_callVariableTable; /* Our transaction IDs. */ struct txnInstanceInfo *transactions; /* result of execute action */ enum T_ActionResult { E_AR_NO_ERROR = 0, E_AR_REGEXP_DOESNT_MATCH, E_AR_REGEXP_SHOULDNT_MATCH, E_AR_STOP_CALL, E_AR_CONNECT_FAILED, E_AR_HDR_NOT_FOUND, E_AR_TEST_DOESNT_MATCH, E_AR_TEST_SHOULDNT_MATCH, E_AR_STRCMP_DOESNT_MATCH, E_AR_STRCMP_SHOULDNT_MATCH, E_AR_RTPECHO_ERROR }; /* Store the last action result to allow */ /* call to continue and mark it as failed */ T_ActionResult last_action_result; /* rc == true means call not deleted by processing */ void formatNextReqUrl(const char* contact); void computeRouteSetAndRemoteTargetUri(const char* rrList, const char* contact, bool bRequestIncoming); bool matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn); bool executeMessage(message *curmsg); T_ActionResult executeAction(const char* msg, message* message); void extractSubMessage(const char* msg, char* matchingString, char* result, bool case_indep, int occurrence, bool headers); bool rejectCall(); double get_rhs(CAction *currentAction); // P_index use for message index in scenario char* createSendingMessage(SendingMessage* src, int P_index=-1, int *msgLen=NULL); char* createSendingMessage(char* src, int P_index, bool skip_sanity=false); char* createSendingMessage(SendingMessage*src, int P_index, char *msg_buffer, int buflen, int *msgLen=NULL); // method for the management of unexpected messages bool checkInternalCmd(char* cmd); // check of specific internal command // received from the twin socket // used for example to cancel the call // of the third party bool check_peer_src(char* msg, int search_index); // 3pcc extended mode:check if // the twin message received // comes from the expected sender void sendBuffer(char *buf, int len = 0); // send a message out of a scenario // execution T_AutoMode checkAutomaticResponseMode(char* P_recv); int sendCmdMessage(message *curmsg); // 3PCC int sendCmdBuffer(char* cmd); // for 3PCC, send a command out of a // scenario execution static void readInputFileContents(const char* fileName); static void dumpFileContents(void); void getFieldFromInputFile(const char* fileName, int field, SendingMessage *line, char*& dest); /* Associate a user with this call. */ void setUser(int userId); /* Is this call just around for final retransmissions. */ bool timewait; /* rc == true means call not deleted by processing */ bool next(); bool process_unexpected(const char* msg); void do_bookkeeping(message *curmsg); void extract_cseq_method (char* responseCseq, const char* msg); void extract_transaction (char* txn, const char* msg); int send_raw(const char * msg, int index, int len); char * send_scene(int index, int *send_status, int *msgLen); bool connect_socket_if_needed(); char * get_header_field_code(const char * msg, const char * code); char * get_last_header(const char * name); char * get_last_request_uri(); unsigned long hash(const char * msg); typedef std::map file_line_map; file_line_map *m_lineNumber; int userId; bool use_ipv6; void get_remote_media_addr(std::string const &msg); std::string extract_rtp_remote_addr(const char * message, int &ip_ver, int &audio_port, int &video_port); #ifdef USE_TLS int check_audio_ciphersuite_match(SrtpAudioInfoParams &pA); int check_video_ciphersuite_match(SrtpVideoInfoParams &pV); int extract_srtp_remote_info(const char * msg, SrtpAudioInfoParams &pA, SrtpVideoInfoParams &pV); #endif // USE_TLS void extract_rtp_remote_addr(const char* message); bool lost(int index); void setRtpEchoErrors(int value); int getRtpEchoErrors(); void computeStat (CStat::E_Action P_action); void computeStat (CStat::E_Action P_action, unsigned long P_value); void computeStat (CStat::E_Action P_action, unsigned long P_value, int which); void queue_up(const char* msg); char *queued_msg; int _callDebug(const char *fmt, ...) __attribute__((format(printf, 2, 3))); char *debugBuffer; int debugLength; #ifdef USE_TLS FILE* _srtpctxdebugfile; int logSrtpInfo(const char *fmt, ...); #endif // USE_TLS SessionState _sessionStateCurrent; SessionState _sessionStateOld; void setSessionState(SessionState state); SessionState getSessionStateCurrent(); SessionState getSessionStateOld(); }; /* Default Message Functions. */ void init_default_messages(); void free_default_messages(); SendingMessage *get_default_message(const char *which); void set_default_message(const char *which, char *message); #endif sipp-3.7.2/include/call_generation_task.hpp0000664000000000000000000000357314525516253015712 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #ifndef CALLGENERATIONTASK_HPP #define CALLGENERATIONTASK_HPP #include "task.hpp" #define DONT_RESCHEDULE 0 class CallGenerationTask : public task { public: static void initialize(); static void set_rate(double new_rate); static void set_users(int new_users); static void set_paused(bool paused); static void free_user(int userId); bool run(); void dump(); protected: unsigned int wake(); private: CallGenerationTask(); virtual ~CallGenerationTask(); static class CallGenerationTask *instance; static unsigned long calls_since_last_rate_change; static unsigned long last_rate_change_time; }; #endif sipp-3.7.2/include/comp.h0000664000000000000000000000327614525516253012140 0ustar /* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #include #include #ifdef __cplusplus extern "C" { #endif char* comp_load(void); #ifndef COMP_MAIN extern #endif int (*comp_compress) (void ** state, char * msg, unsigned int * msg_len); #ifndef COMP_MAIN extern #endif int (*comp_uncompress) (void ** state, char * msg, unsigned int * msg_len); #ifndef COMP_MAIN extern #endif void (*comp_free) (void ** state); #ifndef COMP_MAIN extern #endif char comp_error[255]; #ifdef __hpux #define COMP_PLUGGIN "sippcomp.sl" #else #define COMP_PLUGGIN "sippcomp.so" #endif #define COMP_OK 0 #define COMP_DISCARD 1 #define COMP_REPLY 2 #define COMP_KO -1 #ifdef __cplusplus } #endif sipp-3.7.2/include/config.h.in0000664000000000000000000000152114525516253013043 0ustar /*-------------------------------------------------------------------------- * This file is autogenerated from include/config.h.in * during the cmake configuration of your project. If you need to make changes * edit the original file NOT THIS FILE. * --------------------------------------------------------------------------*/ #ifndef _CONFIGURATION_HEADER_GUARD_H_ #define _CONFIGURATION_HEADER_GUARD_H_ #cmakedefine HAVE_ENDIAN_H @HAVE_ENDIAN_H@ #cmakedefine HAVE_SYS_ENDIAN_H @HAVE_SYS_ENDIAN_H@ #cmakedefine HAVE_EPOLL @HAVE_EPOLL@ #ifdef HAVE_SYS_ENDIAN_H #cmakedefine HAVE_DECL_LE16TOH_BSD @HAVE_DECL_LE16TOH_BSD@ #define HAVE_DECL_LE16TOH HAVE_DECL_LE16TOH_BSD #else #cmakedefine HAVE_DECL_LE16TOH @HAVE_DECL_LE16TOH@ #endif #cmakedefine HAVE_IP_COMPAT_H @HAVE_IP_COMPAT_H@ #cmakedefine HAVE_UDP_UH_PREFIX @HAVE_UDP_UH_PREFIX@ #endif sipp-3.7.2/include/deadcall.hpp0000664000000000000000000000101414525516253013257 0ustar #include "call.hpp" class deadcall : public virtual task, public virtual listener { public: deadcall(const char *id, const char * reason); ~deadcall(); virtual bool process_incoming(const char* msg, const struct sockaddr_storage *); virtual bool process_twinSippCom(char* msg); virtual bool run(); /* When should this call wake up? */ virtual unsigned int wake(); /* Dump call info to error log. */ virtual void dump(); protected: unsigned long expiration; char *reason; }; sipp-3.7.2/include/defines.h0000664000000000000000000000306614525516253012614 0ustar /* * 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 * * Copyright (C) 2003 - The Authors */ #ifndef __DEFINES_H__ #define __DEFINES_H__ #ifdef __cplusplus extern "C" { #endif void ERROR(const char *fmt, ...) __attribute__ ((format(printf, 1, 2), noreturn)); void WARNING(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void ERROR_NO(const char *fmt, ...) __attribute__ ((format(printf, 1, 2), noreturn)); void WARNING_NO(const char *fmt, ...) __attribute__((format(printf, 1, 2))); #ifdef __cplusplus } #endif #define MAX_PATH 250 #define EXIT_TEST_OK 0 #define EXIT_TEST_FAILED 1 #define EXIT_TEST_RES_INTERNAL 97 #define EXIT_TEST_RES_UNKNOWN 98 #define EXIT_OTHER 99 #define EXIT_FATAL_ERROR -1 #define EXIT_BIND_ERROR -2 #define EXIT_RTPCHECK_FAILED -3 #endif /* __DEFINES_H__ */ sipp-3.7.2/include/endianshim.h0000664000000000000000000000231714525516253013314 0ustar #ifndef ENDIANSHIM_H #define ENDIANSHIM_H 1 /* Fetch HAVE_ENDIAN_H, HAVE_SYS_ENDIAN_H, HAVE_DECL_LE16TOH */ #include "config.h" #ifdef HAVE_ENDIAN_H /* Linux and friends. */ # include #endif #ifdef HAVE_SYS_ENDIAN_H /* BSDs */ # include #endif #if defined(__DARWIN) /* Darwin does something else. */ #include #endif #if defined(__SUNOS) /* Solaris and derivatives */ #include #endif #if defined(__DARWIN) #define le16toh(x) OSSwapLittleToHostInt16(x) #elif defined(__HPUX) /* HPUX is big endian (apparently..) */ #define le16toh(x) ((uint16_t)( \ (((uint16_t)(x)) << 8) | \ (((uint16_t)(x)) >> 8))) #elif defined(__SUNOS) #ifdef _BIG_ENDIAN # define le16toh(x) BSWAP_16(x) #else # define le16toh(x) (x) #endif #elif !defined(HAVE_DECL_LE16TOH) || HAVE_DECL_LE16TOH == 0 /* le16toh() is missing in glibc before 2.9 */ #if BYTE_ORDER == BIG_ENDIAN # define le16toh(x) ((uint16_t)( \ (((uint16_t)(x)) << 8) | \ (((uint16_t)(x)) >> 8))) #elif BYTE_ORDER == LITTLE_ENDIAN # define le16toh(x) (x) #else /* BYTE_ORDER == */ # error Unknown endianness #endif #endif /* !HAVE_DECL_LE16TOH */ #endif /* ENDIANSHIM_H */ sipp-3.7.2/include/fileutil.h0000664000000000000000000000161014525516253013005 0ustar /* * 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 */ #ifndef FILEUTIL_H #define FILEUTIL_H #ifdef __cplusplus extern "C" { #endif char* find_file(const char* filename); #ifdef __cplusplus } #endif #endif /* FILEUTIL_H */ sipp-3.7.2/include/infile.hpp0000664000000000000000000000336514525516253013007 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #ifndef __FILECONTENTS__ #define __FILECONTENTS__ #include class FileContents { public: FileContents(const char *file); int getLine(int line, char *dest, int len); int getField(int line, int field, char *dest, int len); int numLines(); int nextLine(int userId); void dump(); void index(int field); int lookup(char *key); void insert(char *value); void replace(int line, char *value); private: void reIndex(int line); void deIndex(int line); typedef enum { InputFileSequentialOrder = 0, InputFileRandomOrder, InputFileUser } InputFileUsage; int usage; int lineCounter; std::vector fileLines; const char *fileName; bool printfFile; int printfOffset; int printfMultiple; int numLinesInFile; int realLinesInFile; int indexField; str_int_map *indexMap; }; #endif sipp-3.7.2/include/jlsrtp.hpp0000664000000000000000000012110014525516253013043 0ustar /* * 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 * * Author: Jeannot Langlois (jeannot.langlois@gmail.com) -- 2016-2020 */ #ifndef __JLSRTP__ #define __JLSRTP__ #if defined(USE_OPENSSL) #include #include #include #include #elif defined(USE_WOLFSSL) #include #include #include #include #endif #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) #include #include #define JLSRTP_VERSION 0.5 #define JLSRTP_ENCRYPTION_KEY_LENGTH 16 // bytes #define JLSRTP_SALTING_KEY_LENGTH 14 // bytes #define JLSRTP_AUTHENTICATION_KEY_LENGTH 20 // bytes #define JLSRTP_MAX_SEQUENCE_NUMBERS 65536 #define JLSRTP_PSEUDORANDOM_BITS 128 #define JLSRTP_KEY_ENCRYPTION_LABEL 0x00 #define JLSRTP_KEY_AUTHENTICATION_LABEL 0x01 #define JLSRTP_KEY_SALTING_LABEL 0x02 #define JLSRTP_SHA1_HASH_LENGTH 20 #define JLSRTP_SRTP_DEFAULT_HEADER_SIZE 12 // bytes #define JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80 10 // bytes #define JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32 4 // bytes static const std::string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; typedef struct _CryptoContextID { unsigned int ssrc; // SSRC std::string address; // IP address unsigned short port; // port } CryptoContextID; typedef struct _AESState { unsigned char ivec[AES_BLOCK_SIZE]; // ivec[0..13] (high-order bytes): 'IV' / ivec[14..15] (low-order bytes): 'counter' unsigned int num; // block byte offset unsigned char ecount[AES_BLOCK_SIZE]; // encrypted ivec EVP_CIPHER_CTX* cipher; // Cipher context } AESState; typedef union _Conversion32 { unsigned long i; unsigned char c[4]; } Conversion32; typedef union _Conversion64 { unsigned long long i; unsigned char c[8]; } Conversion64; typedef enum _CipherType { AES_CM_128, NULL_CIPHER, INVALID_CIPHER } CipherType; typedef enum _HashType { HMAC_SHA1_80, HMAC_SHA1_32, NULL_HASH, INVALID_HASH } HashType; typedef struct _CryptoAttribute { CipherType cipher_algorithm; HashType hmac_algorithm; bool MKI; unsigned int MKI_length; unsigned long active_MKI; std::vector master_key; unsigned long master_key_counter; unsigned short n_e; unsigned short n_a; std::vector master_salt; unsigned long master_key_derivation_rate; unsigned long master_mki_value; unsigned short n_s; unsigned int tag; } CryptoAttribute; typedef enum _ActiveCrypto { PRIMARY_CRYPTO, SECONDARY_CRYPTO, ACTIVE_CRYPTO, INVALID_CRYPTO } ActiveCrypto; class JLSRTP { private: CryptoContextID _id; unsigned long _ROC; unsigned short _s_l; CryptoAttribute _primary_crypto; CryptoAttribute _secondary_crypto; ActiveCrypto _active_crypto; std::vector _session_enc_key; std::vector _session_salt_key; std::vector _session_auth_key; std::vector _packetIV; AESState _pseudorandomstate; AESState _cipherstate; unsigned int _srtp_header_size; unsigned int _srtp_payload_size; /** * isBase64 * * Checks whether the given character satisfies base64 criterias (true) or not (false) * * @param[in] c Unsigned character to examine * * @return TRUE Given character satisfies base64 criterias * @return FALSE Given character DOES NOT satisfy base64 criterias */ bool isBase64(unsigned char c); /** * resetPseudoRandomState * * Resets the state of the AES counter mode pseudo random function * * @param[in] iv Input vector to use * * @return 0 SUCCESS * @return -1 FAILURE */ int resetPseudoRandomState(std::vector iv); /** * pseudorandomFunction * * Generates the given number of key stream bits from the given master key and input vector * * @param[in] iv Input vector to use * @param[in] n Number of keystream bits to generate * @param[out] output Generated keystream bits * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length * @return -2 FAILURE -- Incorrect encryption key length * @return -3 FAILURE -- Could not set encryption key * @return -4 FAILURE -- Invalid crypto attribute specified */ int pseudorandomFunction(std::vector iv, int n, std::vector &output); /** * shiftVectorLeft * * Shifts a given vector to the left by a predetermined number of bytes * * @param[out] shifted_vec Shifted vector * @param[in] original_vec Original vector to shift * @param[in] shift_value Number of bytes to shift original vector by * * @return 0 SUCCESS * @return -1 FAILURE */ int shiftVectorLeft(std::vector &shifted_vec, std::vector &original_vec, int shift_value); /** * shiftVectorRight * * Shifts a given vector to the right by a predetermined number of bytes * * @param[out] shifted_vec Shifted vector * @param[in] original_vec Original vector to shift * @param[in] shift_value Number of bytes to shift original vector by * * @return 0 SUCCESS * @return -1 FAILURE */ int shiftVectorRight(std::vector &shifted_vec, std::vector &original_vec, int shift_value); /** * xorVector * * Performs bitwise exclusive-OR operation between the two given vectors * * @param[in] a First vector to use for exclusive-OR operation * @param[in] b Second vector to use for exclusive-OR operation * @param[out] result Result of exclusive-OR operation * * @return 0 SUCCESS * @return -1 FAILURE -- Input vector sizes do not match */ int xorVector(std::vector &a, std::vector &b, std::vector &result); /** * isBigEndian * * Checks whether the current machine uses BIG ENDIAN byte ordering or not * * @return 1 Current machine uses BIG ENDIAN byte ordering * @return 0 Current machine does NOT use BIG ENDIAN byte ordering */ int isBigEndian(); /** * isLittleEndian * * Checks whether the current machine uses LITTLE ENDIAN byte ordering or not * * @return 1 Current machine uses LITTLE ENDIAN byte ordering * @return 0 Current machine does NOT use LITTLE ENDIAN byte ordering */ int isLittleEndian(); /** * convertSsrc * * Converts the given numeric 32-bit ssrc to its vector version * * @param[in] ssrc Numerical SSRC to convert * @param[out] result Vector-based SSRC * * @return 0 SUCCESS * @return -1 FAILURE */ int convertSsrc(unsigned long ssrc, std::vector &result); /** * convertPacketIndex * * Converts the given numeric 48-bit packet index to its vector version * * @param[in] ssrc Numerical packet index to convert * @param[out] result Vector-based packet index * * @return 0 SUCCESS * @return -1 FAILURE */ int convertPacketIndex(unsigned long long i, std::vector &result); /** * convertROC * * Converts the given numeric 32-bit roll-over-counter to its vector version * * @param[in] roc Numerical roll-over-counter to convert * @param[out] result Vector-based roll-over-counter * * @return 0 SUCCESS * @return -1 FAILURE */ int convertROC(unsigned long ROC, std::vector &result); /** * determineV * * Determines new ROC from existing ROC / s_l / SEQ values * * @param[in] SEQ Packet sequence number * * @return Updated ROC based on existing ROC / s_l / SEQ values */ unsigned long determineV(unsigned short SEQ); /** * updateRollOverCounter * * Updates ROC with given value v * * @param[in] v Value to update ROC with * * @return TRUE SUCCESS * @return FALSE FAILURE */ bool updateRollOverCounter(unsigned long v); /** * fetchRollOverCounter * * Fetches current ROC value * * @return */ unsigned long fetchRollOverCounter(); /** * updateSL * * Updates s_l with given value s * * @param[in] s Value to update s_l with * * @return TRUE SUCCESS * @return FALSE FAILURE */ bool updateSL(unsigned short s); /** * fetchSL * * Fetches current s_l value * * @return */ unsigned short fetchSL(); /** * determinePacketIndex * * Determine index of packet from ROC and SEQ * * @param[in] ROC RollOverCounter * @param[in] SEQ Packet sequence number * * @return Packet index based on ROC and SEQ */ unsigned long long determinePacketIndex(unsigned long ROC, unsigned short SEQ); /** * setPacketIV * * Sets the current computed packet IV into the cipher state prior to encryption/decryption of a packet * * @return 0 SUCCESS * @return -1 FAILURE */ int setPacketIV(); /** * computePacketIV * * Computes the Input Vector for the given session salting key / ssrc / packet index * * @param[in] i Packet index to use * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length */ int computePacketIV(unsigned long long i); /** * displayPacketIV * * Displays the current computed packet Input Vector */ void displayPacketIV(); /** * encryptVector * * Encrypts the given plaintext input vector into the ciphertext output one using selected session encryption key * * @param[in] invdata Input plaintext vector * @param[out] ciphertext_output Output ciphertext vector * * @return 0 SUCCESS * @return -1 FAILURE -- Empty input data vector * @return -3 FAILURE -- Invalid cipher type specified * @return -4 FAILURE -- Invalid crypto attribute specified */ int encryptVector(std::vector &invdata, std::vector &ciphertext_output); /** * decryptVector * * Decrypts the given ciphertext input vector into the plaintext output one using selected session decryption key * * @param[in] ciphertext_input Input ciphertext vector * @param[out] outvdata Output plaintext vector * * @return 0 SUCCESS * @return -1 FAILURE -- Empty input data vector * @return -3 FAILURE -- Invalid cipher type specified * @return -4 FAILURE -- Invalid crypto attribute specified */ int decryptVector(std::vector &ciphertext_input, std::vector &outvdata); /** * issueAuthenticationTag * * Issues a SHA1 hash of a given bit length from the provided data using the given authentication key * * @param[in] data Data to hash * @param[out] hash Hash * * @return 0 SUCCESS * @return -1 FAILURE -- Empty session authentication key * @return -2 FAILURE -- Internal error generating digest * @return -3 FAILURE -- Invalid HMAC algorithm specified * @return -4 FAILURE -- Internal error converting ROC * @return -5 FAILURE -- Invalid crypto attribute specified */ int issueAuthenticationTag(std::vector &data, std::vector &hash); /** * extractAuthenticationTag * * Extracts SHA1 hash of a given bit length from the provided SRTP packet * * @param[in] srtp_packet SRTP packet to extract SHA1 hash from * @param[out] hash Hash * * @return 0 SUCCESS * @return -1 FAILURE -- Empty session authentication key * @return -2 FAILURE -- Given SRTP packet smaller than authentication tag size * @return -3 FAILURE -- Invalid HMAC algorithm specified * @return -4 FAILURE -- Invalid crypto attribute specified */ int extractAuthenticationTag(std::vector srtp_packet, std::vector &hash); /** * extractSRTPHeader * * Extracts the SRTP header from the provided SRTP packet * * @param[in] srtp_packet SRTP packet to extract SRTP header from * @param[out] header SRTP header * * @return 0 SUCCESS * @return -1 FAILURE -- SRTP header size is ZERO * @return -2 FAILURE -- Given SRTP packet smaller than SRTP header size */ int extractSRTPHeader(std::vector srtp_packet, std::vector &header); /** * extractSRTPPayload * * Extracts the SRTP payload from the provided SRTP packet * * @param[in] srtp_packet SRTP packet to extract SRTP payload from * @param[out] payload SRTP payload * * @return 0 SUCCESS * @return -1 FAILURE -- SRTP header size is ZERO * @return -2 FAILURE -- SRTP payload size is ZERO * @return -3 FAILURE -- Given SRTP packet smaller than SRTP header+payload size */ int extractSRTPPayload(std::vector srtp_packet, std::vector &payload); /** * base64Encode * * Encodes the given bytes to a base64 string * * @param[in] s Decoded bytes to encode * * @return Encoded base64 string */ std::string base64Encode(std::vector const& s); /** * base64Decode * * Decodes the given base64 string to bytes * * @param[in] s Encoded base64 string to decode * * @return Decoded bytes */ std::vector base64Decode(std::string const& s); /** * resetCipherBlockOffset * * Resets the block offset of the AES counter mode encryption/decryption cipher * * @return 0 SUCCESS * @return -1 FAILURE */ int resetCipherBlockOffset(); /** * resetCipherOutputBlock * * Resets the output block of the AES counter mode encryption/decryption cipher * * @return 0 SUCCESS * @return -1 FAILURE */ int resetCipherOutputBlock(); /** * resetCipherBlockCounter * * Resets the block counter of the AES counter mode encryption/decryption cipher * * @return 0 SUCCESS * @return -1 FAILURE */ int resetCipherBlockCounter(); /** * setAESPseudoRandomFunctionKey * * Sets the AES key used by the pseudorandom function * * @param[in] crypto_attrib Crypto attribute whose pseudo random function key is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE -- Pseudorandom function EVP_CIPHER_CTX context is NULL * @return -2 FAILURE -- Internal EVP_EncryptInit_ex() error encountered while attempting to set AES key for primary crypto pseudorandom function * @return -3 FAILURE -- Internal EVP_EncryptInit_ex() error encountered while attempting to set AES key for secondary crypto pseudorandom function * @return -4 FAILURE -- Invalid crypto attribute specified */ int setAESPseudoRandomFunctionKey(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * setAESSessionEncryptionKey * * Sets the AES key used by the session encryption function * * @return 0 SUCCESS * @return -1 FAILURE -- Session encryption EVP_CIPHER_CTX context is NULL * @return -2 FAILURE -- Internal EVP_EncryptInit_ex() error encountered while attempting to set AES key for session encryption function */ int setAESSessionEncryptionKey(); /** * AES_ctr128_increment * * Increments the given AES counter * * @param[in] counter AES counter to increment */ void AES_ctr128_increment(unsigned char* counter); /** * AES_ctr128_pseudorandom_EVPencrypt * * Encrypts given input using AES 128-bit counter mode algorithm for pseudorandom function context * * @param[in] in Input plaintext * @param[out] out Output ciphertext * @param[in] length Input/output size * @param[in] counter Concatenated IV/counter array * @param[in] ecount_buf Encrypted IV/counter * @param[in] num Block byte offset * * @return 0 SUCCESS * @return -1 FAILURE -- Internal EVP_EncryptInit_ex() error encountered * @return -2 FAILURE -- Internal EVP_EncryptUpdate() error encountered * @return -3 FAILURE -- Invalid crypto attribute specified */ int AES_ctr128_pseudorandom_EVPencrypt(const unsigned char* in, unsigned char* out, const unsigned long length, unsigned char counter[AES_BLOCK_SIZE], unsigned char ecount_buf[AES_BLOCK_SIZE], unsigned int* num); /** * AES_ctr128_session_EVPencrypt * * Encrypts given input using AES 128-bit counter mode algorithm for session encryption context * * @param[in] in Input plaintext * @param[out] out Output ciphertext * @param[in] length Input/output size * @param[in] counter Concatenated IV/counter array * @param[in] ecount_buf Encrypted IV/counter * @param[in] num Block byte offset * * @return 0 SUCCESS * @return -1 FAILURE -- Internal EVP_EncryptInit_ex() error encountered * @return -2 FAILURE -- Internal EVP_EncryptUpdate() error encountered */ int AES_ctr128_session_EVPencrypt(const unsigned char* in, unsigned char* out, const unsigned long length, unsigned char counter[AES_BLOCK_SIZE], unsigned char ecount_buf[AES_BLOCK_SIZE], unsigned int* num); public: /** * resetCryptoContext * * Resets crypto context * * @param[in] sssrc SSRC ID * @param[in] ipAddress IP address * @param[in] port Port */ void resetCryptoContext(unsigned int ssrc, std::string ipAddress, unsigned short port); /** * resetCipherState * * Resets the state of the AES counter mode encryption/decryption cipher * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length */ int resetCipherState(); /** * deriveSessionEncryptionKey * * Derives the session encryption key from the given master key / master salt * * @li Assumes the key derivation rate is ZERO * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length * @return -2 FAILURE -- Incorrect encryption key length * @return -3 FAILURE -- Could not set encryption key * @return -4 FAILURE -- Invalid crypto attribute specified */ int deriveSessionEncryptionKey(); /** * deriveSessionSaltingKey * * Derives the session salting key from the given master key / master salt * * @li Assumes the key derivation rate is ZERO * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length * @return -2 FAILURE -- Incorrect encryption key length * @return -3 FAILURE -- Could not set encryption key * @return -4 FAILURE -- Invalid crypto attribute specified */ int deriveSessionSaltingKey(); /** * deriveSessionAuthenticationKey * * Derives the session authentication key from the given master key / master salt * * @li Assumes the key derivation rate is ZERO * * @return 0 SUCCESS * @return -1 FAILURE -- Incorrect salting key length * @return -2 FAILURE -- Incorrect encryption key length * @return -3 FAILURE -- Could not set encryption key * @return -4 FAILURE -- Invalid crypto attribute specified */ int deriveSessionAuthenticationKey(); /** * displaySessionEncryptionKey * * Displays the session encryption key */ void displaySessionEncryptionKey(); /** * displaySessionSaltingKey * * Displays the session salting key */ void displaySessionSaltingKey(); /** * displaySessionAuthenticationKey * * Displays the session authentication key */ void displaySessionAuthenticationKey(); /** * selectEncryptionKey * * Selects the session key used for encryption * * @return 0 SUCCESS * @return -1 FAILURE -- Empty session encryption key * @return -2 FAILURE -- Could not set encryption key */ int selectEncryptionKey(); /** * selectDecryptionKey * * Selects the session key used for decryption * * @return 0 SUCCESS * @return -1 FAILURE -- Empty session encryption key * @return -2 FAILURE -- Could not set encryption key */ int selectDecryptionKey(); /** * getCipherAlgorithm * * Gets the cipher algorithm currently in use * * @param[in] crypto_attrib Crypto attribute whose cipher algorithm is to be obtained (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return Cipher algorithm currently in use (AES_CM_128 or NULL_CIPHER) */ CipherType getCipherAlgorithm(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * selectCipherAlgorithm * * Selects the cipher algorithm to use * * @param[in] cipherType Cipher algorithm to use (AES_CM_128 or NULL_CIPHER) * @param[in] crypto_attrib Crypto attribute whose cipher algorithm is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE -- Invalid cipher algorithm specified * @return -2 FAILURE -- Invalid crypto attribute specified */ int selectCipherAlgorithm(CipherType cipherType, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getHashAlgorithm * * Gets the hashing algorithm currently in use * * @param[in] crypto_attrib Crypto attribute whose hash algorithm is to be obtained (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return Hashing algorithm currently in use (HMAC_SHA1_80 or HMAC_SHA1_32) */ HashType getHashAlgorithm(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * selectHashAlgorithm * * Selects the hashing algorithm to use * * @param[in] hashType Hashing algorithm to use (HMAC_SHA1_80 or HMAC_SHA1_32) * @param[in] crypto_attrib Crypto attribute whose hash algorithm is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE -- Invalid HMAC algorithm specified * @return -2 FAILURE -- Invalid crypto attribute specified */ int selectHashAlgorithm(HashType hashType, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getAuthenticationTagSize * * Gets the authentication tag size * * @return >0 SUCCESS -- Authentication tag size (bytes) * @return -1 FAILURE -- Empty session authentication key * @return -2 FAILURE -- Invalid HMAC algorithm specified * @return -3 FAILURE -- Invalid crypto attribute specified */ int getAuthenticationTagSize(); /** * displayAuthenticationTag * * Displays the given authentication tag * * @param[in] authtag Authentication tag to display */ void displayAuthenticationTag(std::vector &authtag); /** * getSSRC * * Gets the SSRC currently in use * * @return SSRC ID currently in use */ unsigned int getSSRC(); /** * getIPAddress * * Gets the IP address currently in use * * @return IP address currently in use */ std::string getIPAddress(); /** * getPort * * Gets the port currently in use * * @return Port currently in use */ unsigned short getPort(); /** * setSSRC * * Sets the SSRC currently in use * * @param[in] ssrc SSRC ID currently in use */ void setSSRC(unsigned int ssrc); /** * setIPAddress * * Sets the IP address currently in use * * @param[in] ipAddress IP address currently in use */ void setIPAddress(std::string ipAddress); /** * setPort * * Sets the port currently in use * * @param[in] port Port currently in use */ void setPort(unsigned short port); /** * setID * * Sets the cryptographic context ID () to use * * @param[in] id Cryptograhic context ID () to use */ void setID(CryptoContextID id); /** * getSrtpHeaderSize * * Gets the current SRTP header size * * @return Current SRTP header size in use */ unsigned int getSrtpHeaderSize(); /** * setSrtpHeaderSize * * Sets the current SRTP header size * * @param[in] size Current SRTP header size to use */ void setSrtpHeaderSize(unsigned int size); /** * getSrtpPayloadSize * * Gets the current SRTP payload size * * @return Current SRTP payload size in use */ unsigned int getSrtpPayloadSize(); /** * setSrtpPayloadSize * * Sets the current SRTP payload size * * @param[in] size Current SRTP payload size to use */ void setSrtpPayloadSize(unsigned int size); /** * processOutgoingPacket * * Processes an outgoing RTP packet (encrypt+authenticate) to become an SRTP packet * * @param[in] SEQ_s Input RTP packet sequence number * @param[in] rtp_header Input RTP header * @param[in] rtp_payload Input RTP payload * @param[out] srtp_packet Output SRTP packet * * @return 0 SUCCESS * @return -1 ENCRYPTION_FAILURE * @return -2 Error issuing authentication tag * @return -3 Error encountered while computing packet IV * @return -4 Error encountered while setting packet IV * @return -5 Error updating rollover counter * @return -6 Error updating SL */ int processOutgoingPacket(unsigned short SEQ_s, std::vector &rtp_header, std::vector &rtp_payload, std::vector &srtp_packet); /** * processIncomingPacket * * Processes an incoming SRTP packet (authenticate+decrypt) to become an RTP packet * * @param[in] SEQ_r Input SRTP packet sequence number * @param[in] srtp_packet Input SRTP packet * @param[out] rtp_header Output RTP header * @param[out] rtp_payload Output RTP payload * * @return 0 SUCCESS * @return -1 AUTHENTICATION_FAILURE * @return -2 DECRYPTION_FAILURE * @return -3 Error extracting authentication tag * @return -4 Error extracting SRTP header * @return -5 Error extracting SRTP payload * @return -6 Error issuing authentication tag * @return -7 Error encountered while computing packet IV * @return -8 Error encoutnered while setting packet IV * @return -9 Error updating rollover counter * @return -10 Error updating SL */ int processIncomingPacket(unsigned short SEQ_r, std::vector &srtp_packet, std::vector &rtp_header, std::vector &rtp_payload); /** * setCryptoTag * * Sets the crypto tag parameter value * * @param[in] tag Crypto tag value to use * @param[in] crypto_attrib Crypto attribute whose tag is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int setCryptoTag(unsigned int tag, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getCryptoTag * * Gets the crypto tag parameter value * * @param[in] crypto_attrib Crypto attribute whose tag is to be obtained (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return Crypto tag value currently in use */ unsigned int getCryptoTag(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getCryptoSuite * * Fetches the string description of the crypto suite currently in use (e.g. "AES_CM_128_HMAC_SHA1_80" or "AES_CM_128_HMAC_SHA1_32") * * @return String description of the crypto suite currently in use */ std::string getCryptoSuite(); /** * encodeMasterKeySalt * * Encodes the current unencoded master key/salt of the context for use in a RFC4568-compliant crypto attribute * * @param[out] mks Encoded master key/salt string (for use in an RFC-4568-compliant crypto attribute) * @param[in] crypto_attrib Crypto attribute whose master key/salt is to be encoded (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int encodeMasterKeySalt(std::string &mks, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * decodeMasterKeySalt * * Decodes the given encoded master key/salt string from an RFC4568-compliant crypto attribute for use in the context * * @param[in] mks Encoded RFC4568-compliant master key/salt value (for use in the context) * @param[in] crypto_attrib Crypto attribute whose master key/salt is to be decoded (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int decodeMasterKeySalt(std::string &mks, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * displayCryptoContext * * Displays current CryptoContext */ void displayCryptoContext(); /** * dumpCryptoContext */ std::string dumpCryptoContext(); /** * generateMasterKey * * Generates a master key * * @param[in] crypto_attrib Crypto attribute whose master key is to be generated (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int generateMasterKey(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * generateMasterSalt * * Generates a master salt * * @param[in] crypto_attrib Crypto attribute whose master salt is to be generated (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int generateMasterSalt(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getMasterKey * * Gets the MASTER KEY * * @param[in] crypto_attrib Crypto attribute whose master key is to be fetched (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return Master key */ std::vector getMasterKey(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * getMasterSalt * * Gets the MASTER SALT * * @param[in] crypto_attrib Crypto attribute whose master salt is to be fetched (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return Master salt */ std::vector getMasterSalt(ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * setMasterKey * * Sets the MASTER KEY * * @param[in] key Master key to use * @param[in] crypto_attrib Crypto attribute whose master key is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int setMasterKey(std::vector &key, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * setMasterSalt * * Sets the MASTER SALT * * @param[in] salt Master salt to use * @param[in] crypto_attrib Crypto attribute whose master salt is to be set (PRIMARY_CRYPTO, SECONDARY_CRYPTO or ACTIVE_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int setMasterSalt(std::vector &salt, ActiveCrypto crypto_attrib = ACTIVE_CRYPTO); /** * swapCrypto * * Swaps the PRIMARY and SECONDARY crypto attributes * * @return 0 SUCCESS * @return -1 FAILURE */ int swapCrypto(); /** * selectActiveCrypto * * Selects the crypto attribute to use (PRIMARY or SECONDARY) * * @param[in] activeCrypto Crypto attribute to use (PRIMARY_CRYPTO or SECONDARY_CRYPTO) * * @return 0 SUCCESS * @return -1 FAILURE */ int selectActiveCrypto(ActiveCrypto activeCrypto); /** * getActiveCrypto * * Gets the crypto attribute currently in use (PRIMARY or SECONDARY) * * @return PRIMARY_CRYPTO Primary crypto attribute is currently in use * @return SECONDARY_CRYPTO Secondary crypto attribute is currently in use * @return INVALID_CRYPTO Internal error occurred */ ActiveCrypto getActiveCrypto(); /** * operator= * * Assigns a given JLSRTP object to the implicit one * * @param[in] that JLSRTP object to assign to the implicit one * * @return Reference to the implicit JLSRTP object */ JLSRTP& operator=(const JLSRTP& that); /** * operator== * * Compares a given JLSRTP object to the implicit one for equality * * @param[in] that JLSRTP object to compare to the implicit one for equality * * @return TRUE Given JLSRTP object is equal to the implicit one * @return FALSE Given JLSRTP object is NOT equal to the implicit one */ bool operator==(const JLSRTP& that); /** * operator!= * * Compares a given JLSRTP object to the implicit one for inequality * * @param[in] that JLSRTP object to compare to the implicit one for inequality * * @return TRUE Given JLSRTP object is inequal to the implicit one * @return FALSE Given JLSRTP object is NOT inequal to the implicit one */ bool operator!=(const JLSRTP& that); /** * JLSTRP * * Default constructor */ JLSRTP(); /** * JLSRTP * * Custom constructor * * @param[in] sssrc SSRC ID * @param[in] ipAddress IP address * @param[in] port Port */ JLSRTP(unsigned int ssrc, std::string ipAddress, unsigned short port); /** * ~JLSRTP * * Default destructor */ ~JLSRTP(); }; #else // !USE_OPENSSL && !USE_WOLFSSL class JLSRTP { public: /** * JLSTRP * * Default constructor */ JLSRTP(); /** * ~JLSRTP * * Default destructor */ ~JLSRTP(); }; #endif // USE_OPENSSL || USE_WOLFSSL #endif // __JLSRTP__ sipp-3.7.2/include/listener.hpp0000664000000000000000000000275514525516253013370 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #ifndef __LISTENER__ #define __LISTENER__ #include #include #include #include #include #include #include "sipp.hpp" class listener { public: listener(const char *id, bool listening); virtual ~listener(); char *getId(); virtual bool process_incoming(const char* msg, const struct sockaddr_storage* src) = 0; virtual bool process_twinSippCom(char* msg) = 0; protected: void startListening(); void stopListening(); char *id; bool listening; }; typedef std::map listener_map; listener * get_listener(const char *); #endif sipp-3.7.2/include/logger.hpp0000664000000000000000000000576014525516253013021 0ustar #ifndef __SIPP_LOGGER_H__ #define __SIPP_LOGGER_H__ /************************** Trace Files ***********************/ #include #include "sipp.hpp" #ifdef GLOBALS_FULL_DEFINITION #define MAYBE_EXTERN #define DEFVAL(value) = value #else #define MAYBE_EXTERN extern #define DEFVAL(value) #endif MAYBE_EXTERN FILE * screenf DEFVAL(0); MAYBE_EXTERN FILE * countf DEFVAL(0); MAYBE_EXTERN FILE * codesf DEFVAL(0); //MAYBE_EXTERN FILE * timeoutf DEFVAL(0); MAYBE_EXTERN bool useMessagef DEFVAL(0); MAYBE_EXTERN bool useErrorCodesf DEFVAL(0); MAYBE_EXTERN bool useCallDebugf DEFVAL(0); MAYBE_EXTERN bool useShortMessagef DEFVAL(0); MAYBE_EXTERN bool useScreenf DEFVAL(0); MAYBE_EXTERN bool useLogf DEFVAL(0); //MAYBE_EXTERN bool useTimeoutf DEFVAL(0); MAYBE_EXTERN bool dumpInFile DEFVAL(0); MAYBE_EXTERN bool dumpInRtt DEFVAL(0); MAYBE_EXTERN bool useCountf DEFVAL(0); MAYBE_EXTERN char * slave_cfg_file; MAYBE_EXTERN unsigned long long max_log_size DEFVAL(0); MAYBE_EXTERN unsigned long long ringbuffer_size DEFVAL(0); MAYBE_EXTERN int ringbuffer_files DEFVAL(0); MAYBE_EXTERN char screen_last_error[32768]; MAYBE_EXTERN char screen_logfile[MAX_PATH] DEFVAL(""); /* Log Rotation Functions. */ struct logfile_id { time_t start; int n; }; struct logfile_info { const char *name; bool check; FILE *fptr; int nfiles; struct logfile_id *ftimes; char file_name[MAX_PATH]; bool overwrite; bool fixedname; time_t starttime; unsigned int count; }; void print_header_line(FILE *f); void print_bottom_line(FILE *f, int last); void print_variable_list(); void print_tdm_map(); void print_screens(void); void log_off(struct logfile_info *lfi); #ifdef GLOBALS_FULL_DEFINITION #define LOGFILE(name, s, check) \ struct logfile_info name = { s, check, NULL, 0, NULL, "", true, false, 0, 0 } #else #define LOGFILE(name, s, check) \ extern struct logfile_info name #endif LOGFILE(calldebug_lfi, "calldebug", true); LOGFILE(message_lfi, "messages", true); LOGFILE(screen_lfi, "screen", true); LOGFILE(shortmessage_lfi, "shortmessages", true); LOGFILE(log_lfi, "logs", true); LOGFILE(error_lfi, "errors", false); void rotate_logfile(); void rotate_shortmessagef(); void rotate_errorf(); void rotate_messagef(); void rotate_screenf(); void rotate_calldebugf(); /* Functions that print to stdout/stderr/logfiles. * Any functions which print to the curses UI live in screen.cpp. * */ void print_errors(); void print_closing(); void print_count_file(FILE* f, int header); void print_error_codes_file(FILE* f); int TRACE_MSG(const char* fmt, ...) __attribute__((format(printf, 1, 2))); int TRACE_CALLDEBUG(const char* fmt, ...) __attribute__((format(printf, 1, 2))); int TRACE_SHORTMSG(const char* fmt, ...) __attribute__((format(printf, 1, 2))); int LOG_MSG(const char* fmt, ...) __attribute__((format(printf, 1, 2))); #endif /* __SIPP_LOGGER_H__ */ sipp-3.7.2/include/md5.h0000664000000000000000000000666214525516253011671 0ustar /* * Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * L. Peter Deutsch * ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* * Independent implementation of MD5 (RFC 1321). * * This code implements the MD5 Algorithm defined in RFC 1321, whose * text is available at http://www.ietf.org/rfc/rfc1321.txt . * * The code is derived from the text of the RFC, including the test suite * (section A.5) but excluding the rest of Appendix A. It does not include * any code or documentation that is identified in the RFC as being * copyrighted. * * The original and principal author of md5.h is L. Peter Deutsch * . Other authors are noted in the change history * that follows (in reverse chronological order): * * 2002-04-13 lpd Removed support for non-ANSI compilers; removed * references to Ghostscript; clarified derivation from RFC 1321; * now handles byte order either statically or dynamically. * 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. * 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); * added conditionalization for C++ compilation from Martin * Purschke . * 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ sipp-3.7.2/include/message.hpp0000664000000000000000000001333114525516253013157 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Andy Aicken */ #ifndef __MESSAGE__ #define __MESSAGE__ #include class scenario; struct MessageComponent; typedef enum { E_Message_Literal, E_Message_Remote_IP, E_Message_Remote_Host, E_Message_Remote_Port, E_Message_Transport, E_Message_Local_IP, E_Message_Local_IP_Type, E_Message_Local_Port, E_Message_Server_IP, E_Message_Media_IP, E_Message_Media_Port, E_Message_Media_IP_Type, E_Message_Call_Number, E_Message_DynamicId, // general usage, global, autoincrementing and wrapping counter E_Message_Call_ID, E_Message_CSEQ, E_Message_PID, E_Message_Service, E_Message_Branch, E_Message_Index, E_Message_Next_Url, E_Message_Len, E_Message_Peer_Tag_Param, E_Message_Routes, E_Message_Variable, E_Message_Fill, E_Message_Injection, E_Message_Last_Header, E_Message_Last_Request_URI, E_Message_Last_CSeq_Number, E_Message_Last_Message, E_Message_TDM_Map, E_Message_Authentication, E_Message_ClockTick, E_Message_Users, E_Message_UserID, E_Message_Timestamp, E_Message_Date, E_Message_SippVersion, E_Message_File, E_Message_Custom, E_Message_RTPStream_Audio_Port, E_Message_RTPStream_Video_Port, #ifdef USE_TLS E_Message_CryptoTag1Audio, E_Message_CryptoTag2Audio, E_Message_CryptoSuiteAesCm128Sha1801Audio, E_Message_CryptoSuiteAesCm128Sha1802Audio, E_Message_CryptoSuiteAesCm128Sha1321Audio, E_Message_CryptoSuiteAesCm128Sha1322Audio, E_Message_CryptoKeyParams1Audio, E_Message_CryptoKeyParams2Audio, E_Message_CryptoTag1Video, E_Message_CryptoTag2Video, E_Message_CryptoSuiteAesCm128Sha1801Video, E_Message_CryptoSuiteAesCm128Sha1802Video, E_Message_CryptoSuiteAesCm128Sha1321Video, E_Message_CryptoSuiteAesCm128Sha1322Video, E_Message_CryptoKeyParams1Video, E_Message_CryptoKeyParams2Video, E_Message_CryptoSuiteNullSha1801Audio, E_Message_CryptoSuiteNullSha1802Audio, E_Message_CryptoSuiteNullSha1321Audio, E_Message_CryptoSuiteNullSha1322Audio, E_Message_CryptoSuiteNullSha1801Video, E_Message_CryptoSuiteNullSha1802Video, E_Message_CryptoSuiteNullSha1321Video, E_Message_CryptoSuiteNullSha1322Video, E_Message_UEAesCm128Sha1801Audio, E_Message_UEAesCm128Sha1802Audio, E_Message_UEAesCm128Sha1321Audio, E_Message_UEAesCm128Sha1322Audio, E_Message_UEAesCm128Sha1801Video, E_Message_UEAesCm128Sha1802Video, E_Message_UEAesCm128Sha1321Video, E_Message_UEAesCm128Sha1322Video, #endif // USE_TLS } MessageCompType; class SendingMessage { public: SendingMessage(scenario* msg_scenario, const char* msg, bool skip_sanity = false); ~SendingMessage(); struct MessageComponent *getComponent(int); int numComponents(); char *getMethod(); int getCode(); bool isResponse(); bool isAck(); bool isCancel(); static void parseAuthenticationKeyword(scenario *msg_scenario, struct MessageComponent *dst, char *keyword); static void freeMessageComponent(struct MessageComponent *comp); private: std::vector messageComponents; char *method; int code; bool ack; bool cancel; bool response; scenario *msg_scenario; // Get parameters from a [keyword] static void getQuotedParam(char * dest, char * src, int * len); static void getHexStringParam(char * dest, char * src, int * len); static void getKeywordParam(char * src, const char * param, char * output); }; /* Custom Keyword Function Type. */ struct MessageComponent; class call; typedef int (*customKeyword)(call *, struct MessageComponent *, char *, int); /* Custom Keyword Registration Function. */ int registerKeyword(char *keyword, customKeyword fxn); struct MessageComponent { MessageCompType type; char *literal; int literalLen; int offset; int varId; union u { /* Authentication Parameters. */ struct { SendingMessage *auth_user; SendingMessage *auth_pass; SendingMessage *aka_OP; SendingMessage *aka_AMF; SendingMessage *aka_K; } auth_param; /* Field Substitution. */ struct { char *filename; int field; SendingMessage *line; } field_param; SendingMessage *filename; customKeyword fxn; } comp_param; }; #endif sipp-3.7.2/include/milenage.h0000664000000000000000000000262614525516253012761 0ustar /*------------------------------------------------------------------- * Example algorithms f1, f1*, f2, f3, f4, f5, f5* *------------------------------------------------------------------- * * A sample implementation of the example 3GPP authentication and * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is * a byte-oriented implementation of the functions, and of the block * cipher kernel function Rijndael. * * This has been coded for clarity, not necessarily for efficiency. * * The functions f2, f3, f4 and f5 share the same inputs and have * been coded together as a single function. f1, f1* and f5* are * all coded separately. * *-----------------------------------------------------------------*/ #ifndef MILENAGE_H #define MILENAGE_H #include #ifdef __cplusplus extern "C" { #endif void f1(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_a[8], uint8_t op[16]); void f2345(uint8_t k[16], uint8_t rand[16], uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6], uint8_t op[16]); void f1star(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_s[8], uint8_t op[16]); void f5star(uint8_t k[16], uint8_t rand[16], uint8_t ak[6], uint8_t op[16]); void ComputeOPc(uint8_t op_c[16], uint8_t op[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif sipp-3.7.2/include/prepare_pcap.h0000664000000000000000000000272014525516253013634 0ustar /* * 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 * * Author : Guillaume TEISSIER from FTR&D 02/02/2006 */ #ifndef PREPARE_PCAP_H #define PREPARE_PCAP_H 1 #include #include #include #include #include typedef struct { u_char* data; u_long pktlen; struct timeval ts; int partial_check; } pcap_pkt; #define PCAP_MAXPACKET 1500 typedef struct { char* file; uint16_t base; u_long max_length; pcap_pkt* max; pcap_pkt* pkts; } pcap_pkts; #ifdef __cplusplus extern "C" { #endif int check(uint16_t*, int); uint16_t checksum_carry(int); int prepare_pkts(const char*, pcap_pkts*); int prepare_dtmf(const char*, pcap_pkts*, uint16_t start_seq_no); #ifdef __cplusplus } #endif #endif /* PREPARE_PCAP_H */ sipp-3.7.2/include/ratetask.hpp0000664000000000000000000000274114525516253013354 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #ifndef RATETASK_HPP #define RATETASK_HPP #include "task.hpp" class ratetask : public task { public: unsigned int wake(); static void initialize(); bool run(); void dump(); private: static class ratetask *instance; }; #endif sipp-3.7.2/include/reporttask.hpp0000664000000000000000000000332714525516253013735 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #ifndef REPORTTASK_HPP #define REPORTTASK_HPP #include "task.hpp" class screentask : public task { public: unsigned int wake(); static void report(bool last); static void initialize(); bool run(); void dump(); private: static class screentask *instance; }; class stattask : public task { public: unsigned int wake(); static void report(); static void initialize(); bool run(); void dump(); private: static class stattask *instance; }; #endif sipp-3.7.2/include/rijndael.h0000664000000000000000000000164014525516253012763 0ustar /*------------------------------------------------------------------- * Example algorithms f1, f1*, f2, f3, f4, f5, f5* *------------------------------------------------------------------- * * A sample implementation of the example 3GPP authentication and * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is * a byte-oriented implementation of the functions, and of the block * cipher kernel function Rijndael. * * This has been coded for clarity, not necessarily for efficiency. * * The functions f2, f3, f4 and f5 share the same inputs and have * been coded together as a single function. f1, f1* and f5* are * all coded separately. * *-----------------------------------------------------------------*/ #ifndef RIJNDAEL_H #define RIJNDAEL_H #include void RijndaelKeySchedule(uint8_t key[16]); void RijndaelEncrypt(uint8_t input[16], uint8_t output[16]); #endif sipp-3.7.2/include/rtpstream.hpp0000664000000000000000000002262514525516253013562 0ustar /* * 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 * * Author : Deon van der Westhuysen - June 2012 - Vodacom PTY LTD */ #ifndef __RTPSTREAM__ #define __RTPSTREAM__ #include "jlsrtp.hpp" #include #define RTPSTREAM_MAX_FILENAMELEN 256 #define RTPSTREAM_MAX_PAYLOADNAME 256 #define RTPECHO_MAX_FILENAMELEN 256 #define RTPECHO_MAX_PAYLOADNAME 256 #ifdef USE_TLS typedef struct _SrtpAudioInfoParams { bool audio_found; int primary_audio_cryptotag; char primary_audio_cryptosuite[25]; char primary_audio_cryptokeyparams[42]; int secondary_audio_cryptotag; char secondary_audio_cryptosuite[25]; char secondary_audio_cryptokeyparams[42]; bool primary_unencrypted_audio_srtp; bool secondary_unencrypted_audio_srtp; } SrtpAudioInfoParams; typedef struct _SrtpVideoInfoParams { bool video_found; int primary_video_cryptotag; char primary_video_cryptosuite[25]; char primary_video_cryptokeyparams[42]; int secondary_video_cryptotag; char secondary_video_cryptosuite[25]; char secondary_video_cryptokeyparams[42]; bool primary_unencrypted_video_srtp; bool secondary_unencrypted_video_srtp; } SrtpVideoInfoParams; #endif // USE_TLS struct threaddata_t; struct taskentry_t; struct taskentry_t { threaddata_t *parent_thread; unsigned long nextwake_ms; volatile int flags; /* rtp stream information */ unsigned long long last_audio_timestamp; unsigned long long last_video_timestamp; unsigned short audio_seq_out; unsigned short video_seq_out; char audio_payload_type; char video_payload_type; unsigned int audio_ssrc_id; unsigned int video_ssrc_id; /* current playback information */ int audio_pattern_id; // FILE: -1 (UNUSED) -- PATTERN: int video_pattern_id; // FILE: -1 (UNUSED) -- PATTERN: int audio_loop_count; // FILE: -- PATTERN: -1 (UNUSED) int video_loop_count; // FILE: -- PATTERN: -1 (UNUSED) char *audio_file_bytes_start; char *video_file_bytes_start; char *audio_current_file_bytes; char *video_current_file_bytes; int audio_file_num_bytes; int video_file_num_bytes; int audio_file_bytes_left; int video_file_bytes_left; /* playback timing information */ int audio_ms_per_packet; int video_ms_per_packet; int audio_bytes_per_packet; int video_bytes_per_packet; int audio_timeticks_per_packet; int video_timeticks_per_packet; int audio_timeticks_per_ms; int video_timeticks_per_ms; /* new file playback information */ int new_audio_pattern_id; // FILE: -1 (UNUSED) -- PATTERN: int new_video_pattern_id; // FILE: -1 (UNUSED) -- PATTERN: char new_audio_payload_type; char new_video_payload_type; int new_audio_loop_count; // FILE: -- PATTERN: -1 (UNUSED) int new_video_loop_count; // FILE: -- PATTERN: -1 (UNUSED) int new_audio_file_size; int new_video_file_size; char *new_audio_file_bytes; char *new_video_file_bytes; int new_audio_ms_per_packet; int new_video_ms_per_packet; int new_audio_bytes_per_packet; int new_video_bytes_per_packet; int new_audio_timeticks_per_packet; int new_video_timeticks_per_packet; /* sockets for audio/video rtp_rtcp */ int audio_rtp_socket; int audio_rtcp_socket; int video_rtp_socket; int video_rtcp_socket; #ifdef USE_TLS /* audio/video SRTP echo activity indicators */ int audio_srtp_echo_active; int video_srtp_echo_active; #endif // USE_TLS /* rtp peer address structures */ struct sockaddr_storage remote_audio_rtp_addr; struct sockaddr_storage remote_audio_rtcp_addr; struct sockaddr_storage remote_video_rtp_addr; struct sockaddr_storage remote_video_rtcp_addr; /* we will have a mutex per call. should we consider refactoring to */ /* share mutexes across calls? makes the per-call code more complex */ /* thread mananagment structures */ pthread_mutex_t mutex; unsigned long audio_comparison_errors; unsigned long video_comparison_errors; int audio_active; int video_active; #ifdef USE_TLS SrtpAudioInfoParams local_srtp_audio_params; SrtpAudioInfoParams remote_srtp_audio_params; SrtpVideoInfoParams local_srtp_video_params; SrtpVideoInfoParams remote_srtp_video_params; #endif // USE_TLS }; struct rtpstream_callinfo_t { taskentry_t *taskinfo; int local_audioport; int local_videoport; int remote_audioport; int remote_videoport; pthread_t threadID; }; struct rtpstream_actinfo_t { char filename[RTPSTREAM_MAX_FILENAMELEN]; // FILE: "" -- PATTERN: "pattern" int pattern_id; // FILE: -1 -- PATTERN: int loop_count; // FILE: count -- PATTERN: -1 (UNUSED) int bytes_per_packet; int ms_per_packet; int ticks_per_packet; /* need rework for 11.025 sample rate */ int payload_type; char payload_name[RTPSTREAM_MAX_PAYLOADNAME]; // FILE/PATTERN: (e.g. "PCMU/8000", "PCMA/8000", "G729/8000", "H264/90000") int audio_active; int video_active; }; struct rtpecho_actinfo_t { int payload_type; char payload_name[RTPECHO_MAX_PAYLOADNAME]; // e.g. "PCMU/8000", "PCMA/8000", "G729/8000", "H264/90000" int bytes_per_packet; int audio_active; int video_active; }; union ParamPass { int i; void* p; }; union ResultCheck { int i; void* p; }; int rtpstream_new_call(rtpstream_callinfo_t *callinfo); void rtpstream_end_call(rtpstream_callinfo_t *callinfo); int rtpstream_shutdown(std::unordered_map& threadIDs); int rtpstream_get_local_audioport(rtpstream_callinfo_t *callinfo); int rtpstream_get_local_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); #ifdef USE_TLS int rtpstream_set_srtp_audio_local(rtpstream_callinfo_t *callinfo, SrtpAudioInfoParams &p); int rtpstream_set_srtp_audio_remote(rtpstream_callinfo_t *callinfo, SrtpAudioInfoParams &p); int rtpstream_set_srtp_video_local(rtpstream_callinfo_t *callinfo, SrtpVideoInfoParams &p); int rtpstream_set_srtp_video_remote(rtpstream_callinfo_t *callinfo, SrtpVideoInfoParams &p); #endif // USE_TLS int rtpstream_cache_file(char *filename, int mode /* 0: FILE - 1: PATTERN */, int id, int bytes_per_packet, int stream_type /* 0: AUDIO - 1: VIDEO */); void rtpstream_play(rtpstream_callinfo_t *callinfo, rtpstream_actinfo_t *actioninfo); void rtpstream_pause(rtpstream_callinfo_t *callinfo); void rtpstream_resume(rtpstream_callinfo_t *callinfo); void rtpstream_playapattern(rtpstream_callinfo_t *callinfo, rtpstream_actinfo_t *actioninfo, JLSRTP& txUACAudio, JLSRTP& rxUACAudio); void rtpstream_pauseapattern(rtpstream_callinfo_t *callinfo); void rtpstream_resumeapattern(rtpstream_callinfo_t *callinfo); void rtpstream_playvpattern(rtpstream_callinfo_t *callinfo, rtpstream_actinfo_t *actioninfo, JLSRTP& txUACVideo, JLSRTP& rxUACVideo); void rtpstream_pausevpattern(rtpstream_callinfo_t *callinfo); void rtpstream_resumevpattern(rtpstream_callinfo_t *callinfo); void rtpstream_audioecho_thread(void * param); void rtpstream_videoecho_thread(void * param); int rtpstream_rtpecho_startaudio(rtpstream_callinfo_t *callinfo, JLSRTP& rxUASAudio, JLSRTP& txUASAudio); int rtpstream_rtpecho_updateaudio(rtpstream_callinfo_t *callinfo, JLSRTP& rxUASAudio, JLSRTP& txUASAudio); int rtpstream_rtpecho_stopaudio(rtpstream_callinfo_t *callinfo); int rtpstream_rtpecho_startvideo(rtpstream_callinfo_t *callinfo, JLSRTP& rxUASVideo, JLSRTP& txUASVideo); int rtpstream_rtpecho_updatevideo(rtpstream_callinfo_t *callinfo, JLSRTP& rxUASVideo, JLSRTP& txUASVideo); int rtpstream_rtpecho_stopvideo(rtpstream_callinfo_t *callinfo); #endif sipp-3.7.2/include/scenario.hpp0000664000000000000000000001707614525516253013350 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Peter Higginson * JPeG * Guillaume TEISSIER from FTR&D */ #ifndef __SCENARIO__ #define __SCENARIO__ #include #include #include #include "actions.hpp" #include "variables.hpp" #include "message.hpp" #include "stat.hpp" #define MSG_TYPE_SENDCMD 0 #define MSG_TYPE_RECVCMD 1 #define MSG_TYPE_SEND 2 #define MSG_TYPE_RECV 3 #define MSG_TYPE_PAUSE 4 #define MSG_TYPE_NOP 5 #define MODE_CLIENT 0 #define MODE_SERVER 1 #define MODE_3PCC_NONE 0 #define MODE_3PCC_CONTROLLER_A 2 #define MODE_3PCC_CONTROLLER_B 3 #define MODE_3PCC_A_PASSIVE 4 /* 3pcc extended mode*/ #define MODE_MASTER 5 #define MODE_MASTER_PASSIVE 6 #define MODE_SLAVE 7 #define OPTIONAL_TRUE 1 #define OPTIONAL_FALSE 0 #define OPTIONAL_GLOBAL 2 class message { public: /* If this is a pause */ CSample *pause_distribution; int pause_variable; /* This string is used for the display screen. */ char *pause_desc; /* Is this a final pause, intended for catching retransmissions? */ bool timewait; /* Number of sessions in a pause */ int sessions; /* should collect route set? */ bool bShouldRecordRoutes; /* should collect authentication info? */ bool bShouldAuthenticate; /* If this is a send */ SendingMessage *send_scheme; unsigned int retrans_delay; /* The receive/send timeout. */ unsigned int timeout; /* 3pcc extended mode: if this is a sendCmd */ char * peer_dest; /* 3pcc extended mode: if this is a recvCmd */ char * peer_src; /* If this is a recv */ int recv_response; char * recv_request; int optional; bool advance_state; int regexp_match; regex_t * regexp_compile; /* Anyway */ int start_rtd; int stop_rtd; bool repeat_rtd; int counter; double lost; int crlf; bool ignoresdp; bool hide; char * display_str; int next; char * nextLabel; int test; int condexec; bool condexec_inverse; int chance;/* 0=always, RAND_MAX+1=never (test rand() >= chance) */ int on_timeout; char * onTimeoutLabel; /* Statistics */ unsigned long nb_sent; unsigned long nb_recv; unsigned long nb_sent_retrans; unsigned long nb_recv_retrans; unsigned long nb_timeout; unsigned long nb_unexp; unsigned long nb_lost; CActions* M_actions; int M_type; SendingMessage *M_sendCmdData; unsigned long M_nbCmdSent; unsigned long M_nbCmdRecv; typedef enum { ContentLengthNoPresent = 0, ContentLengthValueZero, ContentLengthValueNoZero } ContentLengthFlag; ContentLengthFlag content_length_flag ; char *recv_response_for_cseq_method_list; int start_txn; int ack_txn; int response_txn; int index; const char * desc; message(int index, const char *desc); ~message(); }; typedef std::vector msgvec; struct txnControlInfo { char *name; bool isInvite; int acks; int started; int responses; }; typedef std::vector txnvec; class scenario { public: scenario(char * filename, int deflt); ~scenario(); void runInit(); msgvec messages; msgvec initmessages; char *name; int duration; txnvec transactions; int unexpected_jump; int retaddr; int pausedaddr; void computeSippMode(); int get_var(const char *varName, const char *what); int get_counter(const char *varName, const char *what); int get_rtd(const char *ptr, bool start); int find_var(const char *varName); void addRtpTaskThreadID(pthread_t id); void removeRtpTaskThreadID(pthread_t id); std::unordered_map& fetchRtpTaskThreadIDs(); CStat *stats; AllocVariableTable *allocVars; private: /* The mapping of labels to IDs. */ str_int_map labelMap; str_int_map initLabelMap; str_int_map txnMap; bool found_timewait; void getBookKeeping(message *message); void getCommonAttributes(message *message); void getActionForThisMessage(message *message); void parseAction(CActions *actions); void handle_arithmetic(CAction *tmpAction, const char *what); void handle_rhs(CAction *tmpAction, const char *what); void checkOptionalRecv(char *elem, unsigned int scenario_file_cursor); void apply_labels(msgvec v, str_int_map labels); void validate_variable_usage(); void validate_txn_usage(); int get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck); int xp_get_var(const char *name, const char *what); int xp_get_var(const char *name, const char *what, int defval); bool hidedefault; bool last_recv_optional; std::unordered_map threadIDs; }; /* There are external variable containing the current scenario */ extern scenario *main_scenario; extern scenario *ooc_scenario; extern scenario *aa_scenario; extern scenario *display_scenario; extern int creationMode; extern int sendMode; extern int thirdPartyMode; extern message::ContentLengthFlag content_length_flag; void load_scenario(char * filename, int deflt); /* 3pcc extended mode */ void parse_slave_cfg(); void getActionForThisMessage(); CSample *parse_distribution(bool oldstyle); int createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList); int isWellFormed(char * P_listeStr, int * nombre); /* String table functions. */ int createStringTable(const char* inputString, char*** stringList, int* sizeOfList); void freeStringTable(char ** stringList, int sizeOfList); int find_scenario(const char *scenario); extern const char * default_scenario[12]; /* Useful utility functions for parsing integers, etc. */ long get_long(const char *ptr, const char *what); unsigned long long get_long_long(const char *ptr, const char *what); long get_time(const char *ptr, const char *what, int multiplier); double get_double(const char *ptr, const char *what); bool get_bool(const char *ptr, const char *what); int time_string(double ms, char *res, int reslen); int get_var(const char *varName, const char *what); int get_cr_number(const char *msg); #endif sipp-3.7.2/include/screen.hpp0000664000000000000000000000360014525516253013010 0ustar /* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ /**** * Screen.hpp : Simple curses & logfile encapsulation */ #ifndef __SCREEN_H__ #define __SCREEN_H__ #include #include #include #include #include "defines.h" #include "stat.hpp" void screen_init(); void screen_clear(); int screen_readkey(); void screen_exit(); void print_statistics(int last); extern int key_backspace; extern int key_dc; class ScreenPrinter { public: ScreenPrinter(): M_headless(!isatty(fileno(stdout))), M_last(false) {}; void redraw(); void print_closing_stats(); void print_to_file(FILE* f); bool M_headless; private: void get_lines(); void draw_scenario_screen(); void draw_tdm_screen(); void draw_vars_screen(); void draw_stats_screen(); void draw_repartition_screen(int which); void draw_repartition_detailed(CStat::T_dynamicalRepartition * tabRepartition, int sizeOfTab); std::vector lines; bool M_last; }; extern ScreenPrinter* sp; #endif // __SCREEN_H__ sipp-3.7.2/include/send_packets.h0000664000000000000000000001301214525516253013632 0ustar /* * send_packets.h: from tcpreplay tools by Aaron Turner * http://tcpreplay.sourceforge.net/ * send_packets.h is under BSD license (see below) * SIPp is under GPL license * * Copyright (c) 2001-2004 Aaron Turner. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright owners nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SIPP_SEND_PACKETS_H_ #define _SIPP_SEND_PACKETS_H_ #include #include #include #include #include #include "prepare_pcap.h" inline void timerdiv(struct timeval* tvp, float div); inline void float2timer(float time, struct timeval* tvp); #ifndef TIMEVAL_TO_TIMESPEC #define TIMEVAL_TO_TIMESPEC(tv, ts) { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; } #endif /* zero out a timer */ #ifndef timerclear #define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 #endif /* is timer non-zero? */ #ifndef timerisset #define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #endif /* add tvp and uvp and store in vvp */ #ifndef timeradd #define timeradd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ if ((vvp)->tv_usec >= 1000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_usec -= 1000000; \ } \ } while (0) #endif /* subtract uvp from tvp and store in vvp */ #ifndef timersub #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #endif /* compare tvp and uvp using cmp */ #ifndef timercmp #define timercmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #endif /* multiply tvp by x and store in uvp */ #define timermul(tvp, uvp, x) \ do { \ (uvp)->tv_sec = (tvp)->tv_sec * x; \ (uvp)->tv_usec = (tvp)->tv_usec * x; \ while((uvp)->tv_usec > 1000000) { \ (uvp)->tv_sec++; \ (uvp)->tv_usec -= 1000000; \ } \ } while(0) /* device tvp by x. store in tvp */ #define timerdiv2(tvp, x) \ do { \ (tvp)->tv_sec = (tvp)->tv_sec / x; \ (tvp)->tv_usec = (tvp)->tv_usec / x; \ } while(0) /* call specific vars for RTP sending */ typedef struct { /* pointer to a RTP pkts container */ pcap_pkts* pcap; /* Used in send_packets thread */ struct sockaddr_storage to; struct sockaddr_storage from; /* non-zero if the thread should destroy the *pcap when done playing or aborted */ int free_pcap_when_done; uint16_t last_seq_no; } play_args_t; #ifdef __cplusplus extern "C" { #endif int parse_play_args(const char*, pcap_pkts*); int parse_dtmf_play_args(const char*, pcap_pkts*, uint16_t start_seq_no); void free_pcaps(pcap_pkts* pkts); void send_packets(play_args_t*); #ifdef __cplusplus } #endif #endif/*_SIPP_SEND_PACKETS_H_*/ sipp-3.7.2/include/sip_parser.hpp0000664000000000000000000000261214525516253013702 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. */ #ifndef __SIPP_SIP_PARSER_H__ #define __SIPP_SIP_PARSER_H__ #define MAX_HEADER_LEN 2049 char *get_call_id(const char* msg); char *get_peer_tag(const char* msg); int get_method(char *msg); unsigned long int get_cseq_value(const char* msg); unsigned long get_reply_code(const char* msg); char *get_header_content(const char* message, const char* name); char *get_header(const char* message, const char* name, bool content); char *get_first_line(const char* message); #endif /* __SIPP_SIP_PARSER_H__ */ sipp-3.7.2/include/sipp.hpp0000664000000000000000000005161114525516253012511 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #ifndef __SIPP__ #define __SIPP__ /* Std C includes */ #include "config.h" #include "defines.h" #include #include #include #include #include #include #include #include #ifdef USE_SCTP #ifndef __DARWIN #include #endif #endif #include #include #ifdef HAVE_EPOLL #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __SUNOS #include #endif /* Sipp includes */ #include "xp_parser.h" #include "scenario.hpp" #include "screen.hpp" #include "task.hpp" #include "listener.hpp" #include "socket.hpp" #include "socketowner.hpp" #include "call.hpp" #include "comp.h" #include "variables.hpp" #include "stat.hpp" #include "actions.hpp" #include "infile.hpp" #include "call_generation_task.hpp" #include "reporttask.hpp" #include "ratetask.hpp" #include "watchdog.hpp" /* * If this files is included in the Main, then extern definitions * are removed, and the DEFVAL macro becomes '= value;'. Else * extern definition does not contain default values assignment */ #ifdef GLOBALS_FULL_DEFINITION #define MAYBE_EXTERN #define DEFVAL(value) = value #else #define MAYBE_EXTERN extern #define DEFVAL(value) #endif #ifndef __cplusplus #error Unexpected include from non-cxx source #endif /************************** Constants **************************/ #define T_UDP 0 #define T_TCP 1 #define T_TLS 2 #define T_SCTP 3 #ifdef USE_TLS #define DEFAULT_TLS_CERT "cacert.pem" #define DEFAULT_TLS_KEY "cakey.pem" #define DEFAULT_TLS_CA "" #define DEFAULT_TLS_CRL "" #endif #define TRANSPORT_TO_STRING(p) ((p==T_TCP) ? "TCP" : ((p==T_TLS)? "TLS" : ((p==T_UDP)? "UDP" : "SCTP"))) #define SIPP_MAXFDS 65536 #ifndef SIPP_MAX_MSG_SIZE #define SIPP_MAX_MSG_SIZE 65536 #endif #define MSG_RETRANS_FIRST 0 #define MSG_RETRANS_RETRANSMISSION 1 #define MSG_RETRANS_NEVER 2 #define DISPLAY_STAT_SCREEN 1 #define DISPLAY_REPARTITION_SCREEN 2 #define DISPLAY_SCENARIO_SCREEN 3 #define DISPLAY_VARIABLE_SCREEN 4 #define DISPLAY_TDM_MAP_SCREEN 5 #define DISPLAY_SECONDARY_REPARTITION_SCREEN 6 #define MAX_RECV_LOOPS_PER_CYCLE 1000 #define MAX_SCHED_LOOPS_PER_CYCLE 1000 #define NB_UPDATE_PER_CYCLE 1 #define MAX_PEER_SIZE 4096 /* 3pcc extended mode: max size of peer names */ #define MAX_LOCAL_TWIN_SOCKETS 10 /*3pcc extended mode:max number of peers from which cmd messages are received */ #ifdef USE_TLS #define DEFAULT_PREFERRED_AUDIO_CRYPTOSUITE ((char*)"AES_CM_128_HMAC_SHA1_80") #define DEFAULT_PREFERRED_VIDEO_CRYPTOSUITE ((char*)"AES_CM_128_HMAC_SHA1_80") #endif // USE_TLS /******************** Default parameters ***********************/ #define DEFAULT_RATE 10.0 #define DEFAULT_RATE_SCALE 1.0 #define DEFAULT_RATE_PERIOD_MS 1000 #define DEFAULT_TRANSPORT T_UDP #define DEFAULT_PORT 5060 #define DEFAULT_MEDIA_PORT 6000 #define DEFAULT_3PCC_PORT 6060 #define DEFAULT_SERVICE "service" #define DEFAULT_AUTH_PASSWORD "password" #define DEFAULT_REPORT_FREQ 1000 #define DEFAULT_RATE_INCR_FREQ 0 #define DEFAULT_REPORT_FREQ_DUMP_LOG 60000 #define DEFAULT_TIMER_RESOLUTION 1 #define DEFAULT_FREQ_DUMP_RTT 200 #define DEFAULT_MAX_MULTI_SOCKET 50000 #define DEFAULT_CTRL_SOCKET_PORT 8888 #define DEFAULT_DEADCALL_WAIT 33000 #define DEFAULT_BEHAVIOR_NONE 0 #define DEFAULT_BEHAVIOR_BYE 1 #define DEFAULT_BEHAVIOR_ABORTUNEXP 2 #define DEFAULT_BEHAVIOR_PINGREPLY 4 #define DEFAULT_BEHAVIOR_BADCSEQ 8 #define DEFAULT_BEHAVIOR_ALL (DEFAULT_BEHAVIOR_BYE | DEFAULT_BEHAVIOR_ABORTUNEXP | DEFAULT_BEHAVIOR_PINGREPLY | DEFAULT_BEHAVIOR_BADCSEQ) #define DEFAULT_MIN_RTP_PORT DEFAULT_MEDIA_PORT #define DEFAULT_MAX_RTP_PORT 65535 #define DEFAULT_RTP_PAYLOAD 8 #define DEFAULT_RTP_THREADTASKS 20 /************ User controls and command line options ***********/ MAYBE_EXTERN int duration DEFVAL(0); MAYBE_EXTERN double rate DEFVAL(DEFAULT_RATE); MAYBE_EXTERN double rate_scale DEFVAL(DEFAULT_RATE_SCALE); MAYBE_EXTERN int rate_increase DEFVAL(0); MAYBE_EXTERN int rate_max DEFVAL(0); MAYBE_EXTERN unsigned long rate_increase_freq DEFVAL(DEFAULT_RATE_INCR_FREQ); MAYBE_EXTERN bool rate_quit DEFVAL(true); MAYBE_EXTERN int users DEFVAL(-1); MAYBE_EXTERN int rate_period_ms DEFVAL(DEFAULT_RATE_PERIOD_MS); MAYBE_EXTERN int sleeptime DEFVAL(0); MAYBE_EXTERN unsigned long defl_recv_timeout DEFVAL(0); MAYBE_EXTERN unsigned long defl_send_timeout DEFVAL(0); MAYBE_EXTERN unsigned long global_timeout DEFVAL(0); MAYBE_EXTERN int transport DEFVAL(DEFAULT_TRANSPORT); MAYBE_EXTERN bool retrans_enabled DEFVAL(1); MAYBE_EXTERN int rtcheck DEFVAL(RTCHECK_FULL); MAYBE_EXTERN int max_udp_retrans DEFVAL(UDP_MAX_RETRANS); MAYBE_EXTERN int max_invite_retrans DEFVAL(UDP_MAX_RETRANS_INVITE_TRANSACTION); MAYBE_EXTERN int max_non_invite_retrans DEFVAL(UDP_MAX_RETRANS_NON_INVITE_TRANSACTION); MAYBE_EXTERN unsigned long default_behaviors DEFVAL(DEFAULT_BEHAVIOR_ALL); MAYBE_EXTERN unsigned long deadcall_wait DEFVAL(DEFAULT_DEADCALL_WAIT); MAYBE_EXTERN bool pause_msg_ign DEFVAL(0); MAYBE_EXTERN bool auto_answer DEFVAL(false); MAYBE_EXTERN int multisocket DEFVAL(0); MAYBE_EXTERN int compression DEFVAL(0); MAYBE_EXTERN int peripsocket DEFVAL(0); MAYBE_EXTERN int peripfield DEFVAL(0); MAYBE_EXTERN bool bind_local DEFVAL(false); MAYBE_EXTERN void * monosocket_comp_state DEFVAL(0); MAYBE_EXTERN const char * service DEFVAL(DEFAULT_SERVICE); MAYBE_EXTERN const char * auth_password DEFVAL(DEFAULT_AUTH_PASSWORD); MAYBE_EXTERN const char * auth_username DEFVAL(0); MAYBE_EXTERN unsigned long report_freq DEFVAL(DEFAULT_REPORT_FREQ); MAYBE_EXTERN unsigned long report_freq_dumpLog DEFVAL (DEFAULT_REPORT_FREQ_DUMP_LOG); MAYBE_EXTERN bool periodic_rtd DEFVAL(false); MAYBE_EXTERN const char * stat_delimiter DEFVAL(";"); MAYBE_EXTERN bool timeout_exit DEFVAL(false); MAYBE_EXTERN bool timeout_error DEFVAL(false); MAYBE_EXTERN unsigned long report_freq_dumpRtt DEFVAL (DEFAULT_FREQ_DUMP_RTT); MAYBE_EXTERN unsigned max_multi_socket DEFVAL (DEFAULT_MAX_MULTI_SOCKET); MAYBE_EXTERN bool skip_rlimit DEFVAL(false); MAYBE_EXTERN unsigned int timer_resolution DEFVAL(DEFAULT_TIMER_RESOLUTION); MAYBE_EXTERN int max_recv_loops DEFVAL(MAX_RECV_LOOPS_PER_CYCLE); MAYBE_EXTERN int max_sched_loops DEFVAL(MAX_SCHED_LOOPS_PER_CYCLE); MAYBE_EXTERN unsigned int global_t2 DEFVAL(DEFAULT_T2_TIMER_VALUE); MAYBE_EXTERN char local_ip[127]; /* also used for hostnames */ MAYBE_EXTERN char local_ip_w_brackets[42]; /* with [brackets] in case of IPv6 */ MAYBE_EXTERN bool local_ip_is_ipv6; MAYBE_EXTERN int local_port DEFVAL(0); #ifdef USE_SCTP MAYBE_EXTERN char multihome_ip[40]; MAYBE_EXTERN int heartbeat DEFVAL(0); MAYBE_EXTERN int assocmaxret DEFVAL(0); MAYBE_EXTERN int pathmaxret DEFVAL(0); MAYBE_EXTERN int pmtu DEFVAL(0); MAYBE_EXTERN bool gracefulclose DEFVAL(true); #endif MAYBE_EXTERN char control_ip[40]; MAYBE_EXTERN int control_port DEFVAL(0); MAYBE_EXTERN int buff_size DEFVAL(65536); MAYBE_EXTERN int tcp_readsize DEFVAL(65536); MAYBE_EXTERN int hasMedia DEFVAL(0); MAYBE_EXTERN int min_rtp_port DEFVAL(DEFAULT_MIN_RTP_PORT); MAYBE_EXTERN int max_rtp_port DEFVAL(DEFAULT_MAX_RTP_PORT); MAYBE_EXTERN int rtp_default_payload DEFVAL(DEFAULT_RTP_PAYLOAD); MAYBE_EXTERN int rtp_tasks_per_thread DEFVAL(DEFAULT_RTP_THREADTASKS); MAYBE_EXTERN int rtp_buffsize DEFVAL(65536); MAYBE_EXTERN bool rtpcheck_debug DEFVAL(0); #ifdef USE_TLS MAYBE_EXTERN bool srtpcheck_debug DEFVAL(0); #endif // USE_TLS MAYBE_EXTERN double audiotolerance DEFVAL(1.0); MAYBE_EXTERN double videotolerance DEFVAL(1.0); MAYBE_EXTERN bool rtp_echo_enabled DEFVAL(0); MAYBE_EXTERN char media_ip[127]; /* also used for hostnames */ MAYBE_EXTERN int media_port DEFVAL(0); MAYBE_EXTERN size_t media_bufsize DEFVAL(2048); MAYBE_EXTERN bool media_ip_is_ipv6 DEFVAL(false); MAYBE_EXTERN char remote_ip[127]; /* also used for hostnames */ MAYBE_EXTERN char remote_ip_w_brackets[42]; /* with [brackets] in case of IPv6 */ MAYBE_EXTERN int remote_port DEFVAL(DEFAULT_PORT); MAYBE_EXTERN unsigned int pid DEFVAL(0); MAYBE_EXTERN bool print_all_responses DEFVAL(false); MAYBE_EXTERN unsigned long stop_after DEFVAL(0xffffffff); MAYBE_EXTERN int quitting DEFVAL(0); MAYBE_EXTERN int interrupt DEFVAL(0); MAYBE_EXTERN bool paused DEFVAL(false); MAYBE_EXTERN int lose_packets DEFVAL(0); MAYBE_EXTERN double global_lost DEFVAL(0.0); MAYBE_EXTERN char remote_host[255]; MAYBE_EXTERN char twinSippHost[255]; MAYBE_EXTERN char twinSippIp[40]; MAYBE_EXTERN char * master_name; MAYBE_EXTERN char * slave_number; MAYBE_EXTERN int twinSippPort DEFVAL(DEFAULT_3PCC_PORT); MAYBE_EXTERN bool twinSippMode DEFVAL(false); MAYBE_EXTERN bool extendedTwinSippMode DEFVAL(false); MAYBE_EXTERN bool nostdin DEFVAL(false); MAYBE_EXTERN bool backgroundMode DEFVAL(false); MAYBE_EXTERN bool signalDump DEFVAL(false); MAYBE_EXTERN int currentScreenToDisplay DEFVAL (DISPLAY_SCENARIO_SCREEN); MAYBE_EXTERN int currentRepartitionToDisplay DEFVAL(1); MAYBE_EXTERN unsigned int base_cseq DEFVAL(0); MAYBE_EXTERN char * auth_uri DEFVAL(0); MAYBE_EXTERN const char * call_id_string DEFVAL("%u-%p@%s"); MAYBE_EXTERN char **generic[100]; MAYBE_EXTERN bool rtp_echo_state DEFVAL(true); MAYBE_EXTERN bool callidSlash DEFVAL(false); /* TDM map */ MAYBE_EXTERN bool use_tdmmap DEFVAL(false); MAYBE_EXTERN unsigned int tdm_map_a DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_b DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_c DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_x DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_y DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_z DEFVAL(0); MAYBE_EXTERN unsigned int tdm_map_h DEFVAL(0); MAYBE_EXTERN bool tdm_map[1024]; #ifdef USE_TLS MAYBE_EXTERN const char * tls_cert_name DEFVAL(DEFAULT_TLS_CERT); MAYBE_EXTERN const char * tls_key_name DEFVAL(DEFAULT_TLS_KEY); MAYBE_EXTERN const char * tls_ca_name DEFVAL(DEFAULT_TLS_CA); MAYBE_EXTERN const char * tls_crl_name DEFVAL(DEFAULT_TLS_CRL); MAYBE_EXTERN double tls_version DEFVAL(0.0); #endif MAYBE_EXTERN char* scenario_file DEFVAL(NULL); MAYBE_EXTERN char* scenario_path DEFVAL(NULL); // extern field file management typedef std::map file_map; MAYBE_EXTERN file_map inFiles; typedef std::map file_index; MAYBE_EXTERN char *ip_file DEFVAL(NULL); MAYBE_EXTERN char *default_file DEFVAL(NULL); // free user id list MAYBE_EXTERN list freeUsers; MAYBE_EXTERN list retiredUsers; MAYBE_EXTERN AllocVariableTable *globalVariables DEFVAL(NULL); MAYBE_EXTERN AllocVariableTable *userVariables DEFVAL(NULL); typedef std::map int_vt_map; MAYBE_EXTERN int_vt_map userVarMap; MAYBE_EXTERN SIPpSocket* new_sipp_socket(bool use_ipv6, int transport); MAYBE_EXTERN int sipp_bind_socket(SIPpSocket *socket, struct sockaddr_storage *saddr, int *port); MAYBE_EXTERN void sipp_customize_socket(SIPpSocket *socket); MAYBE_EXTERN bool test_socket DEFVAL(true); #include "time.hpp" /************************ Statistics **************************/ MAYBE_EXTERN unsigned long last_report_calls DEFVAL(0); MAYBE_EXTERN unsigned long nb_net_send_errors DEFVAL(0); MAYBE_EXTERN unsigned long nb_net_cong DEFVAL(0); MAYBE_EXTERN unsigned long nb_net_recv_errors DEFVAL(0); MAYBE_EXTERN bool cpu_max DEFVAL(false); MAYBE_EXTERN bool outbound_congestion DEFVAL(false); MAYBE_EXTERN int open_calls_user_setting DEFVAL(0); MAYBE_EXTERN int resynch_send DEFVAL(0); MAYBE_EXTERN int resynch_recv DEFVAL(0); MAYBE_EXTERN unsigned long rtp_pckts DEFVAL(0); MAYBE_EXTERN unsigned long rtp_bytes DEFVAL(0); MAYBE_EXTERN unsigned long rtp_pckts_pcap DEFVAL(0); MAYBE_EXTERN unsigned long rtp_bytes_pcap DEFVAL(0); MAYBE_EXTERN unsigned long rtp2_pckts DEFVAL(0); MAYBE_EXTERN unsigned long rtp2_bytes DEFVAL(0); MAYBE_EXTERN unsigned long rtp2_pckts_pcap DEFVAL(0); MAYBE_EXTERN unsigned long rtp2_bytes_pcap DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_numthreads DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_abytes_in DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_vbytes_in DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_abytes_out DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_vbytes_out DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_apckts DEFVAL(0); MAYBE_EXTERN volatile unsigned long rtpstream_vpckts DEFVAL(0); /************* Rate Control & Contexts variables **************/ MAYBE_EXTERN int last_running_calls DEFVAL(0); MAYBE_EXTERN int last_woken_calls DEFVAL(0); MAYBE_EXTERN int last_paused_calls DEFVAL(0); MAYBE_EXTERN unsigned int open_calls_allowed DEFVAL(0); MAYBE_EXTERN unsigned long last_report_time DEFVAL(0); MAYBE_EXTERN unsigned long last_dump_time DEFVAL(0); MAYBE_EXTERN unsigned long last_rate_increase_time DEFVAL(0); /********************** Clock variables ***********************/ MAYBE_EXTERN unsigned long clock_tick DEFVAL(0); MAYBE_EXTERN unsigned long scheduling_loops DEFVAL(0); MAYBE_EXTERN unsigned long last_timer_cycle DEFVAL(0); MAYBE_EXTERN unsigned long watchdog_interval DEFVAL(400); MAYBE_EXTERN unsigned long watchdog_minor_threshold DEFVAL(500); MAYBE_EXTERN unsigned long watchdog_minor_maxtriggers DEFVAL(120); MAYBE_EXTERN unsigned long watchdog_major_threshold DEFVAL(3000); MAYBE_EXTERN unsigned long watchdog_major_maxtriggers DEFVAL(10); MAYBE_EXTERN unsigned long watchdog_reset DEFVAL(600000); /********************* dynamic Id ************************* */ MAYBE_EXTERN int maxDynamicId DEFVAL(12000); // max value for dynamicId; this value is reached MAYBE_EXTERN int startDynamicId DEFVAL(10000); // offset for first dynamicId FIXME:in CmdLine MAYBE_EXTERN int stepDynamicId DEFVAL(4); // step of increment for dynamicId #define GET_TIME(clock) \ { \ struct timezone tzp; \ gettimeofday (clock, &tzp); \ } /*********************** Global Sockets **********************/ MAYBE_EXTERN SIPpSocket *main_socket DEFVAL(NULL); MAYBE_EXTERN SIPpSocket *main_remote_socket DEFVAL(NULL); MAYBE_EXTERN SIPpSocket *tcp_multiplex DEFVAL(NULL); MAYBE_EXTERN int media_socket_audio DEFVAL(0); MAYBE_EXTERN int media_socket_video DEFVAL(0); MAYBE_EXTERN struct sockaddr_storage local_sockaddr; MAYBE_EXTERN struct sockaddr_storage localTwin_sockaddr; MAYBE_EXTERN int user_port DEFVAL(0); MAYBE_EXTERN char hostname[80]; MAYBE_EXTERN int reset_number DEFVAL(0); MAYBE_EXTERN bool reset_close DEFVAL(true); MAYBE_EXTERN int reset_sleep DEFVAL(1000); MAYBE_EXTERN bool sendbuffer_warn DEFVAL(false); /* A list of sockets pending reset. */ MAYBE_EXTERN set sockets_pending_reset; 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; /* 3pcc extended mode */ typedef struct _T_peer_infos { char peer_host[40]; int peer_port; struct sockaddr_storage peer_sockaddr; char peer_ip[40]; SIPpSocket *peer_socket; } T_peer_infos; typedef std::map peer_addr_map; MAYBE_EXTERN peer_addr_map peer_addrs; typedef std::map peer_map; MAYBE_EXTERN peer_map peers; typedef std::map peer_socket_map; MAYBE_EXTERN peer_socket_map peer_sockets; MAYBE_EXTERN SIPpSocket *local_sockets[MAX_LOCAL_TWIN_SOCKETS]; MAYBE_EXTERN int local_nb DEFVAL(0); MAYBE_EXTERN int peers_connected DEFVAL(0); MAYBE_EXTERN struct sockaddr_storage remote_sockaddr; MAYBE_EXTERN short use_remote_sending_addr DEFVAL(0); MAYBE_EXTERN struct sockaddr_storage remote_sending_sockaddr; enum E_Alter_YesNo { E_ALTER_YES=0, E_ALTER_NO }; #include "logger.hpp" /********************* Utilities functions *******************/ #include "strings.hpp" void sipp_exit(int rc, int rtp_errors, int echo_errors); char *get_peer_addr(char *); bool reconnect_allowed(); void reset_connection(SIPpSocket *); void close_calls(SIPpSocket *); int close_connections(); int open_connections(); void timeout_alarm(int); /* extended 3PCC mode */ SIPpSocket **get_peer_socket(char *); bool is_a_peer_socket(SIPpSocket *); bool is_a_local_socket(SIPpSocket *); void connect_to_peer(char *, int, sockaddr_storage *, char *, SIPpSocket **); void connect_to_all_peers(); void connect_local_twin_socket(char *); void close_peer_sockets(); void close_local_sockets(); void free_peer_addr_map(); /********************* Reset global kludge *******************/ #endif // __SIPP__ sipp-3.7.2/include/socket.hpp0000664000000000000000000001551514525516253013031 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #ifndef __SIPP_SOCKET_H__ #define __SIPP_SOCKET_H__ #ifdef USE_TLS #include "sslsocket.hpp" #endif /** * On some systems you must pass the exact sockaddr struct size to * connect/bind/sendto calls. Passing a length that is too large * causes EINVAL. * * For instance on OSX. If you don't, you'll get this: * Unable to bind audio RTP socket (IP=X.X.X.X, port=6100), errno = 22 * (Invalid argument). * * Usage: * * struct sockaddr_storage addr; * ... * bind(socket, (struct sockaddr*)&addr, socklen_from_addr(&addr)); */ inline socklen_t socklen_from_addr(const struct sockaddr_storage* ss) { if (ss->ss_family == AF_INET) { return sizeof(struct sockaddr_in); } else if (ss->ss_family == AF_INET6) { return sizeof(struct sockaddr_in6); } else { assert(false); return 0; } } int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, unsigned short port, int flags, int family); int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, const char *service, int flags, int family); void sockaddr_update_port(struct sockaddr_storage* ss, short port); /* This is an abstraction of a socket, which provides buffers for input and * output. */ class SIPpSocket { public: SIPpSocket(bool use_ipv6, int transport, int fd, int accepting); static SIPpSocket* new_sipp_call_socket(bool use_ipv6, int transport, bool *existing); void set_bind_port(int bind_port); int connect(struct sockaddr_storage* dest = NULL); int reconnect(); // Reset a failed connection void reset_connection(); // Accept new connections from a TCP socket SIPpSocket* accept(); // Write data to the socket. int write(const char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest); // Empty data from the socket into our buffer int empty(); // Decrement the reference count of this socket, shutting it down when it reaches 0 void close(); int read_error(int ret); // Have we read a message from this socket? bool message_ready() { return ss_msglen > 0; }; static void pollset_process(int wait); int ss_count; /* How many users are there of this socket? */ bool ss_ipv6; int ss_transport; /* T_TCP, T_UDP, or T_TLS. */ bool ss_control; /* Is this a control socket? */ int ss_fd; /* The underlying file descriptor for this socket. */ int ss_port; /* The port used by this socket */ int ss_bind_port; /* Optional local port used by this socket */ void *ss_comp_state; /* The compression state. */ bool ss_changed_dest; /* Has the destination changed from default. */ struct sockaddr_storage ss_dest; /* Who we are talking to. */ private: bool ss_congested; /* Is this socket congested? */ bool ss_invalid; /* Has this socket been closed remotely? */ int handleSCTPNotify(char* buffer); void sipp_sctp_peer_params(); void invalidate(); void buffer_read(struct socketbuf *newbuf); void buffer_write(const char *buffer, size_t len, struct sockaddr_storage *dest); ssize_t read_message(char *buf, size_t len, struct sockaddr_storage *src); struct socketbuf *ss_in; /* Buffered input. */ struct socketbuf *ss_out; /* Buffered output. */ struct socketbuf *ss_out_tail; /* Tail of buffered output */ size_t ss_msglen; /* Is there a complete SIP message waiting, and if so how big? */ void close_calls(); int flush(); int write_error(int ret); void abort(); int check_for_message(); int enter_congestion(int again); ssize_t write_primitive(const char* buffer, size_t len, struct sockaddr_storage* dest); bool ss_call_socket; /* Is this a call socket? */ #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) SSL *ss_ssl; /* The underlying SSL descriptor for this socket. */ BIO *ss_bio; /* The underlying BIO descriptor for this socket. */ #endif int ss_pollidx; /* The index of this socket in our poll structures. */ #ifdef USE_SCTP int sctpstate; #endif }; void setup_ctrl_socket(); void setup_stdin_socket(); int handle_ctrl_socket(); void handle_stdin_socket(); void process_message(SIPpSocket* socket, char *msg, ssize_t msg_size, struct sockaddr_storage *src); bool reconnect_allowed(); /********************** Network Interfaces ********************/ int send_message(int s, void ** comp_state, char * msg); #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) int send_message_tls(SSL *s, void ** comp_state, char * msg); #endif /* Socket Buffer Management. */ #define NO_COPY 0 #define DO_COPY 1 struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy); struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy, struct sockaddr_storage *dest); void free_socketbuf(struct socketbuf *socketbuf); /* These buffers lets us read past the end of the message, and then split it if * required. This eliminates the need for reading a message octet by octet and * performing a second read for the content length. */ struct socketbuf { char *buf; size_t len; size_t offset; struct sockaddr_storage addr; struct socketbuf *next; }; #ifdef USE_SCTP #define SCTP_DOWN 0 #define SCTP_CONNECTING 1 #define SCTP_UP 2 #endif /* Abort a connection - close the socket quickly. */ #define WS_EAGAIN 1 /* Return EAGAIN if there is no room for writing the message. */ #define WS_BUFFER 2 /* Buffer the message if there is no room for writing the message. */ #if defined (__hpux) || (defined (__alpha) && !defined (__FreeBSD__) && !defined (__linux__)) #define sipp_socklen_t int #else #define sipp_socklen_t socklen_t #endif #if defined(__cplusplus) && defined (__hpux) #define _RCAST(type, val) (reinterpret_cast (val)) #else #define _RCAST(type, val) ((type)(val)) #endif /* Time to wait in microseconds before retrying querying an SSL socket */ #define SIPP_SSL_RETRY_TIMEOUT 200000 /* Max retries when querying an SSL socket */ #define SIPP_SSL_MAX_RETRIES 10 #endif /* __SIPP_SOCKET_H__ */ sipp-3.7.2/include/socketowner.hpp0000664000000000000000000000346214525516253014102 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #ifndef __SOCKETOWNER__ #define __SOCKETOWNER__ class socketowner { public: socketowner(); virtual ~socketowner(); /* Associate/Dissociate this call with a socket. */ SIPpSocket *associate_socket(SIPpSocket *socket); SIPpSocket *dissociate_socket(); /* Notification of TCP Close events. */ virtual void tcpClose() = 0; protected: /* What socket is this call bound to. */ SIPpSocket *call_socket; unsigned long ownerid; static unsigned long nextownerid; private: void add_owner_to_socket(SIPpSocket *socket); void remove_owner_from_socket(SIPpSocket *socket); }; typedef std::map owner_map; typedef std::pair socket_map_pair; typedef std::map socket_owner_map_map; typedef std::list owner_list; typedef std::pair long_owner_pair; owner_list *get_owners_for_socket(SIPpSocket *socket); #endif sipp-3.7.2/include/sslsocket.hpp0000664000000000000000000000323414525516253013546 0ustar /* * 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 * * Author : Gundu RAO - 16 Jul 2004 * From Hewlett Packard Company. */ #ifndef __SSLSOCKET__ #define __SSLSOCKET__ #if defined(USE_OPENSSL) #include #include #include #include #include #include #elif defined(USE_WOLFSSL) #include #include #include #include #include #endif /* Initialises an SSL context and makes the lib thread safe */ int TLS_init(); enum tls_init_status { TLS_INIT_NORMAL, /* 0 Normal completion */ TLS_INIT_ERROR /* 1 Unspecified error */ }; enum tls_init_status TLS_init_context(void); /* Helpers for OpenSSL */ #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) SSL* SSL_new_client(); SSL* SSL_new_server(); const char *SSL_error_string(int ssl_error, int orig_ret); #endif #endif sipp-3.7.2/include/stat.hpp0000664000000000000000000005413014525516253012510 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. */ #ifndef __STAT_H__ #define __STAT_H__ #define TIME_LENGTH 64 #define DEFAULT_FILE_NAME (char*)"dumpFile" #define DEFAULT_EXTENSION (char*)".csv" #define MAX_CHAR_BUFFER_SIZE 1024 #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_GSL #include #include #include #endif #include "variables.hpp" using namespace std; /* __________________________________________________________________________ C S t a t C L A S S __________________________________________________________________________ */ /** * This class provides some means to compute statistics. * This is a singleton class. */ class CStat { friend class ScreenPrinter; public: std::vector error_codes; /* * This struct is used for repartition table * border max is the max value allow for this range * nbInThisBorder is the counter of value in this range */ typedef struct _T_dynamicalRepartition { unsigned int borderMax; unsigned long nbInThisBorder; } T_dynamicalRepartition; typedef struct _T_value_rtt { double date ; int rtd_no ; double rtt ; } T_value_rtt, *T_pValue_rtt ; /** * Actions on counters */ enum E_Action { E_NO_ACTION, E_CREATE_OUTGOING_CALL, E_CREATE_INCOMING_CALL, E_CALL_FAILED, E_CALL_SUCCESSFULLY_ENDED, E_RESET_C_COUNTERS, E_RESET_PD_COUNTERS, E_RESET_PL_COUNTERS, E_ADD_CALL_DURATION, E_ADD_RESPONSE_TIME_DURATION, E_FAILED_CANNOT_SEND_MSG, E_FAILED_MAX_UDP_RETRANS, E_FAILED_TCP_CONNECT, E_FAILED_TCP_CLOSED, E_FAILED_UNEXPECTED_MSG, E_FAILED_CALL_REJECTED, E_FAILED_CMD_NOT_SENT, E_FAILED_REGEXP_DOESNT_MATCH, E_FAILED_REGEXP_SHOULDNT_MATCH, E_FAILED_REGEXP_HDR_NOT_FOUND, E_FAILED_OUTBOUND_CONGESTION, E_FAILED_TIMEOUT_ON_RECV, E_FAILED_TIMEOUT_ON_SEND, E_FAILED_TEST_DOESNT_MATCH, E_FAILED_TEST_SHOULDNT_MATCH, E_FAILED_STRCMP_DOESNT_MATCH, E_FAILED_STRCMP_SHOULDNT_MATCH, E_OUT_OF_CALL_MSGS, E_WATCHDOG_MAJOR, E_WATCHDOG_MINOR, E_DEAD_CALL_MSGS, E_FATAL_ERRORS, E_WARNING, E_RETRANSMISSION, E_AUTO_ANSWERED, E_ADD_GENERIC_COUNTER }; /** * Counters management */ enum E_CounterName { // Per-Scenario Counters // Cumulative counters CPT_C_IncomingCallCreated, CPT_C_OutgoingCallCreated, CPT_C_SuccessfulCall, CPT_C_FailedCall, CPT_C_CurrentCall, CPT_C_CurrentCallPeak, CPT_C_CurrentCallPeakTime, CPT_C_NbOfCallUsedForAverageCallLength, CPT_C_AverageCallLength_Sum, CPT_C_AverageCallLength_Squares, CPT_C_FailedCallCannotSendMessage, CPT_C_FailedCallMaxUdpRetrans, CPT_C_FailedCallTcpConnect, CPT_C_FailedCallTcpClosed, CPT_C_FailedCallUnexpectedMessage, CPT_C_FailedCallCallRejected, CPT_C_FailedCallCmdNotSent, CPT_C_FailedCallRegexpDoesntMatch, CPT_C_FailedCallRegexpShouldntMatch, CPT_C_FailedCallRegexpHdrNotFound, CPT_C_FailedCallTestDoesntMatch, CPT_C_FailedCallTestShouldntMatch, CPT_C_FailedCallStrcmpDoesntMatch, CPT_C_FailedCallStrcmpShouldntMatch, CPT_C_FailedOutboundCongestion, CPT_C_FailedTimeoutOnRecv, CPT_C_FailedTimeoutOnSend, CPT_C_Retransmissions, // Periodic Display counter CPT_PD_IncomingCallCreated, // must be first (RESET_PD_COUNTER macro) CPT_PD_OutgoingCallCreated, CPT_PD_SuccessfulCall, CPT_PD_FailedCall, CPT_PD_CurrentCallPeak, CPT_PD_CurrentCallPeakTime, CPT_PD_NbOfCallUsedForAverageCallLength, CPT_PD_AverageCallLength_Sum, CPT_PD_AverageCallLength_Squares, CPT_PD_NbOfCallUsedForAverageResponseTime, CPT_PD_NbOfCallUsedForAverageResponseTime_2, // This must match or exceed MAX_RTD_INFO CPT_PD_NbOfCallUsedForAverageResponseTime_3, // This must match or exceed MAX_RTD_INFO CPT_PD_NbOfCallUsedForAverageResponseTime_4, // This must match or exceed MAX_RTD_INFO CPT_PD_NbOfCallUsedForAverageResponseTime_5, // This must match or exceed MAX_RTD_INFO CPT_PD_AverageResponseTime_Sum, CPT_PD_AverageResponseTime_Sum_2, CPT_PD_AverageResponseTime_Sum_3, CPT_PD_AverageResponseTime_Sum_4, CPT_PD_AverageResponseTime_Sum_5, CPT_PD_AverageResponseTime_Squares, CPT_PD_AverageResponseTime_Squares_2, CPT_PD_AverageResponseTime_Squares_3, CPT_PD_AverageResponseTime_Squares_4, CPT_PD_AverageResponseTime_Squares_5, CPT_PD_FailedCallCannotSendMessage, CPT_PD_FailedCallMaxUdpRetrans, CPT_PD_FailedCallTcpConnect, CPT_PD_FailedCallTcpClosed, CPT_PD_FailedCallUnexpectedMessage, CPT_PD_FailedCallCallRejected, CPT_PD_FailedCallCmdNotSent, CPT_PD_FailedCallRegexpDoesntMatch, CPT_PD_FailedCallRegexpShouldntMatch, CPT_PD_FailedCallRegexpHdrNotFound, CPT_PD_FailedCallTestDoesntMatch, CPT_PD_FailedCallTestShouldntMatch, CPT_PD_FailedCallStrcmpDoesntMatch, CPT_PD_FailedCallStrcmpShouldntMatch, CPT_PD_FailedOutboundCongestion, CPT_PD_FailedTimeoutOnRecv, CPT_PD_FailedTimeoutOnSend, CPT_PD_Retransmissions, // Periodic logging counter CPT_PL_IncomingCallCreated, // must be first (RESET_PL_COUNTER macro) CPT_PL_OutgoingCallCreated, CPT_PL_SuccessfulCall, CPT_PL_FailedCall, CPT_PL_CurrentCallPeak, CPT_PL_CurrentCallPeakTime, CPT_PL_NbOfCallUsedForAverageCallLength, CPT_PL_AverageCallLength_Sum, /* The squares let us compute the standard deviation. */ CPT_PL_AverageCallLength_Squares, CPT_PL_NbOfCallUsedForAverageResponseTime, CPT_PL_NbOfCallUsedForAverageResponseTime_2, CPT_PL_NbOfCallUsedForAverageResponseTime_3, CPT_PL_NbOfCallUsedForAverageResponseTime_4, CPT_PL_NbOfCallUsedForAverageResponseTime_5, CPT_PL_AverageResponseTime_Sum, CPT_PL_AverageResponseTime_Sum_2, CPT_PL_AverageResponseTime_Sum_3, CPT_PL_AverageResponseTime_Sum_4, CPT_PL_AverageResponseTime_Sum_5, CPT_PL_AverageResponseTime_Squares, CPT_PL_AverageResponseTime_Squares_2, CPT_PL_AverageResponseTime_Squares_3, CPT_PL_AverageResponseTime_Squares_4, CPT_PL_AverageResponseTime_Squares_5, CPT_PL_FailedCallCannotSendMessage, CPT_PL_FailedCallMaxUdpRetrans, CPT_PL_FailedCallTcpConnect, CPT_PL_FailedCallTcpClosed, CPT_PL_FailedCallUnexpectedMessage, CPT_PL_FailedCallCallRejected, CPT_PL_FailedCallCmdNotSent, CPT_PL_FailedCallRegexpDoesntMatch, CPT_PL_FailedCallRegexpShouldntMatch, CPT_PL_FailedCallRegexpHdrNotFound, CPT_PL_FailedCallTestDoesntMatch, CPT_PL_FailedCallTestShouldntMatch, CPT_PL_FailedCallStrcmpDoesntMatch, CPT_PL_FailedCallStrcmpShouldntMatch, CPT_PL_FailedOutboundCongestion, CPT_PL_FailedTimeoutOnRecv, CPT_PL_FailedTimeoutOnSend, CPT_PL_Retransmissions, E_NB_COUNTER, // Global Counters // Cumulative counters CPT_G_C_OutOfCallMsgs, CPT_G_C_DeadCallMsgs, CPT_G_C_FatalErrors, CPT_G_C_Warnings, CPT_G_C_WatchdogMajor, CPT_G_C_WatchdogMinor, CPT_G_C_AutoAnswered, // Periodic Display counter CPT_G_PD_OutOfCallMsgs, CPT_G_PD_DeadCallMsgs, CPT_G_PD_FatalErrors, CPT_G_PD_Warnings, CPT_G_PD_WatchdogMajor, CPT_G_PD_WatchdogMinor, CPT_G_PD_AutoAnswered, // must be last (RESET_PD_COUNTER) // Periodic logging counter CPT_G_PL_OutOfCallMsgs, CPT_G_PL_DeadCallMsgs, CPT_G_PL_FatalErrors, CPT_G_PL_Warnings, CPT_G_PL_WatchdogMajor, CPT_G_PL_WatchdogMinor, CPT_G_PL_AutoAnswered, // must be last (RESET_PL_COUNTER) E_NB_G_COUNTER }; /* ** exported methods */ /** * Constructor. */ CStat (); /** * Destructor. */ ~CStat (); /** * Delete the single instance of the class. * * Only one instance of CStat exists in the component. This * instance is deleted when the close method is called. */ void close (); void setRtpEchoErrors(int value); int getRtpEchoErrors(); /** * ComputeStat Methods are used to modify counter value * It's the main interface to update counter * * @return 0 if the action is known * -1 else */ int computeStat (E_Action P_action); int computeStat (E_Action P_action, unsigned long P_value); int computeStat (E_Action P_action, unsigned long P_value, int which); /* This works for global counters and does not require an instance. */ static int globalStat (E_Action P_action); /** * ComputeRtt Methods are used to calculate the response time */ void computeRtt ( unsigned long long P_start_time, unsigned long long P_stop_time, int which); /** * GetStat Method is used to retrieve a counter value * * @return the counter value **/ unsigned long long GetStat (E_CounterName P_counter); /* Get the current start time. */ void getStartTime(struct timeval *t); /** * formatTime. * * This method converts a struct timeval parameter into a printable string * in the format given in parameter. * * @param P_tv. * @return a pointer on a static string containing formated time */ static char* formatTime (struct timeval* P_tv, bool with_epoch = false); /** * setRepartitionCallLength * - set the unsigned int table passed in parameter as the repartition table * for call length. This is done by calling the initRepartition methode on * the M_CallLengthRepartition variable. * - set the char* list of int (must be separeted with coma as the * repartition table for call length * This is done by calling the createIntegerTable to transform the char* * list into unsigned int list. Then the initRepartition methode is * call with the created unsigned int list and the M_CallLengthRepartition * variable * * setRepartitionResponseTime * Same than setRepartitionCallLength with the variable * M_ResponseTimeRepartition variableinstead of M_CallLengthRepartition * variable */ void setRepartitionCallLength (unsigned int* repartition, int nombre); void setRepartitionCallLength (char * liste); void setRepartitionResponseTime (unsigned int* repartition, int nombre); void setRepartitionResponseTime (char * liste); /* define the file name to use to dump statistic in file */ void setFileName(const char* name); void setFileName(const char* name, const char* extension); void initRtt(const char* name, const char* extension, unsigned long P_value); /** * Dump data periodically in the file M_FileName */ void dumpData (); void dumpDataRtt (); /** * initialize the class variable member */ int init(); /** * computeDiffTimeInMs. * * This method calculates elaped time in ms * * @param tf = final date * @param ti = initial date * * @return number of ms between the 2 dates */ static long computeDiffTimeInMs (struct timeval* tf, struct timeval* ti); /** * msToHHMMSS. * * This converts an unsigned long containing a number of ms * into a string expressing the same value in format HH:MM:SS. * * @param P_ms. * * @return a pointer on a static string containing formated time */ static char* msToHHMMSS (unsigned long P_ms); /** * msToHHMMSSmm. * * This converts an unsigned long containing a number of ms * into a string expressing the same value in format HH:MM:SS:mmm. * * @param P_ms. * * @return a pointer on a static string containing formated time */ static char* msToHHMMSSus (unsigned long P_ms); /* Get a counter ID by name. */ int findCounter(const char *counter, bool alloc); int findRtd(const char *name, bool start); void validateRtds(); int nRtds(); private: unsigned int M_rtpEchoErrors; unsigned long long M_counters[E_NB_COUNTER]; static unsigned long long M_G_counters[E_NB_G_COUNTER - E_NB_COUNTER]; #define GENERIC_C 0 #define GENERIC_PD 1 #define GENERIC_PL 2 #define GENERIC_TYPES 3 unsigned long long *M_genericCounters; str_int_map M_genericMap; int_str_map M_revGenericMap; int_str_map M_genericDisplay; str_int_map rtd_started; str_int_map rtd_stopped; #define RTD_COUNT 0 #define RTD_SUM 1 #define RTD_SUMSQ 2 #define RTD_TYPES 3 unsigned long long *M_rtdInfo; str_int_map M_rtdMap; int_str_map M_revRtdMap; T_dynamicalRepartition** M_ResponseTimeRepartition; T_dynamicalRepartition* M_CallLengthRepartition; int M_SizeOfResponseTimeRepartition; int M_SizeOfCallLengthRepartition; struct timeval M_startTime; struct timeval M_pdStartTime; struct timeval M_plStartTime; bool M_headerAlreadyDisplayed; char* M_fileName; ofstream* M_outputStream; bool M_headerAlreadyDisplayedRtt ; char* M_fileNameRtt ; ofstream* M_outputStreamRtt ; double M_time_ref ; T_pValue_rtt M_dumpRespTime ; unsigned int M_counterDumpRespTime ; unsigned long M_report_freq_dumpRtt ; /** * initRepartition * This methode is used to create the repartition table with a table of * unsigned int the reparition is created like following, with Vi the given * value in the table * 0 <= x < V1 * V1 <= x < V2 * ... * Vn-1 <= x < Vn * x >= Vn * So the repartition table have the size n+1 if the given table has a size * of n */ void initRepartition(unsigned int* repartition, int nombre, T_dynamicalRepartition ** tabRepartition, int* nbTab); /** * createIntegerTable * this method try to create a table of unsigned int with the list of char* * passed in parameters * if it succed, it's return true (1) * else it's return false (0) */ int createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList); /** * isWellFormed * this method check if the char* passed in parameter in really a list of * integer separated with comma. * if yes, it's return true (1) * else, it's return false (0) */ int isWellFormed(char * P_listeStr, int * nombre); /** * updateRepartition * The method looks for the place to set the value passed in parameter * Once found, the associated counter is incremented */ void updateRepartition( T_dynamicalRepartition* tabRepart, int sizeOfTab, unsigned long value); /** * resetRepartition * Zeros out all repartition counters. */ void resetRepartition(T_dynamicalRepartition* P_tabReport, int P_sizeOfTab); /** * sRepartitionHeader * return a string with the range description of the given repartition */ char* sRepartitionHeader(T_dynamicalRepartition * tabRepartition, int sizeOfTab, const char* P_repartitionName); /** * sRepartitionInfo * return a string with the number of value in the differente range of the * given repartition */ char* sRepartitionInfo(T_dynamicalRepartition * tabRepartition, int sizeOfTab); /** * UpdateAverageCounter * This methode compute the real moyenne with the passed value on the given * counter */ void updateAverageCounter(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares, unsigned long P_value); /** * computeStdev * This method computes the standard deviation using our recorded mean * and recorded mean square. */ double computeStdev(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares); /** * computeMean * This method computes the recorded sum and count. */ double computeMean(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed); double computeRtdMean(int which, int type); double computeRtdStdev(int which, int type); /** * Effective C++ * * To prevent public copy ctor usage: no implementation */ CStat (const CStat&); /** * Effective C++ * * To prevent public operator= usage: no implementation */ CStat& operator=(const CStat&); }; /** * This abstract class provides the ability to sample from a distribution. */ class CSample { public: virtual double sample() = 0; virtual int textDescr(char *s, int len) = 0; virtual int timeDescr(char *s, int len) = 0; virtual double cdfInv(double percentile) = 0; virtual ~CSample(); private: }; /* Always return a fixed value for the sample. */ class CFixed : public CSample { public: CFixed(double value); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); private: double value; }; /* Return the default scenario duration. */ class CDefaultPause : public CSample { public: CDefaultPause(); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); private: }; /* Uniform distribution. */ class CUniform : public CSample { public: CUniform(double min, double max); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); private: double min, max; }; #ifdef HAVE_GSL /* Normal distribution. */ class CNormal : public CSample { public: CNormal(double mean, double stdev); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); protected: double mean, stdev; gsl_rng *rng; }; /* Lognormal distribution. */ class CLogNormal : public CNormal { public: CLogNormal(double mean, double stdev) : CNormal(mean, stdev) {}; double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); }; /* Exponential distribution. */ class CExponential : public CSample { public: CExponential(double mean); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); private: double mean; gsl_rng *rng; }; /* Weibull distribution. */ class CWeibull : public CSample { public: CWeibull(double lambda, double k); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); private: double lambda, k; gsl_rng *rng; }; /* Pareto distribution. */ class CPareto : public CSample { public: CPareto(double k, double xsubm); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); protected: double k, xsubm; gsl_rng *rng; }; /* Generalized Pareto distribution. */ class CGPareto : public CSample { public: CGPareto(double shape, double scale, double location); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); protected: double shape, scale, location; gsl_rng *rng; }; /* Gamma distribution. */ class CGamma : public CSample { public: CGamma(double k, double theta); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); protected: double k, theta; gsl_rng *rng; }; /* Negative Binomial distribution. */ class CNegBin : public CSample { public: CNegBin(double p, double n); double sample(); int textDescr(char *s, int len); int timeDescr(char *s, int len); double cdfInv(double percentile); protected: double p, n; gsl_rng *rng; }; #endif #endif // __STAT_H__ sipp-3.7.2/include/strings.hpp0000664000000000000000000000200714525516253013222 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #ifndef __SIPP_STRINGS_H__ #define __SIPP_STRINGS_H__ int get_decimal_from_hex(char hex); void get_host_and_port(const char *addr, char *host, int *port); void trim(char *s); #endif /* __SIPP_STRINGS_H__ */ sipp-3.7.2/include/task.hpp0000664000000000000000000001006514525516253012476 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #ifndef __TASK__ #define __TASK__ #include #include #include #include /* Forward declaration of call, so that we can define the call_list iterator * that is referenced from call. */ class task; typedef std::list task_list; /* This arrangement of wheels lets us support up to 32 bit timers. * * If we were to put a minimum bound on timer_resol (or do some kind of dynamic * allocation), then we could reduce the level one order by a factor of * timer_resol. */ #define LEVEL_ONE_ORDER 12 #define LEVEL_TWO_ORDER 10 #define LEVEL_THREE_ORDER 10 #define LEVEL_ONE_SLOTS (1 << LEVEL_ONE_ORDER) #define LEVEL_TWO_SLOTS (1 << LEVEL_TWO_ORDER) #define LEVEL_THREE_SLOTS (1 << LEVEL_THREE_ORDER) /* A time wheel structure as defined in Varghese and Lauck's 1996 journal * article (based on their 1987 SOSP paper). */ class timewheel { public: timewheel(); int expire_paused_tasks(); /* Add a paused task and increment count. */ void add_paused_task(task *task, bool increment); void remove_paused_task(task *task); int size(); private: /* How many task are in this wheel. */ int count; unsigned long wheel_base; /* The actual wheels. This is a variation on having one wheel for * seconds, another for minutes and a third for hours - in this * model, the first wheel holds tasks that should be scheduled in * the next 2^12ms (~4s), the second wheel holds tasks that * should be scheduled between 2^12 and 2^22ms (~4s-~69m), and * the third wheel holds tasks that should be scheduled between * 2^22ms and 2^32ms (~69m-~8 years). */ task_list wheel_one[LEVEL_ONE_SLOTS]; task_list wheel_two[LEVEL_TWO_SLOTS]; task_list wheel_three[LEVEL_THREE_SLOTS]; /* Calls that are paused indefinitely. */ task_list forever_list; /* Turn a task into a list (based on wakeup). */ task_list *task2list(task *task); }; class task { public: task(); virtual ~task(); virtual bool run() = 0; /* Our abort action. */ virtual void abort(); /* Dump task info to error log. */ virtual void dump() = 0; protected: /* Wake this up, if we are not already awake. */ void setRunning(); /* Put us to sleep (we must be running). */ void setPaused(); /* When should this task wake up? */ virtual unsigned int wake() = 0; /* Is this task paused or running? */ bool running; private: /* Run and Pause Queue Maintenance. */ void add_to_runqueue(); bool remove_from_runqueue(); void add_to_paused_tasks(bool increment); void recalculate_wheel(); /* This is for our complete task list. */ task_list::iterator taskit; /* If we are running, the iterator to remove us from the running list. */ task_list::iterator runit; /* If we are paused, the iterator to remove us from the paused list. */ task_list::iterator pauseit; /* The list that we are stored in (only when paused) . */ task_list *pauselist; /* The timing wheel is our friend so that it can update our list pointer. */ friend class timewheel; }; task_list * get_running_tasks(); int expire_paused_tasks(); int paused_tasks_count(); void abort_all_tasks(); void dump_tasks(); #endif sipp-3.7.2/include/time.hpp0000664000000000000000000000201514525516253012466 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #ifndef __SIPP_TIME_H__ #define __SIPP_TIME_H__ unsigned long getmilliseconds(); unsigned long long getmicroseconds(); void sipp_usleep(unsigned long usec); void update_clock_tick(); #endif /* __SIPP_TIME_H__ */ sipp-3.7.2/include/urlcoder.hpp0000664000000000000000000000166514525516253013361 0ustar /* * 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 * * Author : Jérôme Poulin - 20 Apr 2021 */ #ifndef SIPP_URLCODER_HPP #define SIPP_URLCODER_HPP std::string url_encode(const std::string& str); std::string url_decode(std::string str); #endif //SIPP_URLCODER_HPP sipp-3.7.2/include/variables.hpp0000664000000000000000000000674614525516253013517 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. */ #ifndef _CVARIABLE #define _CVARIABLE #include #include #include #include typedef std::map str_int_map; typedef std::map int_str_map; typedef std::map int_int_map; #define BUFFER_SIZE 512 #define MAX_MATCHING_EXPR 50 #define REGCOMP_PARAMS REG_EXTENDED #define REGEXEC_PARAMS 0 enum T_VarType { E_VT_REGEXP = 0, E_VT_DOUBLE, E_VT_BOOL, E_VT_STRING, E_VT_UNDEFINED }; class CCallVariable { public: bool isSet(); bool isDouble(); bool isBool(); bool isRegExp(); bool isString(); // WARNING : setMatchingValue does't allocate the memory for the matching value // but the destructor free the memory void setMatchingValue(char* P_matchingValue); char* getMatchingValue(); /* When the variable is used for a string, these functions should be called. */ // WARNING : setString does't allocate the memory for the matching value // but the destructor free the memory void setString(char *s); char *getString(); /* When the variable is used for a double, these functions should be called. */ void setDouble(double val); double getDouble(); /* When the variable is used for a bool, these functions should be called. */ void setBool(bool val); bool getBool(); /* Cast this to a double variable, return the result in newValue. */ bool toDouble(double *newValue); // constructor and destructor CCallVariable(); ~CCallVariable(); private: T_VarType M_type; char* M_matchingValue; int M_nbOfMatchingValue; double M_double; char* M_stringValue; bool M_bool; }; class AllocVariableTable; class VariableTable { public: VariableTable(VariableTable *parent, int size); VariableTable(AllocVariableTable *src); VariableTable *getTable(); void putTable(); int size; CCallVariable *getVar(int i); protected: virtual ~VariableTable(); void expand(int size); int count; int level; CCallVariable **variableTable; VariableTable *parent; }; class AllocVariableTable : public VariableTable { public: AllocVariableTable(AllocVariableTable *av_parent); ~AllocVariableTable(); int find(const char *name, bool allocate); char *getName(int i); void validate(); void dump(); private: AllocVariableTable *av_parent; str_int_map variableMap; int_str_map variableRevMap; int_int_map variableReferences; }; void clear_int_str(int_str_map m); void clear_str_int(str_int_map m); void clear_int_int(int_int_map m); #endif sipp-3.7.2/include/version.h0000664000000000000000000000006614525516253012661 0ustar #define SIPP_VERSION VERSION #define VERSION "v3.7.2" sipp-3.7.2/include/version.h.in0000664000000000000000000000007114525516253013262 0ustar #define SIPP_VERSION VERSION #define VERSION "@VERSION@" sipp-3.7.2/include/watchdog.hpp0000664000000000000000000000343414525516253013336 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #ifndef WATCHDOG_HPP #define WATCHDOG_HPP #include "task.hpp" class watchdog : public task { public: unsigned int wake(); watchdog(int interval, int reset, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers); bool run(); void dump(); private: int interval; int reset_interval; int minor_threshold; int major_threshold; int minor_maxtriggers; int major_maxtriggers; unsigned long last_fire; unsigned long last_trigger; int major_triggers; int minor_triggers; }; #endif sipp-3.7.2/include/xp_parser.h0000664000000000000000000000241114525516253013173 0ustar /* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #ifdef __cplusplus extern "C" { #endif int xp_unescape(const char *source, char *dest); 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_cdata(void); int xp_get_content_length(const char *P_buffer); #ifdef __cplusplus } #endif sipp-3.7.2/pcap/0000775000000000000000000000000014525516253010321 5ustar sipp-3.7.2/pcap/dtmf_2833_0.pcap0000664000000000000000000000137414525516253013023 0ustar òCs::P6 $E,@'[/0D8N C::P6 $E,@'[,e/1D8N @C_ ::P6 $E,@'Ye/2D8N C] ::P6 $E,@'Xe/3D8N C ::P6 $E,@'Wie/4D8N C ::P6 $E,@'V(e/5D8N @CG ::P6 $E,@'Te/6D8N Cܕ ::P6 $E,@'S&e/7D8NC ::P6 $E,@'S&e/7D8NC/ ::P6 $E,@'S&e/7D8Nsipp-3.7.2/pcap/dtmf_2833_1.pcap0000664000000000000000000000137414525516253013024 0ustar òCs::P6 $E,@'{038N C::P6 $E,@'{,e138N @C_ ::P6 $E,@'ye238N C] ::P6 $E,@'xe338N C ::P6 $E,@'wie438N C ::P6 $E,@'v(e538N @CG ::P6 $E,@'te638N Cܕ ::P6 $E,@'s&e738NC ::P6 $E,@'s&e738NC/ ::P6 $E,@'s&e738Nsipp-3.7.2/pcap/dtmf_2833_2.pcap0000664000000000000000000000137414525516253013025 0ustar òC ::P6 $E,@n'SjZ8N Ci ::P6 $E,@m'S2ekZ8N @C ::P6 $E,@l'QelZ8N C ::P6 $E,@k'PemZ8N CS ::P6 $E,@j'OoenZ8N C ::P6 $E,@i'N.eoZ8N @C; ::P6 $E,@h'LepZ8N C>::P6 $E,@g'K,eqZ8NCP>::P6 $E,@f'K,eqZ8NCZ>::P6 $E,@e'K,eqZ8Nsipp-3.7.2/pcap/dtmf_2833_3.pcap0000664000000000000000000000137414525516253013026 0ustar òCQ ::P6 $E,V@'4&y@8N C ::P6 $E,W@'3eey@8N @Ch ::P6 $E,X@'2$ey@8N Cj ::P6 $E,Y@'0ey@8N C ::P6 $E,Z@'/ey@8N CR ::P6 $E,[@'.aey@8N @C ::P6 $E,\@'- ey@8N C? ::P6 $E,]@'+_ey@8NC ::P6 $E,^@'+_ey@8NC ::P6 $E,_@ '+_ey@8Nsipp-3.7.2/pcap/dtmf_2833_4.pcap0000664000000000000000000000137414525516253013027 0ustar òC "::P6 $E,@'D8N Cp::P6 $E,@'e8N @Co::P6 $E,@'Be8N C ::P6 $E,@'e8N ClZ ::P6 $E,@'e8N C ::P6 $E,@'e8N @C[ ::P6 $E,@'>e8N CD ::P6 $E,@'}e8NCD ::P6 $E,@'}e8NCD ::P6 $E,@'}e8Nsipp-3.7.2/pcap/dtmf_2833_5.pcap0000664000000000000000000000137414525516253013030 0ustar òCx::P6 $E,@'b8N C|::P6 $E,@'e8N @C::P6 $E,@'`e8N Cb::P6 $E,@'e8N C::P6 $E,@'݀e8N C$::P6 $E,@'e8N @C$M::P6 $E,@~'[e8N C]::P6 $E,@}'e8NC͛::P6 $E,@|'e8NCٛ::P6 $E,@{'e8Nsipp-3.7.2/pcap/dtmf_2833_6.pcap0000664000000000000000000000137414525516253013031 0ustar òC&::P6 $E, @c'b8N C2::P6 $E, @b'ꡀe8N @C::P6 $E, @a'`e8N C::P6 $E,@^'e8N C::P6 $E,%@G'ހe8N Ck::P6 $E,1@;'址e8N @C::P6 $E,6@6'\e 8N C'::P6 $E,7@5'⛀e 8NC}::P6 $E,8@4'⛀e 8NC::P6 $E,9@3'⛀e 8Nsipp-3.7.2/pcap/dtmf_2833_7.pcap0000664000000000000000000000137414525516253013032 0ustar òC. ::P6 $E,V@'! 8N C} ::P6 $E,W@'`e 8N @Cp ::P6 $E,X@'e 8N C ::P6 $E,Y@'ހe 8N Cg ::P6 $E,Z@'Νe 8N Cص ::P6 $E,[@'\e 8N @C ::P6 $E,\@'e !8N C|Q ::P6 $E,]@'Ze "8NCQ ::P6 $E,^@'Ze "8NCQ ::P6 $E,_@ 'Ze "8Nsipp-3.7.2/pcap/dtmf_2833_8.pcap0000664000000000000000000000137414525516253013033 0ustar òCR::P6 $E,@'? =8N Cb::P6 $E,@'~e >8N @C!::P6 $E,@'=e ?8N Co::P6 $E,@'e @8N C::P6 $E,@'e A8N C ::P6 $E,@'ze B8N @CY ::P6 $E,@'9e C8N C ::P6 $E,@'xe D8NCZ ::P6 $E,@'xe D8NCf ::P6 $E,@'xe D8Nsipp-3.7.2/pcap/dtmf_2833_9.pcap0000664000000000000000000000137414525516253013034 0ustar òC::P6 $E,@x' e 8N Ce::P6 $E,@w'րe f 8N @CK::P6 $E,@v'e g 8N C::P6 $E,@u'Te h 8N Cw::P6 $E,@t'e i 8N CX6::P6 $E,@s'Ҁe j 8N @C::P6 $E,@r'e k 8N C::P6 $E,@q'Ѐe l 8N C@::P6 $E,@p'Ѐe l 8N CK::P6 $E,@o'Ѐe l 8N sipp-3.7.2/pcap/dtmf_2833_pound.pcap0000664000000000000000000000137414525516253014011 0ustar òCa3::P6 $E,@w':( i8N C::P6 $E,@v'9ge i8N @C::P6 $E,@u'8&e i8N C::P6 $E,@t'6e i8N C|k::P6 $E,@s'5e i8N C::P6 $E,@r'4ce i8N @C ::P6 $E,@q'3"e i8N C|U ::P6 $E,@p'1ae i8N CU ::P6 $E,@o'1ae i8N CU ::P6 $E,@n'1ae i8N sipp-3.7.2/pcap/dtmf_2833_star.pcap0000664000000000000000000000137414525516253013635 0ustar òCV ::P6 $E,@'V/ O8N CǤ ::P6 $E,@'Une O8N @C ::P6 $E,@'T-e O8N C@ ::P6 $E,@'Re O8N C& ::P6 $E,@'Qe O8N Cb ::P6 $E,@'Pje O8N @C?+ ::P6 $E,@'O)e O8N Cy ::P6 $E,@'Mhe O8N Cz ::P6 $E,@'Mhe O8N Cz ::P6 $E,@'Mhe O8N sipp-3.7.2/pcap/g711a.pcap0000664000000000000000000021674014525516253012020 0ustar ò@=V&&Pfv" E@@#  R€@=f&&Pfv" E@@#  RQ@=&&Pfv" E@@#  Q`@=w&&Pfv" E@@#  Po@=[&&Pfv" E@@#  O~@=Bc&&Pfv" E@@#  N@=|&&Pfv" E@@#  M@=H&&Pfv" E@@#  L@=ɽ&&Pfv" E@@#  Kp@= 3&&Pfv" E@@#  Jɀ `@=1&&Pfv" E@@#  I؀ P@= &&Pfv" E@@#  H @@=Ē &&Pfv" E@@#  G 0@=% &&Pfv" E@@#  G @=| &&Pfv" E@@#  F @=. &&Pfv" E@@#  E# @=k &&Pfv" E@@#  D2 @= &&Pfv" E@@#  CA@=WR &&Pfv" E@@#  BP@= &&Pfv" E@@#  A_@=< &&Pfv" E@@#  JJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=> &&Pfv" E@@#  DZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=v&&&Pfv" E@@#  .fZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=ݛ&&Pfv" E@@#  ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  ӀpZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=D&&Pfv" E@@#  `ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  рPZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=M5&&Pfv" E@@#  1Ā@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=L&&Pfv" E@@#  M0ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=1&&Pfv" E@@#  qʀ ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=5&&Pfv" E@@#  ٦ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJZZZJJJJJJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZJJJJJJJJJ@= |&&Pfv" E@@#  ]JJJJJJJJJJJJJJJJJJJZZZZZZZJJJJJJJJZJZZZZZZZZZZZZJJZJJJJJZZZJJZZZZJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZZJZZZJJJJJJJJJJJJJJJJJJZJZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ@==&&Pfv" E@@#  ̀JZJJJJJJJJJJJrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJrJJrrrrrrrrrrrrrrrrrrrrrrrrrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZJJJZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=b&&Pfv" E@@#   ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#    !ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJzZZZ@=CM&&Pfv" E@@#  !"ZZZZZZZZZJZZZZZJJZJJJJJJrrrrrrrrrrrrrrrzzzzzzzzzzzfffffffffffbbbbbbbbbbbbbbbbbbbbnnnbnbnnnnnnnnnjjnjjjjjjjjjjnjnnnjnjnnjjjjjnjjnjjjjjjjjjjjnjjjjnnn@=&&Pfv" E@@#  ǀ"#nnnnnnnnnbbbbbbbbbbbbbbbbbbfffffffzzzzzzzzzrzrJrrJrrrrJZrJZJJZZZZZZZZ@=7&&Pfv" E@@#  !#$ZZZZZZZZZZZZZZZZZZZZJJJJJJJJJJJJJJJJJJJJJrJrrrrrrrJrrrrrrrJJrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrJrrrJJJrJJJJzbJJrZZJJZJJJZZJZZZZZJZZJJZZJJJJZZZZ@=&&Pfv" E@@#  <$%ZZZJJZZZZZJZZJJJZZZJZZZZZZZZZZZZZZZJZZZZJZZJZZZJJJZJJJZZJJrrJJJZZZZZJJrJJJZZJrJJJJJrrrrrrrJJJJrrJ@=!&&Pfv" E@@#  އ%&prJJZZrrrrrrJZJrrrrzzzrZZZZJJrzzrrJZrrrrrJJzrJJJJrrrJrrrrrrrrJrJJrzrrrrrzzJrrJZJJJrrrJJJJrrJJJJrrrrJJrJJJZJrrJJJJrJJZJJJJJJJJrJJJJJJJJZZZJJZZZZJJJJZZZJJZZZZJJZZZJZJJZZZZZZZJZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  р&'`JZZZJZZZZZJZZZZZZZZZZZZZZJZJJZZZJJZJrJJZZZZJJZZZZrrZZJZZZJZZJJJZJZZZJrJZZJZZJJJrZZZZJJZZJZJJZZrrJJZZJJJZJJJJJJrrZZJZJJJJJrrJJJrrJJZJzrZZJJJJZZJrrrJZZJrrrJZJZZJJJrrJJJZJrJZZJZrJZrrrzZJJZJJZJ@=o &&Pfv" E@@#  Q'(PZZJZZZZJZZZJJJJrJZZZZZZZZZZZJZJZZZZZZZZZZZZZZJZJZJZZJZZZZZZZZZZZZZZZZZZZZZZJZZZJZZZZJ@=)&&Pfv" E@@#  8()@ZZZZZZZJZZZZZZZJZZZZZJZJJZJZZZZJZJZZZJZZZZZZZZZZZZJZZZZJZJJZZJZZZZZJZJZZJZJZZZZZZZZZJrZZJrZZJZJJJJZJJZZJZZZJZJrZJJZJZJZZZZZZZZJJZZZZZZZJJZZZZZZZZZZZZZZJZZZZJ@=i&&Pfv" E@@#  0)*0ZZJZZZZZZZZZJZZrJZZJZZZZZZZZZJZZJZZZZZZZZZZZZZZZZZZZZZZJZZZZZJZZJZZZJZZZZZZJJZZJZZZZZZZZZZZZZZZZZZJZZZZZJJJJZZZJZZZZJZJJZZZJZZJJZ@=l &&Pfv" E@@#  *+ ZJJJJZJZZZJJZZZZrJZZZJJJZJrJZZJZJJZZJZrJZJrJZZrJJZZJJZZZZJJZJrJZJZJZZJZZZZJZZZJJJZZJJJZZJrZZZZJZJrZZZZZJZZZJJJJZZZZZZZZZZJZJJJJZJZZZZZZZZZJJZJZZZZJJJrJZZZZZZJrrJJZZJJJJJZJJJZZZJJJZZJJrJJJJJZZZJJZZZJJJZJJJJZJJZZZZJJZZJJ@= &&Pfv" E@@#  vЀ+,JJZZZZZZZZZZJZZZZZZJJZZZZJZZZZZZZZZZZJZZZZZZZZZZJJZZZZZJZZZZZZJZZZJZZZZZZJJJJJJJZZJJZZZZJJJZZJJJJZJrJJJJJJJZJZJJJJZZJJJZZZZJJZZJJJJJZZZJJJJJJJJJJJJJZJJJJZJJZZJJJZZZJJZZJJJZZZJZZZJJJZJJJZZZZJZZZZZJZZZZZJJZZZJJJJJZZJZZZZJJZZZJJZZZZZZZZJJJJZ@=X &&Pfv" E@@#  L,-JJJJZZZZZJJJZJJJJZZZZJZZZJJJZZZZZJJJZZJJJJJZZJZZZZZZJJZZJZZZZJJJJZZZZZZZZZZZZZZJZZZZZZZZZZJJZZZZZZZZZZZZZJZZZZJJZJZZZZZZZJJJZZZZZZZZZZZZZZZJJJJJZZZZJJJJJJJZZZJJZZJZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJZJZZZZJJJZZZZZZZZJZZZZJ@= &&Pfv" E@@#  x--ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=D &&Pfv" E@@#  À..ZZZZZZZZZZZZZZZZZZZZZZZZJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZJJZJJJJJJJJJJJ@=ص &&Pfv" E@@#  //JJJJJJJJJJJJJJJJJJJJJJJJJJJZZJZZZZZZZZZZZZ@=+ &&Pfv" E@@#  Ԋ00ZZZZZZZZZZZZZZZZJJZZZZJZZZJJJJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZJJZZJZZZJZZJJZZJJJZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJZJJJJJJJJJJJJJJJJ@= &&Pfv" E@@#  11rrJJJJrJJJJJJJJJJJZZJZZZZZZZZZZZZZZZZZZZZZZZJZZZJJZJJJJJJJJrrrrrzzzzzzzffffffffffffzzffzzzzzzzrrrrJJJJJJJJJJZZZZZZZJZZZZZZZJrrzffbnjjjnnbfzzrJZ镗rrzbnbnbbnnbbbzzffzzrrrrJZJZZJrrrzzfbbbnnjjjjjnnbbf@= &&Pfv" E@@#  /B22ffzrJZ闑JzffbbfnnbbbffbfffzrrrJJZZJJJJJJzfffbbbnnnjnnbbbbfzrrJZ镗rffffbnjnbbffbnbffzzzrrJZZJJJrJrzffbbnnjjjjjjjnnbffzrJZ闑rfbbbbnjjnnbfnnnbbzzfzrrJZrzfzrzfffbnnnnjjjjjjnbfzrJZ@=> &&Pfv" E@@#  33闑JrfbbbnnnjnbbnnbbfffzzzrJZZJrrJrfffbnjjjjjjnbfzrrZ闓JzfnnbnjjnnbbbnnbfzzzrrJZJJZJzzfbfnjjjjjnnbbzrJZ闓ZzzbbfbnbbnfzfbfbfzzrJJJZZrffffbnjjjjjjjnbbfzrJZᕑ@= &&Pfv" E@@#  ŀ44JrffzfbbfbfzzffffzzrJJJJzzfffbnnnnbnnbfzrJZᕑZJJrzrrrZrrrrJZJJZZZrrzffzzfzzzJJJZᕓZZJrJJZJrzrZZJJrJJJJJrrffzzrrrrJJZ鑟ZJZZrrJJfjbrZZZrrZ@=u&&Pfv" E@@#  955pZJzzzrrzzrJJJzfzJZ铅jfZrJ홛ZjrZffJbbrrjJJbjnzzzzzrᓁZ᝛ZJZrJJnrrfnbrZJffJJ턉 őZz ՕrbfZJbfnnjbZZJ rr b靗@=&&Pfv" E@@#  M66`nbjfzrrJz 4 ZfrfznfrZzjnzZ靀n 5 jrrZZbZZzfrZZzbbzᗅ54ZrjfJrjf靕rzrbfrZZzbfJ鑙z 4 nnzfZjrZJrrJZrzr呅@=&&Pfv" E@@#  77P ŇZnnffjfrjrZZZZᝃ4 bbjbfnjjfJZJJrrrJ흁j6 nnnffJJffzJ47rJrrnJZZrzzJ66ZJjrZzJZZJZ陀515f@=&&Pfv" E@@#  B88@nJzfJZ啟44 brnrrnbzZnrbbrJrzffzrrJJJ镑ZrzzzzzbnjjjjnnnnnbbffzrZ핕rzfbbbnjjjnnbbffrrZZrzfbbnnjjjjjnbffzrJZZ@=f&&Pfv" E@@#  `~990ZJrrzfbbbnnnnnnbbbfzzrrJZZZZZZJJrrrzzzzzzzzrrrrJJJZZZZZZZZZZZZZJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZJZZZZZZZJJZZJJJJJZZJJJJJJJJJJ@=Y|&&Pfv" E@@#  {:: JJZJJZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJJJZZJJJZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=L&&Pfv" E@@#  'e;;ZZZ镕ZrrZ镕ZzbjjzJZᕝnfffZzfnzzfzJJznjnnnjnbfJJZZZJrzfb@=yh&&Pfv" E@@#  <<bnjjjjnnbffzrrJJZZJrzzfbbnbbbnnnnnbbbfzzrrrJZZZ镕ZJrrrrrJrrrrrrrJJJZZZ镕ZJrzffffffffzfffffzzzzzrrrrrJJJJJJrrrrrJZZJzfbnjjjjn@=a&&Pfv" E@@#  ^==rZJfnzZZZzfffbbbfzzrJZZZZZJrrzrrrJ靄JZZZZZJJrfffbbffbfzrJJJJJrrzrrJJZᑅJrrrzJZrJZZrrzbbbbnnnnjnbfzzzzffbbfffzJ퓄ZZZzbffbfJJzzrrzffnjjjnffbnnjjjnnbzᓅzzznjbbbfJrfzr@=&&Pfv" E@@#  GS?>zfbnjnbbbnnjjjnnbr퟇zzznnnfJJzzJrfbnjjjnbnnbnnbbbfJ陆zbrfjjnfzffrznnbjbbbnbbbbbfrᓄzbJfjjnrzbzfnnnjbbfzzffzzJ퓅ffJnbfJznfzbjjjjnfzzrJrJZ韄@=;&&Pfv" E@@#  D@?rjZJfbJrjbrnjjjnnbzrzJZJJᗙnbZjrzZnjJfjjnbzzJZZ闟znrzrffZznffjjbzrrZ闟rzrnnzbbJZrrJfnjjnnfJJZ闓fnJfbzbzbrfjjnbbzJZ핑JjzzjJJfJJ@=J&&Pfv" E@@#  A@bffjjjjnnfzrJ闝ZbfbnrrJZrzfnjnnbzrJ镑fjbjnrJZZJrznjnbzrJZ鑝bbrZJzffjjjnfrJZ헟JfJJfbnjnfrJJ铛zZZfjnfzrᗟJz@=%&&Pfv" E@@#  WBAbrZfbfzJ鑙JrbZnjjnrZbnbfrzznjfffJ铟bnzzzrJzzffbnnnbbbbbfzzJZ镕JrrrzfbbbbbbbfffzrJZ镗ZZZZZZZ镗@=&&Pfv" E@@#  CB镑镗镕镕镕@=U&&Pfv" E@@#  DC镕ZZZZZZZJJJJJJZZZZZZZZZZ@=&&Pfv" E@@#  jEDpZZJJJJrJrrrrrrrzrrrJJZZZZZZZZZZZZZJJJJrrrrrzzzzzzzzzzzzzrJJJrrrJJJZJJJJJJrrrrrrrrzjnffffzzzzzzzfzzrJJrJbjzrrJJZJJrrrzfffzffzzfffffzzzzzffzzzzzrrrzzrzzzzrzfrrfffbjnbfrrzbfzzbjjbfzrJZJJJzfffbbnnnjjjnjjjnnnnfbfzZrn@=&&Pfv" E@@#  FE`jbjjnjjrbnznjfJ푙JJJZrbjfrJ靅ZZrrZrbnzrZ铛jjZJznzJZ푟zbnnrj@=o&&Pfv" E@@#  OGFPfJZ핝zznzznjnzJ闙bbrZnjnzrZ靅fjnZbbzJ푛JJZfjjfrZᑛJZJznjfrZᗛ@=&&Pfv" E@@#  HG@nzJJbjjzrJᕟZbZJrfbjbzJ鑟zbnzZZZJbnffbbzJ镑JfbbbbnfrZZZZrzfffzzrZ闓JfbnnnbfrJZJrzfbnnbzrJZ헓rbjj@=Z&&Pfv" E@@#   gIH0bfzJZrfbnnnnnfrJ闓ZbjfrJZrbjnzJ鑙ZJzbJᕕJzfjjbJᓄjj r핟nbfbbJZZᙃn5 JbffjzJffZ遈 75ZZ z@=&&Pfv" E@@#  QJI bbznnZr 65過n 5jrnjnn՗ 77b 4 bᗗbjjՕ56r5 nJᗗzjjrᙍ  15 jŕrfnnZ홍51 frjjfZ@=D &&Pfv" E@@#  KJf 7 n陁fbbfJZZZZ哄jfjrZbbrZ啟JnnrZfnfzr铛zrZznjjnbfJ푙jjJZZZfbbjnnfJᕟzb@=+ &&Pfv" E@@#  i,LKJJrZrfbnjnzZ푛Jzrnfr铇zJf bjᄛJnbbfZJfZ靇ZJ 06fjfJ5fZzZZn嗅jJ3<4JzZzz74brfrzrេb090j 136 @=1 &&Pfv" E@@#  MMKjrJjՓ 2:?nş 56334nnjՑf 5?<4ŕn 5rZrrZzrjnbzZz铟镗镗Jrzzfffffbbbbfz@= &&Pfv" E@@#  e6NLzzrJJJZZZZZJrrzzzzzzzzzzrrrrrrJJJZZZZZZZZZZZJZZZJJJJJJZZZZZZZZZZJJZZJJrzrJJJrrrrrJrrJrrrrrJJJJJJrrrrrrrzzzzrzrzzzzzzzrJJJrrzzzzzzzfzzzzzfzzfzzrrrrzzzzzrJrrrrzrrJJZJJZZJJJJZZZZZZZZJJJJJJZZZ@= &&Pfv" E@@#  ZOMZZZZZZZZZZZZZZZZZZZZJJZ핕闗@= &&Pfv" E@@#  `PNJZZrzzzzzffzzzzzrrzrzffrzffzrzfffJrfjbJrbjnffzrJZrfnbzzzrJzfzznjnffnjnfzbjnnnfrzbbfzzbfzfbfrJrrzrrrfbzfnfZrfrJZZZZrrJZZZJJZZZJZZZrzzJJJJJ@= &&Pfv" E@@#  XQOJJZZJᕗZJZrrJZZZZJrrZZZZZZzzJJzfrJZrzzzzJJffrZJrJZZZJJJJJZZZZZZZZZ镕핕Jzbfrrfnnbbbfbnjbfbnjj@=y &&Pfv" E@@#  fRPnjnnnjnbfzzzrJZJJJZZJJrzrJrffbbbbffnjnbffbfzZZzJJZJZZrrrJffrznjbfbbZfnrrfjnJbbzbjfrnjnjffbZfz哅@= &&Pfv" E@@#  SQ᝗ᗕrZJZJrZffJrrJJZzZrzzfrzzfbbnbnjnjnjjjjjjnjjnjjnbnnjbjjjjj@=$c &&Pfv" E@@#  _TRjjjnbfbbbbzrJZJrrrJZZZZZrrJZJZJrrJZJJJrzzrJZZZJrzJZZZJJZZZZZZZ@=Z &&Pfv" E@@#  USpZZZZZZJJrrrrzzrrrrJZJZ镗JzfnjbzZ푙znnZJ jz呄br᝛Znj  frjJfn@=M&&Pfv" E@@#  ހVT`fbՓn fnzrnjbrfnnnnz헄b jjrfjzfbznnzbbzJᑄrjbjfbnfbjfzbfrzzZ흄nfjzJZjnbbfrzzJJrZ啙bbnf@=&&Pfv" E@@#  lWUPjbffzJrzZZJ흅rbfnfbjbzzrJJZZZ鑅nnjbZrjjbzrrJZ啝bfjbJZnjnrJrZ啟zzjjfJfbjnzrJZᕓZzbbb@=O8&&Pfv" E@@#  XV@bnnfzfzrzbnnnbfzJZ鑟ZJfnnbfrJ闑JzbjjjjjnbfzJZZJrzfbjbjjnjjnnjjjnnnbbbfrJJJZZJrrrzffbbbfbbbffffzzzrJJJZ@=\k&&Pfv" E@@#  YW0ZznrnjnjnbfzJJJJZZJZJJbbfzrfjfjbzZZrZZJzzZZJJZZZJJZZZJrJJZrJZJJZZZZJrJJJJrZZJZrzZzzZJZᗑ@=&&Pfv" E@@#  ^ZZX ZZZZZZZZZZZZZJrfffffffrJJJJJZZJZZZJrfnnnnjnbzzzzzzrJZZZZJrrJJJrrrJJZZZZZ@=PU&&Pfv" E@@#  n[YZZZJZZZZZZJrrrJJJJJrrrrrzrZZJJJJrJJJZZZZZZJJZZZZZZZJrrJZZZZZJrrJZZZZZZJJJJJZZJJJJrrzrrrJJrzjfZbjJZnbZrnnfJZzjnzJrbnbrJrzffrJrzzrrrzzrrrrJrzzrrrrJJJnbzjnJjbfzZJrzJZnz@=3&&Pfv" E@@#  \ZbzJbrrzbfrrzzJrrrrjJrnfZbnfJZrbzJJrJZJrzrJJrrrrJJJJJJJZJrJJZZrrJZZZZZJJZZZfjbbbjjnbzrJJrffzr푟jnJf bzZ嗟5 1= 큛J Zfjjf@=WA&&Pfv" E@@#  P]ZffzZ啓 6 rJnjbjzfnbJ鑛Z 7rzjnjbfbnfJᗛ  醙헇JJZffrrnjnnzzzrᕝbjj靕呄rnfzjfZJbnrrbjnbnnf@=B&&Pfv" E@@#  z^[JᗓrJzrᗓrbnjjbbnnfzzrrrrzbbfzffzJ핓zbjzZZZJzffnjjnbzzrJJJJJrrrJJ헝JfnnfzzrZZrzffnjnfrJJrrzffzzrJJZ镑ZzfbbffbfrJJZZZZZZJJznjnj@=;-&&Pfv" E@@#  n<_\jnbnfzzfzzzzzzzJrZZ镕JzfffffbzrrrJJJJJrrzfbbnjjjnnbbfzzfrzzzrzJJrJZ镕ZrzzzffffzrrrrJJJrrrzfffbnnnnnnbffzrrrrrrrJJJJ闗JrrrzzzrJJZZZZZZZJrrrrzfffbbffzrzrJJJZrJZZZZZ@=ã&&Pfv" E@@#  uB`]镕JJJJrJJJZZZZZZJrzzzzffzrJJrJZZZZZZ镕ZJZJJJJZZZZZZZJJJrrzzzzrrrrrJJJZZZZZZ镑JJJrzrrJZZJZZZZZJJJJrrrrzzzzzzzrrzzrrrrrrrJJJZZZ镕JrzzfffzzrJJJJJJJJrr@=&&Pfv" E@@#  =a^rrrrzzzffzzrrrrrrrzrJJrJJJZZZ镗ZJrzzfbfzrJJJZZZJJJJJJrrrrzzzzzrrrrJJJJJJJJJJZZZZ镗ZrzzzffzrrJJJZZJJJJJJrrrrrrrrrJJJZZZZZZZZZZZZ镕JrrrrzzrJZZZZZZZJJrrrzfzzzrrrrJJJJJJZZJZZZZZ镕@=&&Pfv" E@@#  Kb_JzbbfzzfzrZZJJZZZrzbbbbbbbffzrrrrrJJJJrrrrrrZ闓ZJzjnfzrJZZJrbjfJJJrJJJrzzzzrZ푙zbnr靅JfnjfzzZJfbbbjjbrᗙJ ퟟrzZZfnbbz闑nr@=&&Pfv" E@@#   c`bzfzfjfrJZᕑJbnnfZrbbbbnjjfZ푝f釀bfrJZJbnnbfffr镑ZJJjJZZJbjjbzrrZ鑝ZJffJZzjjfrrrᗝ@=s&&Pfv" E@@#  ՀdaşrzbfZrJZfnzJZZ퓙jJrbfZJrZZznnfJᗙjœzbzfzZZrrZ啓bbbnfrnnnjjbzJJZZ핑JrJrbjbJZZZzffffbnjnfzrJ镑ZJZJznj@=Q&&Pfv" E@@#  ƀebpbrZZJzzzzfbnnfzrJZ핑rJJznjnzZZZZrrrzfbnbfzrJZ핑JzfbjjbJZJJZJrrzzfnnbfzzrJ핗ZrbnjjnfrrrrZZJrrzfbnnnbffzrZ闑ZrbjjjnfzzzrZZJzzzzfbnjnnnbfzrZ핗ZJzbjjbfbbfrrzffffb@=_&&Pfv" E@@#  \)fc`njjjjjnbfzJ镗ZJrbnnnbnbfrrzffzzfbnnnnnbfzrZ镑zbjnbrZJrrzbjjjnbzJZ헓fnjzfjfZZZ핑Znn闑Jf헝fnjzᗕ鑝jbZZJ@=x&&Pfv" E@@#  *gdP鑙nj镕zzZJJ푛n镕fzJrrZᗙnJ핕rfZJzrJZ퓅Jj镗zzZrzzrJ퓅nZ镗rfJrffzzrZ@=H&&Pfv" E@@#  ^he@rŕbfZZzbffzrZ靅ZՕffZJfbbbffrᑙf呑jzZznjnbbzJ퓅n坟zJznjnbz啝៟fZJrznnfzJ@=˽&&Pfv" E@@#  рif0Zf陟jnJJrrzbjjnfrJ嗟Jj闗rjzZZJZ镕ZZJJJJZZJJrrrrrrrrrzrrzzrrrrrrrrJJJZZZZJJJrrrrzrzzrrrrrrrJJJZZZZZZZZZ@=3&&Pfv" E@@#  .jg JJJJJJJJJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJJJJZZJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJrJJJrrJrrJrrrJJrJrrJrrrrrrJrrrrrrrrrrrrrrrJrrrrrrrrrrrrrrrJrrrrJJ@=/&&Pfv" E@@#  Y!khJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZJJJJZJJZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@= &&Pfv" E@@#  UliZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZJJJJZZZZZJJJZZJJZZZZZZZZZJZZZZJJJJZZJrzfzzzzzrrrrrrrJJJJJJJJJZZZZ@= &&Pfv" E@@#  miZZZJrrzzfbbbbbbbbbfffzrJ镗 r嗟rnznbJZJznjjnnbfrZ핑 jnrzrJfnfZZZZJrfnbfzrJZ镓bbbzZrfjZ@= &&Pfv" E@@#  ^}njzjjfrrzJZZ啑zbzZ᝛ZrnfJZJzzzzrJ闑zfzbjbJZzbnbfrJrzffbnjjnbbfrZ푙bᓛffzbrZZZJrfnnjjjbfzJ镕zZznjnzJ@=l &&Pfv" E@@#  րokrbfnjjzrfzJJzzzzfbbfzfrZ闝r핝znzJZzbnjnnbrZZJZᗝjnnrZ闕znjjnjfJJrffzffrZ핓njjzZ嗕JbnjnbbJJzbbbnnfzzJ핗@=K &&Pfv" E@@#  &plZbzjjnf啕rzbjbbfZJZZZrzfbbbbfzrJ鑝ZjfjJᑑJrfjfzfJZJZfzzbjnnbbzJZ铙fjff靕JrbnzzzJJfzbjjbnbfrJᕓjnfjr呝JrzjjfJzJJzfzbj@=k &&Pfv" E@@#  qmnbbfrZᕗnnf靟ffbbJrJZrbfnjnnzJ闝jb鑟jfZJZZJJfjjnbzZᕑ헕jjn헕jjjzrzfjjbzJ핓nbJ嗓rffnzrJJZJb@= &&Pfv" E@@#  ʀrnjnjnnbr핑zr푝bjfZZrrrbjnjnfrrZ헝ZᗝjnbbZZrbfbjnffzr핓jJ鑝ZZfnrJJrrnjbbnfrrZ헝rb啓JbbjbJznfbjnfzzJZ@=jR &&Pfv" E@@#  so헙rj静jnzJrrznnbbfzJZZZ핝fJnZfJZJbjnjnbfzJZZZZ푟fbnb퓛jbZZzjjjjnzJJZZZ嗟bbb鑙ZfbJZJfjnbbzJZᗙbb@= &&Pfv" E@@#  tpb鑟JbfZJrfjnbfJZ韅znjr헓znJJznjnnbzrZZᗟrbnJᕑrnzZJzbbbnbzrrZ헟rff呙ZrnbbbrZrzzrrzrJZ镑rfbzZZZZ@=< &&Pfv" E@@#   uqpZZJJJZZJJZZZZZZZZZZZZZZZJJJJJJrrrrrrrrrrrrrJJJZZZrrrrrrJZJJJJJJrrrJrrrrrrrrJJZZZZZZZZZZZrrrrJrJZJrrzzzzzzrrrJZJZZZZJZZZZZ闓ZfnnbbnnzZZrrrfnj@=? &&Pfv" E@@#  8vr`nnbnbfrrzzrrrzzzzzfzzrrrZ镑bjjjrZJrrrnjfzzzrZJzffbbnnbfzzJ闓JjjfJzrfjjnbrJrJZzffbnnjnfzrZ闑jbjz헑JzrfnbfrJZZZZJrzfbjjjjnbfzJZ闓nfnJ@=L9&&Pfv" E@@#  TwsP핗ZrrbjjnfzbrZJZrzrfjnjbnfrJZ헓JfzrjJJrrnnznjffbzfbrznnfnjnnnfzzJZZ闗ZrzbbzbbrJJJrrznjjbbfzzzzzzrrrJZZZZZZZZJJJJJJJZJJrrrzzzzzrrrrJJJJJJJZZ@=&&Pfv" E@@#  ]xt@ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJJJJZJJJJJJJJJJJJJJJJJJJJJJ@=&&Pfv" E@@#  yu0JJJJJJJJJrrJJJJJJrJJJJrJJrJrrJJrrrrrrrJJrrrrrrrrrrrrrrrrrrrrrrrrrrrJJrrrrrrrrrrrrrJrrrrrrrrJJJJrrJJrJJJJJJJJJJJJJJJJJJJJJJJZZZZZZZZZZZZZZZZZZZZZ@= D&&Pfv" E@@#  'zv ZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZJZZZZZZZZZZJZZJZZJZJJZZJJZJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJZJJZJJJJJZJJZZJJJJJJZZJJJJJJJJZJZZZJJJJJZZJZJJJZJZJZZZJZJJJZZZJJZZZZJZZ@=&&Pfv" E@@#  {wZJJZZZZZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=B5&&Pfv" E@@#  |xZZZZZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZJZJZZZZZZZZZZZZZZZZZZZZZZZZJJZZJZZZZZZZJZJJZZZZZZZJJZJJJJJZJJJrJJJZJJJJJJZJJJJJJZZJZZZZJJZJZZZZZJZZJJJrJJrrrrrrrrrrJrrrrJrJJJJJJZJZZZZJJrrzffbbbnnnnnnbbbffzrrZ@=O&&Pfv" E@@#  5}x핗JrzbnnjjjjnnnbffzzzzzrrzrrrrJJJZ闑ZJzbbnjjnnnbzzzzzzfzzfffbbbffzrJZ闓ZJznjjnbbzrrrrJrrrzfbbbbbffzrJJZZ镗ZJzfffnnbbbbbnnjnjjjjjjnbbzrZ闝Zrbj@=]&&Pfv" E@@#  E~yjjnbfzfzzffbnnjjjjnbfzrrrJJJJZZZZZZ핑ZJzbnbbnbfbbffbnbnjjjjjjjjnffzrJZ헝ZJfnjjnjbfbfzrzzzzbnbjjjjjnfzzrZZZ핓ZJfjbbbbzzzzrJzzzfnnnjjnnnbfzrJJZZZᕑJrfnbfzfzrzzrJrzfbnnnnnn@=h&&Pfv" E@@#  `zbffzJZZZ闓ZrzzffrrzzrrrrrzffbnbbbffzzrrJJJZZZ핑ZJrzfzfbbffffzbbfbbbfbbffzzrJJZZZ鑝JJrzfzfbbffffzffffbffbbffzrrrrJZZ핑rrzffzbnffbfzffffbbbbnbbffzzrrJJZZZZZZ@=]&&Pfv" E@@#  ۸{鑝ZrrffffnnbbbfzfbbbnnbbnbbbfzzzrJJJZZZZZᕑZrrzfzfbbfbbzzfffbnbfbbbbfzzzrJJZZZZᕑrrzfzzbbfffzzfzzbbffbbfffzrrJJJZZZZ闝JzzzzzfbbbfzzzzfbbbbbffffzzzrJJZZZZZ핑@=W|&&Pfv" E@@#  |rrrzzrfbbfzzzrzffbbfffffzzrJrJZZZZ闝JrrzzrrfbffzrrzzzfbfffffzzrrrJJZ헓rrzzrrzfffbzrzzzzbbffbfzzrrrrJZZ闓ZrzffrJzffbbzrzrrzffffffzzrrrrJJZZZ핑rzffrJzffbbf@=&&Pfv" E@@#  )}zfzzffbbnnbfzzrzzzrJJZZZZJZ闓ZfbnnfzfbbnjbbbfzfbbnjnnnfzzzzzzzrJJJJJJJZ闓핕zbnjnffbbnjjnnbfbbbbnjjnnffzzzffzrrJJJrJJZᕑJfnjjffbbbjjnnnbfbbbnjjnnbzzzzzzzrrJJJJJJJ헓rfnjbzfffbnnbbbfffffbnn@=b&&Pfv" E@@#  Ҁ~nbfrrrrrrrrJZZZZZ헓zfnnfzfzzbbffbfzffzfbbbbfzrrJrrrrJZZZ鑝JfnjnfffzfnbbnbfffffbbnnbfzrrrzzrrrJZZZZ헓ZzbjnffzzzbnbnnbfffznbzrZZZJZJZZ铛ZfjbffrJJJfbjjbbzzzrJJJJrzzz@=&&Pfv" E@@#  EȀzzrrJJZᕓbjbzZJbnjbfzrzzrrrrrzzzfzrrrJᕓfbrJbnfrrrzzzrrrrzffzzzrJJᕝjzZJfjnzrrzfbnbfzzrzzzffzzrrJZ鑙J嗑rzbfJJfjjfzrJrzzzzzzrrrZ@=DM&&Pfv" E@@#  p푙 J흟zbnrJbjfJZZJrzzzzzzrrZ鑙 ZJfnjjJrnnzZZJrzzzfzzzrJ헝r j훇rfnjjrJbjzZZJrzzzzrrrZ헝 ᅁrbjjjrZbzZJzfzzzzzrJᕑ@=&&Pfv" E@@#  N` ՙrnjfbfZJzffzffzzrZ헟z nnbfnJJzfffffffzJᕑ ńfjrZjzZrffffffbfzZᕓ 5 퀏bjjjJjZZzfffffffzJ鑟@=7&&Pfv" E@@#  P5퀎fjnnnŕZnZzzzzzzffzJ鑙 jnjnbjzᑑbzJzzzzfbbbzZ铅n 5 ZrjnnjՕjJZrzfzzfbbfrZᕝ5zjjnjnJnZZrzfzfbbbfrZ铅@=y&&Pfv" E@@#  %@5zjjnJnZZzffffbbbfrZ퓅 5 ՆzjjnŕZjZZzffffbbbfrZᕟ njjJ푗nzJzfbfbbbbzJ푅j 5ՇzjjbZjZZrffffbnnfrZ铄@=!&&Pfv" E@@#  K0 rjjnjz呝JbZrzzzzbbbzJ靄 Jnnbjz呝JnJzzzffbbzJZᗙ nfnbbjŗjrJrzzfbbnfrZ铅j JbnbjrᓝrbZJrzzfnnnfJᗟ@=&&Pfv" E@@#  0  zbbbՕbzZJJrfbnjbrZᕟ znbbjŗnjrZZZZrzbjjbzZZZ헟zJZfnbbz哝JbJZZJJZJzbjjbrZZZZ铛J fZfbbbz呓rjzZZZZZZrznjjfJ@=Z&&Pfv" E@@#  'WZ闟适rfbfnnjnbfzJJbnrZZZZ헙 rfffbjzrznjbJzjfJZJJZ푛 jJrfff哟jjrZJbjjfrjfJZZJrJZᗟ JrzfnՕffJr@=A&&Pfv" E@@#  1njrZbjzZJrzzrZᗙ 凍JrfbjŗbfZZzjfZJfjjzJJrzzrZ铄Z zZrzfbzᓓZnrzjfJZrfnbrJJrrrJZ푛r JJrfbbrᓑZjbJJbjjzrzfnnjbzJJrrrJZᕟ 퇂@=y&&Pfv" E@@#   rzffnnŕzjzZfnnzJJzbnjnzJZJrrJJᕟj rrffbJ헕rjfZJbnnfJJznjjbrJJrzrJZᕟ ZrrffbrᕕJjfrbfJZrbjnzJJrzrJZ鑙 fjbfrrbJzjJJbbrJznjjnzrzfbf@=k &&Pfv" E@@#  zJZᕝr៙rzzfffnnJZznjfJJfnjbzJJzbjnfzrrrrJZ闝JfJzffzzfnjfZrbjbrZfjjfrrznjbzzrrrrZ镑Jnr嗑ZfnbffzfnjfrJJrfnnfrrzbnnnbfffnjjbfzzzrJZ闑zzrZZrzffffffffzrr@= &&Pfv" E@@#  NrzzffnjnbzzfbnnjjjnbfrJJZZZ镗ZZJrzzzzzzzzzzzbnjjnbbbnjjjnbbbbffzzrrrJZ핗ZJJrrrrrrJrzzbnjbbbbbnjjnbfzrrrrrJJJ镕ZZZrzfrrJZZZZZZZ핕@=U &&Pfv" E@@#  i镑JZZZZJJZ镕ZJJrzfbffffbbbbnnbfzzzrrrJJrrJZJJZJrrr@= &&Pfv" E@@#  4zfnjjjjjjjjjnnnbfffffffrJJJZZZZJJJrrzzzzffbjjjjjjjjnnbfffbfzrrrJJZZZZJJrzzzzzfbjjjnjjnbbffzzrrrJJZZZJJJrrzzzfbnjjjjnnbffzzfzJJZ@=U@ &&Pfv" E@@#  ZJJrzzbnnjjjjnnbbbnbfzzzrrrJJJJZZZZZJJJrzzzfbnjjjjjnnbfffzzzrJJZZZZJzzzfbnjjnbfffffzrJJZZZZrzzfbnnnnn@=ĵ &&Pfv" E@@#  UjnjjjjnnnnbbfzzzzzJJZZJJrzzfbnnjjjjnnjnbbfffzrzrrJJZZZZJrrrzffffffffffffzzzrJJJJJZZZJJrzzzfffbbbbbnnnnbbbbffzrrrJJZ@=(+ &&Pfv" E@@#  ZZJJrzfffbbbbbnnnnnnbbbffzzzzrJJJZZZJJrzzzfffbnnnjjjjjjjjnnnnnbbfffzzzrrrJZZZZZZZZJ@= &&Pfv" E@@#  ̀pZJJrJJJrJrrrrrrzrrzzzrrrrrrrrJJJJJZZZZZZZZZZZZZZZZZZJJJJJJJrrrrrrzzzzzzffzffzzzzzzzzffffffffbbbbbbbbbbbbfffzrrJJJJZZZZZZZZZZJJJJJJJJrJJJZZZZZZZZZZJJJJrrrrrrrrzzzffzfzffffffffzfff@= &&Pfv" E@@#  `zzzzzzzzzzzzzzzrrrrrrJrrrJJrJJJJJrrJJrrJJJrJJJrrrJrrrrrrrrrrrrrrrrrrrzrzzzzrrzrrrzzrrrrrJJrrJJJZZJZZZZZZJJZZZZZZZZZZ@= &&Pfv" E@@#  <؀PZZZZZZZZZZZZZZZZZZJZZZJJJJJJJJJJrrrrrrrrrrrzrrrrrrrJJrrJJJJJJZJJZZZZZ@=&&Pfv" E@@#  @ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=v&&Pfv" E@@#  d0ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  V ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=x &&Pfv" E@@#  ~NZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  ]2ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  dπZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=T|&&Pfv" E@@#  F;ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=h&&Pfv" E@@#  ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZJZZZZZZZZZZZZZZZZZZJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=f&&Pfv" E@@#  .ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  =ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=QQ&&Pfv" E@@#  ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=&&Pfv" E@@#  jZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZJZZZZZZZZZZZZZZZZZJJZJJZZZZZZZZZJZZZZZJJZZJJJZJZJJZZZZZZJJJJZZZJZZJJZZZJJZZJJJJZZZZZZZZZJZZZZZZZZZZZZZJJZZJJZJJZZZZZJZZZZZJZZZZJZZZZZZZZZZZJZZZ@=s;&&Pfv" E@@#  ڀZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZJZZZZJZZZZZZZZJZJZZZJZJZZZZZZZZZZJJZZZJZZZJZZZJZZJJZZZJZJZrJJJZZZ@=&&Pfv" E@@#  @pZZZZZZJZZJJZZZZrZZZZJZZZZrZZZZZZZZZZZZJJZZZZJZJZZZZZZZZZZJZJZZJrZZZZJZZZJZZJZZJrJZzJJZr@=(&&Pfv" E@@#  S`JJZZZZZZZZZZJJZZJJZZJZZJZJJZJZrJrzZrZZJJJZZZrZZZJrZJZZrJJJJZJZZJrJZrrrJZrZJJZJJJZJrzJZJzzJZJrJJzzZZrzrJJJrZZZJrZZrrJZJrrrJJJrrrJr@=&&Pfv" E@@#  WPrJJJZJJrJJJzrJzJrrJrrJJrrfrJZrrrJZZrfrJffrZJzzzJJrrzzrrzzzrrzzrrrrrfzrrrzrrzzrzzJzzzzzzfzzzfbfffrfbffzfzzrrzzrzfzzffffrzffffzzfzfzzbfffbzzzzzzzzzfffzfzzzfzfzzzfzrzffzfrzzzrzffzzzzzzzzzzzzrrzzrzrfzzzrzzzzzrrzrrJrzzzrrrzzrJrrzzrrrrrzzrrrrrrJ@=f&&Pfv" E@@#  *Ҁ@JrrrrJJrJrrrrrJrJJrrrJJJJJJJJJrrrJJJJrrJJJJJrrJJJJJJJJJZZJJJZZJJJJZZJJJJZZZZZJZZJZZZZZZZZZZZZZZZZJZZZZZZZZJZZZZZZZZZZZZZZZZZJZZZZZZZZJJJZJJZZZZJJZZZJJJJJJJJJZZZJJJJZJJZJJJJJJrJJJJJJJZZJJJZZZJJJJJJJJJJJJJJJJJJJJrJJJJJJJJJJJJJJJJJJJJJJJJJ@=&&Pfv" E@@#  L0ZZJJJJJJJJJJJJJZJJZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@= &&Pfv" E@@#  T ZZZZZZZZZZZZZZ@=o&&Pfv" E@@#  HZZZZZZZZZZZZZZZZZZZZZZZZZbjrrZrZZZZJJZZZJZZJZZZZZZZrfrZZZZZZZrrZJrJfzrzzzfznnfZJ@=&&Pfv" E@@#  ȦbZrrrfzrzzZbzffzzZJZZfbjnzbrJZZZJJzrrJZrZZZZZZJJzrZJJZZrZrzZJJZZJZZZZZZZZZZ@=Z&&Pfv" E@@#  ֢rZJJZrJZZJZZrJZrrJZJJJrrJrJZJrZJZrzbjjnnnfffrJZ鑝ZjzfnzZnjzbnffzZZ镑雇fnj@=o&&Pfv" E@@#  nrnnZJnzZbbbjbfzJZ헑JfZjnnfbjfffZJbbrbjnjjnbffzrrZ镓zfZnjjJJfzbnbfzJJrJZZJᕝfZ rJZZbrzzZZZZZZ헝@=D &&Pfv" E@@#  znz嗓jjnfrJZJZ镟JjzJZfnnzrJ闓zbnzzJZZfnnnnfJ闓ZrbjjnfbzrrJrzrznnjjjjjnbfzrJZ@= &&Pfv" E@@#  絀镗ZrzfbbnbbbfzzzzzzzfbbbbbbbbfzzrrJZZZZZZJJrrrrzrzzzzzzzzzrrrrrrJJZZZZZZZZZZZZZZZZZZZZZZZZZZJJZJJZJJJJJJJJJZZJZJJZZJJZZZZZZZJZZZZZJZZZZZZZZZZZJJZZZZZ@=./ &&Pfv" E@@#  1ZZZJJZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZJJZZZZZZJZZZZJJZJJZJJJZZZJJZZZZZZZZJZZZJZZZZJZJJZJZZZJZZZZZZZZZZZZJJJZJZZJZZJJJJrZZJJZZJZZZZZZZZZZZZZZJZJJZJrJrJZZJJJJJJrJZrJJrrrrJJrrJJJZrzJJJJJzzzfzJzrrzJJzzJrrz@= &&Pfv" E@@#  >3rzrzrJrrrJZZZzZJrZJJJZZJJZZZZZJՕZZJZJJzJJZZZrbfzZrJJJ@=+ &&Pfv" E@@#  g;rZZzzbbZnJZzfrrZJJJZZZZZZZZZJJZZJZZZZ闓JznjbzJ푙 1  n74fn fbfrfjzrrr嗟1?J 5@= &&Pfv" E@@#  Zf fJznjbzZᝃ ::5큏126Jb44r푅jZZnbJZJJJrr啙Z2%2n523勵J 5zzjnbbjbzzzzJᕙ?< Jn ZZnj՗jbrZZZ@=K &&Pfv" E@@#  KxpfzrbnbfzfzJ학16r퍎bnᓄbbZᗕbퟗzzzZZJᕝ1 nrrfjbb嗛fjJnJbjbffbnfZJZ靀04Jnfbzjrfb靗ZzZJrZz5zf@=;z &&Pfv" E@@#  v`znZrfzzfJ26bzf Jnjnnfnfzffjjnbfrrffzᝀ65zjzrJZnzrjznbjnnnfrzffJៀZ5铟rJrZnnzrr铕fbbbjbJJbnbbjnbjjnffbbbzJJ@= &&Pfv" E@@#  ;P嗟nnŕrjZZZrbJbjjjjnrJJzbnjjnnbfzzzzzJjnz훙bbffJrfrrfbzJzfnjnnnbbbbbjjnnnbfzzzJZ鑟JzrZrzzffbbbbbfzrJrrJJJrrrrzffffbbbbbbnnnbbbfffzzJZᕓZr@=Sf &&Pfv" E@@#  A0@zzrJJrrrzzzfbnnjnfffzrrJJJZJrrJrzzfbbbbbbbbbffzrJ鑙JrJJJZJZJrrrfbbnnbffzrzrJZZZZZJJrrzzffffzzzzrrJ헝JJJZZJZJJJrfbbnbbffffzrrJJJrrrzzffbbbbfffzrZ핓ZzfzzJZJrrrzrzbnnjjnnnnnbfzzzzfffbbnnnjnnbf@= &&Pfv" E@@#  0frZ啓rbfffzrzzzzfzfnjjjnjnnnbfzzffbbnnnjjjjnbfrZᗝZJzbffzrzJffzznnjnnnfffffbbnnjjnnnfrZ푙bfzbbbZZJJJrfnnjnnnbfffzrrzzfbbnnbbbfrZᗟrbrfrbJZJJzbjnjnjbfzzrJJrrrfzfffzrJZᗙ@=`&&Pfv" E@@#  8ʀ ZnbbfzfJrzfjjjjnzzrrJJJrJrzzzzrZᗟrJnjnbJrZJzfbjnfzrJJJJZJJJrrJZᕝfnzjnzrbjfrrJJJJrJJJJZZᗟnbnrrJfbbbJZrrJJrrJZ핝bnnfJbnbjjrrfz@=&&Pfv" E@@#  !ZrzzZZJ鑝rbfzZbbbbzZJzrJrzJ闑jzJJJJjzrfrJzfzzrJZ闟fjZ흗JZbbrJrrJzffzrZ핗ZJzffjjzJffzfbbJ啝JZnj陓zfjjjnJrfffbnnr@=g8&&Pfv" E@@#  "SrJbJnnjbJZfjnbnjfZᓛzzj᝝bjjjzZrbjnbjf퓙rzbZ呝bjjfZrnjnjf韇Jbf陗JjnjrJzjjjr韄ff陑njzJfjz呅zbnz@=kk&&Pfv" E@@#  坄zjjrrnrZZrbrnrnjzrjfrr陇rnnrᑛbjfbfrrJnff陕fjbnbrrŕzjrr៕njbfzrZbzfn@=&&Pfv" E@@#  7nzfnzjfzfjjbnrfZ鑙zzՕznfjjjf핟Zrśfbjf핑J՗zrzjr镟ZnZ呓J@=`U&&Pfv" E@@#  ZrjjjjjZ啗zb퓑ZZfjjjnjjnJ啝JJjZ铕JzffbbjjnbfZ闙ZrᗓJrzjbnjnbnjjbrZ헟rfr헟ZzfbfnzZfjjjzzfZ镓fJbb@=\&&Pfv" E@@#  (ŕJbznjbjjrrbnbnbnnzZ闑zbfjjrfnnnzffJrfbbnbffJ镑zzfnnbJrfnjnrrrZrrzfzJZZrrrJJZZJJJzzzzzrrJZJrrrJZJfnnrᗝZrfjnr헕ZznnbzZJrrrrJZ@=@&&Pfv" E@@#  QZJrrrrJZJJrrJZZJrJJZZZZZZZZJrrrrJZZZJJZZZJrzzzrJZZJJrJJZZJrzffzrJZrrrrJZZrzffzzrZJrrrrJZZZZZJrrzzrJJJZZZJJZZZZZrzrrzzJZZZZZZJJZZJrzrJrzrZZ@=ȴ&&Pfv" E@@#  ,ZZZJJZJJJZZJJrzffrJJJZZJJJZZZZZZZJrrrrrrJZZZZZZZJJJJJJJZZZZZZZZZZJJJZZZZZZZZZZZZZZZZJZZZZZZZZZZZJJrrzzrrrJZZZZZJJZZZJJJJrrrrrzzrJrrrJZZJJZZZZZZZZZZJJJrzzrrrrrJJJJJZZZZZZZZZZJJJrrJJrrrJJrrrrJJJJZZZZZ@=%*&&Pfv" E@@#  ZZZJJJJrJJJJJJJZZZZZZJJJZZJJJZZZZZZZZZZZZZZZZZJJJJZZZZZZJJZZZJZZZZrZJZZZZJZZZZZZJ@=(&&Pfv" E@@#  fZZZJJZZZZZJZZZZZJJZZZJZJJJZZJrZrZZJrJZZZZZZZZZZZJJZrJJZrrZrJJZJJrZZZZZZJZZZJZJJJJrJJZJJJJJZJZJJZZZZZZZZZZJJZZZJZZJrJJrrJrJJJrJJrZZZZZZZZZZZJZZJJrJ@=&&Pfv" E@@#  UpJrJJzrJZJJJZJJZZZZZZZZJZZJZJJJJJJJJZJJJJZrZZZZJZZJJJZJJJZJZJZJZZJZJZJJZZZJZJZZZZJZJrZJrJJJrZZZrJZJJJJJJZJZZZZZZZZZZJZZJJZZZJZZZJZZZZZJZJJJZJZJJzJZZzrJJJZZZZZrZZJ@=Q&&Pfv" E@@#  =<`ZZZrZJZZJJZZrrJrJJZrrJZJZZJZZJZzJZZZZZJJZZJrJZJJrZJJZZJZZZzJZrZrZrZZJZzJZZZJJJZJJJzJZZJZJJZZJJJZJZrrJZZJZJZJJZZJZJrJrfrZZZZJJJZJrJZZZZZZJJZZJZZJJJZZZZZZZJJZ@=U&&Pfv" E@@#  pЀPZZJrzbnbbnnnjjjnjjnbffzJᕑ ?3f65JjJZJnnrzfffnnfZᑟ2> 폏55zfzzbbnZrzfjjnbnfJJrzrj6=ŌZ ᄇfbjjŕZjzznjbJ@=Xv&&Pfv" E@@#  9@ZnbzzrrJrbnfZ흄67zfbᅟjnjZzjfzfnfJZzfrJrzbnnbzᗟn5JfnnnrrbbnnfnnzrzfzJrbjnrZJrfbnbzJ铙 탍bnbbjzbffbzZJfnnbzrrrJrzbjnzJZJzbbbzJᗝf@=H&&Pfv" E@@#  _0nfznnZJbrrzzJZJzbbzJZZJrzfnnbrZZrffzrZᕓzՅZbnffnnzzfrrrrJJZJzbbfJZJfbnnbzrJJrzffzrZ헟ᇁznbfbjjzbbzzfzrZZznnbrZZrfnnnfrrrzzffzrJᗝfrbnffjjrjbzfbzZJbbzJZ@=b&&Pfv" E@@#  7 rbbbffzzzzzzzrZᗟ՟rzzbfzjjnnbrrfbbfzzrrJJJrrzzrrJJZᕝJf呓rjbJZJzbnnzJZJzffzrJZᕓZzfzZZrzzzzbbbzrJJJrrJJJJJJJJJJJrrrrrrrJJJZZZZ핓ZJ@=&&Pfv" E@@#  d̀ZZJrrJJJrzffffffbfffzrrrJJZZZJJJJJJrrrJJJJZ핑ZzzrJrrzfzrJJrzffffbbnnbbfzzzrrJJJrrrrrzzzzzzrrJ헓rfzrzzfffzJJrffffffnnnnnbfffzrrrJJrrrrzzzzzzrrJ闝JffzrzffffrJJzfffffbnnnnbffzzrrJJrrrrrzzzzzzrrJ@=H&&Pfv" E@@#  q鑝rbfzzffffzrJrfffbffbnnnbbfzzrrJJJJrrrrrrzzrJJ핝JffrrrzzfzJZJrzzfzzfbbbbnnzzrJZZrrrrrrJZ靅fbnjnnZ푑ZJzbJJrzJZZJzffrJJJrrJJJrrrJZ퓛fjjnf՗ZJbbrZZJJrfffzJZJJZJrJZᗟ@=ҽ&&Pfv" E@@#  EbjjbZZrnffzzrJZJfbbfzzzzJZJJJZ啝fnnbJ퓕ZfjjbzrrfnnbbnbbfrJrrrJJZZ헝rfbzZfjjnbbbbfzfbbbfzzzzrJZZ헓ZrbnzZJJzbnnnjnnnbzrzzzzrrzzrJJZ핗ZzbbbfzzzrJJrr@= 3&&Pfv" E@@#  zffbnjjjnbfzzrrrrrJJJJZZJrzzzzzfzrrrrzffbbnnnnbbffzrrrJZZZJrrrrzzrrrrrrrrzfffffzzzrJJJZJrrrrrrrrrrrzzzzffffzzzzrrJJZZZJrzfzzzzzrrrzzffffffzzzzrrJJJZZ@=9&&Pfv" E@@#   qJrzfbbfzzrrJJrzffbbbbbffzzrrrJJZZJzbjjbfrrJZJrzfnnjjjbbbfzrrJJZZ핑JZzbrZZZJznjnnnzZZJJJJJrzrJ鑟rbznf핓zffnzrzrZrzrzzfzJ헟ffᕗnfZZJZJfjnzzbfJ@= &&Pfv" E@@#  WL푟nz镕znJZzfrzbjjnffbzJ铙fznfnbbbjjnjjbfzr闟雙nzZnjfZJffZJjnzbnjjnfrzfzZZ镑JzbnnbnnnbfbnnfzzzzrJZZ镑@= &&Pfv" E@@#  ǰZ啑闗헟鑑闕ZZJZrJZJZZJJzrZZrrZrJZrzZZzrfzrJfrzrzJzJzzJzzzfrrzZzfJZnzzrzzzfrrfbzffzzbrzzJfrZfrZJJZJrrZJrJJZfrrrrzZJzrJZJzJrfZrfZZzrJzJrJrZrrJJJrrrZrrrZfJzJ@=Q &&Pfv" E@@#   ȠJJrJrZZJJZrZZJZJZJZZJJJrJJrZZrJJrJZrJJrzZJrzJrrzzJzzJJrrJzzrzzzJrZzrrJfrrzzJzfzzzzffrznfzbzzbnbnjfnjbbbfzfbnbbbbzffnzzfbrfbfbffzzfzzffbnzffffzzbzzzffzrrffrzzzzrJJJJzJZrJJJZJJJZJZZrJZZJZZZZ@=| &&Pfv" E@@#  (-ɐZZZZZJZZZZZJZJZrJZZZZrZJJZJrrZJZZJrrZJJrJJzfzzrzfzrfzzrJrrrrJrrrrrrrrrzrrrrJJrJJzrrJrZrzZzZJrJJZrZJZJzJrJzJJJJZZJZZZZZJZJZZZ@= &&Pfv" E@@#  "ʀZZJZZZZZJZZZZJZJZZJZZZZZZZZJrZJZZrZJZZrZrrJZZZZJZZJZZZZJJZJZZZZZZZZZZZZZZZZZZZZZZZJJrrrrzrrzzzzzzzzzzrrJJJZZᕑ@=!k &&Pfv" E@@#  Ypzbjr鑑JnjbrZJrrbjnjnbfzzrJrJJrrJJZ韆rzbrᑟfnrZZzfbjjbfzZ푅bnrᑝzbjjz헓JbjnfrZJrfbffzrZᕓznZ푛j@= &&Pfv" E@@#  #`jz闑JbjnzJnbJJrJzbzrJ铛rnz푝JjbZ啗JfnnfJbbfZzffnbrJJ铟JjjrZbjz푓ZbjbrZrbbZZJZzbjnfbr학njbJ@=6S &&Pfv" E@@#  PP퓝Zrf啑fnjbrZrfnjzZZZZJrfnnffzZᕟfbfr闗ZfnbrrffbnbzrZJbbzrZZZZJrrzrJJJZ镑ZrbnnbfzrZZJrJJJJZZZZZJJJrffffzzrJrrJJJJJJJZZZ@=F &&Pfv" E@@#  @@ZZJJJrzzzfzrrrrrrrrJJJZZZJJrrzzzzfffffzzzrrrJZZZZZJJrrzzzfzzrrrrrrJJZZZZZJJJrzzfffffbbfffzzzrrJJJZZZZJrrzzzzzzzzzzrrrrrJJJZZZZZZZZZZZZZJJJ@=J? &&Pfv" E@@#  ̀0JrzzffbbbbbbfffzzzrrJJZZZZZZJJJrrzzzzrrrrrJrrJJJZZZZZZZZZZZZZZZJJrrzzzzffffffzzzzrrrJJZZZZZJJrzzrrJJZZZZZZZZZZZZZZZZZZZZZZZZZJrzbbbffzzzzrrrJJJZZ镗Jfbbnbf@=6 &&Pfv" E@@#  c׀ rJJrrrrrrJZZJrfnnjjfrrrJJJZJJJZZJZZJJZ핗ZzbjbzJZfbbnnbrZJrfbbbffbbbnrzbzrzzrzrZZJJZJJ鑝zfnnz闗ZfjnrrbbbnbzJZjnzrZZrzrzfzJZJZ@=&&&Pfv" E@@#  6핝rbnzJ闗JrbbzrJfnbfzJZrnjJrzbnfzzrZZ헝zjjr啗JfbjjfJJrfjnfrZrfrrfnjfzzJZJJJZZJzfrffrzzrzzrZZrzzzfzJJJJ@=&&Pfv" E@@#  EjnjzrzJZJrrzzJJzJZZ静rbbjbZrffnnzZJzzzzJZrrzzzbjrJzJZJZJzrrrZZZZZZZJJJJrJJJJZZZZZZJZJJZZZZZᕓbbfbfZJffzfzZrrJJZJrJJJJZZ@=&&Pfv" E@@#  ƬJJJJJZZJJrjfZZJrrrJZZJZZJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ@=D&&Pfv" E@@#  "ZZZZZZZZZZZZZZJJJrrrrrrrrrJJJZZZ闓rzZzfbfrJZZzzrrrJZZJJJJZZJJrrJZZZZZJJJJZZZZZZJJJZZZZZZZZZJJJZZZZZZZJJJJJJZZZZZZZZZJJZJZZZZZZZZJJJJJJJJJJJznZJZrbfr@=&&Pfv" E@@#  oZZJrzrZZZZJrrJZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZZJJZZZZZZZZZZZJJZZZJZZZZZZZZZJZZZZZZZZZZZZZZZZZZZZZZZZJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJZZZJJJZZZZJZZ@=\5&&Pfv" E@@#  镕fjzznjnzZJzbbzZZrfnbfzrJJJrzzfffzrrJJJJZZJrrJZZZbjnrZfnjbrZJzjbJZZrzzbnjjbrZZZJrrrrJZ헟rfjrrnbZJrbjjfrJrzfffbjjnfrJJJJJJJJrrJZ@=P&&Pfv" E@@#  xְZZZZJJrJrrrrzzzzfffffzzrrrrrrJJZZ鑙nՓfőJbr嗑JfrJfbnfJZJrrzrJZ핓铓nn핕ZbnrZznjjjnfrZJrfnnbbbzrJZJrrrJ헟z흝nr@=&&Pfv" E@@#  Xנ핗nbZJjzzzZrnnffzJJrzffzZ铛Օr啗nfJznnbrZfnjnnfrZJrzfzJ啑ZZ闙jnz鑑znjjnfrZrbjfJrfbnjnnjnzrzzzfrJZ鑝fZᝅzr@=&&Pfv" E@@#  ؐJfbbfJZJffzzffnjbbnjjjnffbfzzzzfzrJZ闓znbjnnf퓕JrJzfJZZJrJrnnbfzzfffbjjjbbbzzzrrrrJZ镗fbjnzrrJrrzrJJJJrbjjnnbfzzzrzffbfzzzzrrJJJZ镗ZJJZJrJzfzrzrrr@=Z&&Pfv" E@@#  ـrrffzrzrrJrJJrJrJZZZJzrJrJZJJZrJZJrZZJJZrZZZZZZZZZZZZZZJJJZrJZJJJZJZZJJJJJJrZZJJJZJJZZJJZrJrJJZJZJJZZZJJJZZZZJZZJZJJZr@=3x&&Pfv" E@@#  pJJrJJZrZJZJJZZrZJJZJJJJZZZZJZZZJZzJrJzJrJJJrZrZJZrrJZJJrZZZJZZZJZJZrJJJJZJJZJJZrZZJZJJJrJJZJJZZJZJrJrZzZrJrZrZfJrZrJZrrJZzrrJrJrZJJrJJJJJZJZJZrZzZrZrZJrrrJZZrJZJJJJJrJJrJZZJZJJJJZJrJZJJZrZrZJrZJZJZJJZJZZZJJJZZJ@=&&Pfv" E@@#  ނ`JJJZZrZZrZrZZrrZJJZZfrZrJZZJJJrZZrJrZJrJJZJJJZZJrrJJrJrJrJJJrZJzrrzrJrJzrrJJrzZJrJzzrrzrJfJJrzrrfJzrzrzzJrzrrzzrfrrrzrzzrJzzrrzJzrrfzzJfrJrrzJfZbJbZffzzZzrzZzJzzzJfrJrrrzzJrzrrJrzzzzzrZrrzzJfJfJrJZrrJJJrZzJrJrJJfZrJrfZrJJZJrZJJJ@=Ic&&Pfv" E@@#  j]PJJZrZZrZZZZJZZZZJZZZZZZ핕JZZJJZJJZZJZZZ핗rrzzzrzZ@=2&&Pfv" E@@#  <|@JJrzzrzrJrrJrrJJzrrzzzzzrJJJZ핕ZrrzzzzJZZJrrzfbjnbnjnbbffbfbnbzfbbfzzrrJZJ闑JrzffbfzzzzJJrJJrrzfbjjnbffbnbbbfbbbbfzzzJZJZZZrZrfzrzrrrrrzbzbnjjnbnnnfbbzfffrsipp-3.7.2/regress/0000775000000000000000000000000014525516253011050 5ustar sipp-3.7.2/regress/functions0000664000000000000000000000541414525516253013007 0ustar # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 init() { # Jump into the directory where the test is. cd "`dirname "$0"`" # Print a nice message. echo -n "Testing `basename \`pwd\``: " } # Allow SIPP=/path/to/sipp to override autodetection. Please use an # absolute path. This allows you to test a different version. get_sipp() { if test -z "$SIPP"; then # The cd in init() jumps to the directory where the test lives. # We traverse upwards in the directory until we find the sipp # binary. tmp=`pwd` while ! test -x "$tmp/sipp"; do tmp=`dirname "$tmp"` test "$tmp" = "/" && break done test -x "$tmp/sipp" || exit 1 SIPP="$tmp/sipp" fi echo "$SIPP" } sipp() { exec "`get_sipp`" "$@" } sippversion() { "`get_sipp`" -v | sed -e 's/^ //;/SIPp/!d;s/ built.*//' } sippfg() { timeout -sKILL 183 "`get_sipp`" -nostdin "$@" } sippbg() { temp=`mktemp` sipp -nostdin "$@" >$temp 2>&1 /dev/null; then wait $pid ret=$? if test $ret -ne 0; then echo "sipp $@: startup failure, got $ret:" >&2 cat $temp rm $temp exit 1 fi fi rm $temp } tcplisten() { port="$1" exec perl -e ' use Socket;$|=1;(socket(S,PF_INET,SOCK_STREAM,6)&& setsockopt(S,SOL_SOCKET,SO_REUSEADDR,1)&& bind(S,pack_sockaddr_in('$port',"\0\0\0\0"))&& listen(S,1)&&($sa=accept(N,S))&&(($port,$ia)= unpack_sockaddr_in($sa))&&($ia=inet_ntoa($ia))&& print "Connection from $ia:$port\n")||die($!); close(S);while(1){recv(N,$o,2048,0);print "$o";}' } udplisten() { port="$1" exec perl -e ' use Socket;$|=1;(socket(S,PF_INET,SOCK_DGRAM,17)&& bind(S,pack_sockaddr_in('$port',"\0\0\0\0"))&& setsockopt(S,SOL_SOCKET,SO_RCVTIMEO,pack("L!L!",3,0))&& ($sa=recv(S,$o,2048,0))&&(($port,$ia)= unpack_sockaddr_in($sa))&&($ia=inet_ntoa($ia))&& print "Connectionless from $ia:$port\n$o\n")||die($!); close(S);' } cleanup() { # Kill all of our children, if there are any. pkill -P$$ wait } ok() { echo "." cleanup exit 0 } fail() { test -z "$@" && echo "failed" || echo "failed ($@)" cleanup exit 1 } unexpected_ok() { echo "unexpected success" cleanup exit 2 } expected_fail() { echo "x" cleanup test -n "$VERBOSE_EXITSTATUS" && exit 3 exit 0 } skip() { echo "skipped ($@)" cleanup test -n "$VERBOSE_EXITSTATUS" && exit 4 exit 0 } # Workaround when no term is set. See also github-#0075. export TERM=dumb # vim: set syn=sh ts=8 sw=4 sts=4 et ai: sipp-3.7.2/regress/github-#0018/0000775000000000000000000000000014525516253012763 5ustar sipp-3.7.2/regress/github-#0018/notify-brackets.xml0000664000000000000000000000116614525516253016615 0ustar sipp-3.7.2/regress/github-#0018/run0000775000000000000000000000060414525516253013515 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init tcplisten 5070 >tmp.log 2>&1 & pid=$! sleep 1 sippbg -m 1 -sf notify-brackets.xml -t t1 127.0.0.1:5070 /bin/kill -9 $pid 2>/dev/null if grep -qF 'X-Literal: [date]' tmp.log && ! grep -q 'X-Keyword: .*[][].*$' tmp.log; then ok else fail fi sipp-3.7.2/regress/github-#0034/0000775000000000000000000000000014525516253012761 5ustar sipp-3.7.2/regress/github-#0034/notify-1234.xml0000664000000000000000000000125114525516253015401 0ustar sipp-3.7.2/regress/github-#0034/notify-manylf.xml0000664000000000000000000000107214525516253016277 0ustar sipp-3.7.2/regress/github-#0034/notify-nocrlf.xml0000664000000000000000000000106114525516253016272 0ustar sipp-3.7.2/regress/github-#0034/notify-withlf.xml0000664000000000000000000000111614525516253016305 0ustar sipp-3.7.2/regress/github-#0034/run0000775000000000000000000000227414525516253013520 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init tcplisten 5070 >notify-1234.log 2>&1 & notify_1234=$! tcplisten 5071 >notify-manylf.log 2>&1 & notify_manylf=$1 tcplisten 5072 >notify-nocrlf.log 2>&1 & notify_nocrlf=$1 tcplisten 5073 >notify-withlf.log 2>&1 & notify_withlf=$1 sleep 1 sippbg -m 1 -sf notify-1234.xml -t t1 127.0.0.1:5070 sippbg -m 1 -sf notify-manylf.xml -t t1 127.0.0.1:5071 sippbg -m 1 -sf notify-nocrlf.xml -t t1 127.0.0.1:5072 sippbg -m 1 -sf notify-withlf.xml -t t1 127.0.0.1:5073 /bin/kill -9 $notify_1234 $notify_manylf $notify_nocrlf $notify_withlf 2>&1 last_bytes_equal() { size=`stat -c%s $1` tail=`dd if=$1 bs=1 skip=$((size - $2)) 2>/dev/null` test "$tail" = "$3" } fail= last_bytes_equal notify-1234.log 5 "`printf '\n\1\2\3\4'`" || fail="$fail notify-1234" last_bytes_equal notify-manylf.log 5 "`printf '0\r\n\r\n'`" || fail="$fail notify-manylf" last_bytes_equal notify-nocrlf.log 5 "`printf '0\r\n\r\n'`" || fail="$fail notify-nocrlf" last_bytes_equal notify-withlf.log 8 "`printf '3\r\n\r\nX\r\n'`" || fail="$fail notify-withlf" if test -z "$fail"; then ok else fail "on$fail" fi sipp-3.7.2/regress/github-#0083-1/0000775000000000000000000000000014525516253013123 5ustar sipp-3.7.2/regress/github-#0083-1/run0000775000000000000000000000053614525516253013661 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 . "`dirname "$0"`/../functions"; init tcplisten 5070 >tmp.log 2>&1 & pid=$! sleep 1 sippbg -m 1 -sn uac -i 127.0.3.1 -t t1 -p 5071 127.0.2.1:5070 /bin/kill -9 $pid 2>/dev/null if grep -q '^Connection from 127.0.3.1:' tmp.log; then ok else fail fi sipp-3.7.2/regress/github-#0083-2/0000775000000000000000000000000014525516253013124 5ustar sipp-3.7.2/regress/github-#0083-2/run0000775000000000000000000000342214525516253013657 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 . "`dirname "$0"`/../functions"; init tcplisten 5070 >tmp.log 2>&1 & pid=$! sleep 1 sippbg -m 1 -sn uac -i 127.0.3.1 -t t1 -p 5071 127.0.2.1:5070 /bin/kill -9 $pid 2>/dev/null conn_sport=`sed -ne 's/^Connection from.*://p' tmp.log` via_sport=`grep ^Via: tmp.log | sed -e 's/.*:\([0-9]*\);.*/\1/'` if test $conn_sport = $via_sport; then ok else fail fi # This is disabled, because it broke github-#0098. # # From 807a97fc3ef97811c393266ce57ff8c5afd185d9 Mon Sep 17 00:00:00 2001 # From: Walter Doekes # Date: Wed, 6 Aug 2014 09:30:48 +0200 # Subject: [PATCH] Also update local_port value after binding a TCP socket. # # The [local_port] will hold the random port that was bound to. # --- # src/socket.cpp | 3 +-- # 1 file changed, 1 insertion(+), 2 deletions(-) # # diff --git a/src/socket.cpp b/src/socket.cpp # index 9214a2d..74c77b6 100644 # --- a/src/socket.cpp # +++ b/src/socket.cpp # @@ -1520,14 +1520,13 @@ int sipp_do_connect_socket(struct sipp_socket *socket) # # if (socket->ss_transport == T_TCP || socket->ss_transport == T_TLS) { # struct sockaddr_storage local_without_port; # - int port = -1; # memcpy(&local_without_port, &local_sockaddr, sizeof(struct sockaddr_storage)); # if (local_ip_is_ipv6) { # (_RCAST(struct sockaddr_in6 *, &local_without_port))->sin6_port = htons(0); # } else { # (_RCAST(struct sockaddr_in *, &local_without_port))->sin_port = htons(0); # } # - sipp_bind_socket(socket, &local_without_port, &port); # + sipp_bind_socket(socket, &local_without_port, &local_port); # } # #ifdef USE_SCTP # if (socket->ss_transport == T_SCTP) { sipp-3.7.2/regress/github-#0085/0000775000000000000000000000000014525516253012767 5ustar sipp-3.7.2/regress/github-#0085/run0000775000000000000000000000161414525516253013523 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init # Test that out-of-dialog INFO, NOTIFY, UPDATE and OPTIONS get an # automatic 200 reply with automaticResponseMode (-aa): sippbg -sn uas -i 127.0.0.1 -p 5070 -m 1 -aa sippbg -sf uac.xml -i 127.0.0.1 -m 1 127.0.0.1:5070 job2=$! # If job2 did not finish, we have failure. if /bin/kill -0 $job2 2>/dev/null; then fail fi # Remove all running sipp instances manually. cleanup # Test that out-of-dialog INFO, NOTIFY, UPDATE and OPTIONS do *NOT* # get the automatic 200 if we don't use automaticResponseMode (-aa). sippbg -sn uas -i 127.0.0.1 -p 5070 -m 1 sippbg -sf uac.xml -i 127.0.0.1 -m 1 127.0.0.1:5070 job2=$! # If job2 did not finish, it is waiting for job1 to send a 200. This # is expected behaviour. if /bin/kill -0 $job2 2>/dev/null; then ok else fail fi sipp-3.7.2/regress/github-#0085/uac.xml0000664000000000000000000001037414525516253014266 0ustar ;tag=[pid]SIPpTag00[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 INFO Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> ;tag=[pid]SIPpTag02[call_number] To: [service] Call-ID: unrelated-notify///[call_id] CSeq: 100 NOTIFY Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> ;tag=[pid]SIPpTag03[call_number] To: [service] Call-ID: unrelated-options///[call_id] CSeq: 100 OPTIONS Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 3 UPDATE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 4 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0098/0000775000000000000000000000000014525516253012773 5ustar sipp-3.7.2/regress/github-#0098/run0000775000000000000000000000061714525516253013531 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 . "`dirname "$0"`/../functions"; init sippbg 127.0.0.1:5070 -sn 3pcc-C-B -3pcc 127.0.0.1:9000 -i 127.0.0.1 -p 5071 -m 1 sippbg 127.0.0.1:5072 -sn 3pcc-C-A -3pcc 127.0.0.1:9000 -i 127.0.0.1 -p 5073 -m 1 -trace_msg -message_file tmp.log if grep -q Via.*127.0.0.1:5073 tmp.log; then ok else fail fi sipp-3.7.2/regress/github-#0103/0000775000000000000000000000000014525516253012756 5ustar sipp-3.7.2/regress/github-#0103/run0000775000000000000000000000057014525516253013512 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 . "`dirname "$0"`/../functions"; init sippbg -sf uas-modified.xml -i 127.0.0.1 -p 5070 -m 1 sippbg 127.0.0.1:5070 -sn uac -i 127.0.0.1 -m 1 job2=$! # If job2 was finished (ran the entire scenario), we have success. if ! /bin/kill -0 $job2 2>/dev/null; then ok else fail fi sipp-3.7.2/regress/github-#0103/uas-modified.xml0000664000000000000000000000364014525516253016051 0ustar Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0130/0000775000000000000000000000000014525516253012756 5ustar sipp-3.7.2/regress/github-#0130/run0000775000000000000000000000100614525516253013505 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init # Test whether sipp will run without a proper TERM setting. # After pull request #130 it will, as long as stdout is not a tty. TERM=bad_term sippbg -sn uas -i 127.0.0.1 -p 5070 -m 1 TERM=bad_term sippbg -sn uac -i 127.0.0.1 -m 1 127.0.0.1:5070 job2=$! # If job2 was finished (ran the entire scenario), we have success. if ! /bin/kill -0 $job2 2>/dev/null; then ok else fail fi sipp-3.7.2/regress/github-#0150/0000775000000000000000000000000014525516253012760 5ustar sipp-3.7.2/regress/github-#0150/run0000775000000000000000000000103114525516253013505 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init sippfg -m 1 -sn uac '127.0.0.1:5071' -p 5070 -timeout 1 -timeout_error 2>&1 | grep -q 'Unable to send UDP' if test $? -ne 0; then true else fail 'unable to send UDP for IPv4' fi sippfg -m 1 -sn uac '[::1]:5071' -p 5070 -timeout 1 -timeout_error 2>&1 | grep -q 'Unable to send UDP.*family not supported' if test $? -ne 0; then ok else fail 'unable to send UDP for IPv6' fi sipp-3.7.2/regress/github-#0152/0000775000000000000000000000000014525516253012762 5ustar sipp-3.7.2/regress/github-#0152/run0000775000000000000000000000100614525516253013511 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init sippbg -sf uas.xml -p 5070 sippfg -m 20 -sf uac.xml 127.0.0.1:5070 \ -timeout 4 -timeout_error -trace_stat -stf tmp.log \ >/dev/null 2>&1 status=$? # (it's not a log, it's a csv) calls=`cut -d';' -f12 tmp.log | tail -n1` failed=`cut -d';' -f18 tmp.log | tail -n1` if test $calls -eq 20 && test $failed -gt 0; then ok else fail "got $calls calls, and $failed failed" fi sipp-3.7.2/regress/github-#0152/uac.xml0000664000000000000000000000111614525516253014253 0ustar ;tag=[pid]SIPpTag00[call_number] To: [service] Call-ID: [call_id] CSeq: 1 WITHOUTTO Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0152/uas.xml0000664000000000000000000000140314525516253014272 0ustar Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0155/0000775000000000000000000000000014525516253012765 5ustar sipp-3.7.2/regress/github-#0155/run0000775000000000000000000000067214525516253013524 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init sippbg -sn uas -p 5070 # Problem which appears to goes away when -nostdin or when screen is # not initialized. `get_sipp` -m 1 -sn uac -p 5071 127.0.0.1:5070 -timeout 2 -timeout_error \ >/dev/null 2>&1 status=$? if test $status -eq 0; then ok else fail "uac packets seem not to arrive at uas" fi sipp-3.7.2/regress/github-#0156/0000775000000000000000000000000014525516253012766 5ustar sipp-3.7.2/regress/github-#0156/run0000775000000000000000000000230214525516253013515 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2015 . "`dirname "$0"`/../functions"; init # Make the test skippable test -n "$TEST_SKIP_VALGRIND" && exit 0 # Check that we have valgrind which valgrind >/dev/null || fail "no valgrind installed" # Valgrind on UAS. valgrind --xml=yes --xml-file=uas.log --partial-loads-ok=yes \ --show-leak-kinds=all --leak-check=full `get_sipp` -m 10 -sn uas \ >/dev/null 2>&1 & uaspid=$! sleep 1 # Valgrind on UAC. valgrind --xml=yes --xml-file=uac.log --partial-loads-ok=yes \ --show-leak-kinds=all --leak-check=full `get_sipp` -m 10 -sn uac \ 127.0.0.1 >/dev/null 2>&1 & uacpid=$! wait $uaspid test $? -ne 0 && fail "UAS returned non-zero (might be benign(*))" wait $uacpid test $? -ne 0 && fail "UAC returned non-zero (might be benign(*))" grep -q '' uas.log && fail "valgrind reported leaks in UAS" grep -q '' uac.log && fail "valgrind reported leaks in UAC" ok # (*) https://bugs.kde.org/show_bug.cgi?id=345307 # In some cases, leaks are reported that shouldn't be. If you're looking # at dl-init.c, it might be one of those cases. Upgrading gcc (from 5 to # higher) can help. Or upgrading valgrind. sipp-3.7.2/regress/github-#0192/0000775000000000000000000000000014525516253012766 5ustar sipp-3.7.2/regress/github-#0192/beep_1sec_50x160b.alaw0000664000000000000000000001750014525516253016552 0ustar 􇷽4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<J[QTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTQ_S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼4<>92lN[STUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUsipp-3.7.2/regress/github-#0192/run0000775000000000000000000000147314525516253013525 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init uas_media_port=5071 udplisten $uas_media_port >udplisten.log & udplisten_job=$! trap "kill -9 $udplisten_job 2>/dev/null" EXIT sippbg -sf uas.xml -p 5070 -key custom_media_port $uas_media_port sippfg -m 1 -sf uac.xml 127.0.0.1:5070 -trace_msg \ -message_file tmp.log -timeout 5 -timeout_error >/dev/null 2>&1 status=$? test $status -ne 0 && fail "SIPp UAC job failed" uac_media_port=`sed -e '/^m=audio /!d;s/m=audio \([^ ]*\) .*/\1/;q' \ tmp.log` port=`sed -e '/^Connectionless from/!d;s/.*://' udplisten.log` if test "$port" = "$uac_media_port"; then ok elif test -n "$port"; then fail "got RTP from wrong source port $port" else fail "got no RTP at all" fi sipp-3.7.2/regress/github-#0192/uac.xml0000664000000000000000000000403314525516253014260 0ustar sipp-3.7.2/regress/github-#0192/uas.xml0000664000000000000000000000350314525516253014301 0ustar sipp-3.7.2/regress/github-#0196/0000775000000000000000000000000014525516253012772 5ustar sipp-3.7.2/regress/github-#0196/beep_1sec_50x160b.alaw0000664000000000000000000001750014525516253016556 0ustar 􇷽4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<J[QTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTQ_S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼4<>92lN[STUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUsipp-3.7.2/regress/github-#0196/run0000775000000000000000000000166714525516253013536 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 # # Similar to github-#0192, it checks whether the rtpstream source port # is correct, but this time uses the [media_port] instead of a special # [rtpstream_audio_port]. # . "`dirname "$0"`/../functions"; init uac_media_port=5072 # client port uas_media_port=5071 # server port udplisten $uas_media_port >udplisten.log & udplisten_job=$! trap "kill -9 $udplisten_job 2>/dev/null" EXIT sippbg -sf uas.xml -p 5070 -key custom_media_port $uas_media_port sippfg -m 1 -sf uac.xml 127.0.0.1:5070 -min_rtp_port $uac_media_port \ -timeout 5 -timeout_error >/dev/null 2>&1 status=$? test $status -ne 0 && fail "SIPp UAC job failed" port=`sed -e '/^Connectionless from/!d;s/.*://' udplisten.log` if test "$port" = "$uac_media_port"; then ok elif test -n "$port"; then fail "got RTP from wrong source port $port" else fail "got no RTP at all" fi sipp-3.7.2/regress/github-#0196/uac.xml0000664000000000000000000000402114525516253014261 0ustar sipp-3.7.2/regress/github-#0196/uas.xml0000664000000000000000000000350314525516253014305 0ustar sipp-3.7.2/regress/github-#0207/0000775000000000000000000000000014525516253012763 5ustar sipp-3.7.2/regress/github-#0207/run0000775000000000000000000000143414525516253013517 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2019 . "`dirname "$0"`/../functions"; init sippbg -m 1 -sf uas.xml -p 5070 if ! sippfg -m 1 -sf uac.xml 127.0.0.1:5070 \ -trace_msg -message_file tmp.log -trace_err \ -timeout 4 -timeout_error >/dev/null 2>&1; then fail "call failed" fi from="$(sed -e '/message received/,$!d;/^From/!d;s/[[:cntrl:]]*$//' tmp.log)" if test "$from" = "From: Alice ;tag=uniquetag"; then # From: Alice ;tag=uniquetag # ^-- last_From only gets the SIP-body from ok else # From: Alice ;tag=uniquetag, ) # ^-- last_From incorrectly gets the body-from too fail "got From from body? ($from)" fi sipp-3.7.2/regress/github-#0207/uac.xml0000664000000000000000000000157714525516253014267 0ustar ;tag=uniquetag To: Bob Call-ID: [call_id] CSeq: 1 OPTIONS Contact: Content-Length: [len] Content-Type: multipart/mixed; boundary=++ --++ Content-Type: application/sdp foo=bar --++ Content-Type: message/cpim X-Reason: "We don't want this FROM in the [last_From]" From: To: --++-- ]]> sipp-3.7.2/regress/github-#0207/uas.xml0000664000000000000000000000070214525516253014274 0ustar Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0234/0000775000000000000000000000000014525516253012763 5ustar sipp-3.7.2/regress/github-#0234/run0000775000000000000000000000061114525516253013513 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init sippfg -m 1 -sf uas.xml -p 5070 >/dev/null 2>&1 & job=$! sippfg -m 1 -sf uac.xml 127.0.0.1:5070 \ -timeout 4 -timeout_error >/dev/null 2>&1 status=$? wait $job || status=1 if test $status -eq 0; then ok else fail "bad R-URI through [next_url]?" fi sipp-3.7.2/regress/github-#0234/uac.xml0000664000000000000000000000214214525516253014254 0ustar ;tag=[pid]SIPpTag00[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0234/uas.xml0000664000000000000000000000136314525516253014300 0ustar ;+sip.instance="" Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0238/0000775000000000000000000000000014525516253012767 5ustar sipp-3.7.2/regress/github-#0238/run0000775000000000000000000000131614525516253013522 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init sippfg -m 1 -sf uas.xml -p 5070 >/dev/null 2>&1 & job=$! sippfg -m 1 -sf uac.xml 127.0.0.1:5070 \ -trace_msg -message_file tmp.log \ -timeout 4 -timeout_error >/dev/null 2>&1 status=$? wait $job || status=1 if test $status -eq 0; then bye=$(sed -e '/^BYE/,/^[[:blank:]]*$/!d' tmp.log) if echo "$bye" | grep -qF 'From: tom.jones@wales.uk'; then fail "header match unjustly finds headers by value" elif echo "$bye" | grep -qF '"Fromage" '; then ok else fail "unknown failure" fi else fail "process failure" fi sipp-3.7.2/regress/github-#0238/uac.xml0000664000000000000000000000251014525516253014257 0ustar ;tag=[pid]SIPpTag00[call_number] To: "Fromage" Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: "Fromage" [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0238/uas.xml0000664000000000000000000000232114525516253014277 0ustar sipp-3.7.2/regress/github-#0259/0000775000000000000000000000000014525516253012772 5ustar sipp-3.7.2/regress/github-#0259/beep_1sec_50x160b.alaw0000664000000000000000000001750014525516253016556 0ustar 􇷽4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀ7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<6y52>9?1 i3?9?3i 1?9>25y6<9>=4ɀU7=99<7I52>9<1 ` 3>9?00?9>3 錱` 1<9>25I7<99=7Ն4=>9<J[QTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTQ_S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼S7=99=7C52>9<1f 3>9?00?9>3 돱b 1<9>25t6<99=7ӆ4=>9<6|52>9?1 n3?9?0 k1?9>2 ⍱z6<9>=4􀶼4<>92lN[STUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUsipp-3.7.2/regress/github-#0259/run0000775000000000000000000000062314525516253013525 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Pietro Bertera, Snom Technology AG, 2016 # # Start the UAS scenario, UAS stops the rtp_echo # for some seconds # . "`dirname "$0"`/../functions"; init sippbg -sf uas.xml -p 5070 -rtp_echo sippfg -m 1 -sf uac.xml 127.0.0.1:5070 \ -timeout 10 -timeout_error >/dev/null 2>&1 status=$? test $status -ne 0 && fail "SIPp UAC job failed" ok sipp-3.7.2/regress/github-#0259/uac.xml0000664000000000000000000000376714525516253014301 0ustar sipp-3.7.2/regress/github-#0259/uas.xml0000664000000000000000000000402214525516253014302 0ustar sipp-3.7.2/regress/github-#0262/0000775000000000000000000000000014525516253012764 5ustar sipp-3.7.2/regress/github-#0262/run0000775000000000000000000000205014525516253013513 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 # # Test Record-Route and [routes]. # . "`dirname "$0"`/../functions"; init sippbg -sf uas.xml -p 5070 -i 127.0.127.1 -trace_msg -message_file uas.log sippfg -m 1 -sf uac.xml 127.0.127.1:5070 -i 127.0.127.1 \ -trace_msg -message_file uac.log \ -timeout 5 -timeout_error >/dev/null 2>&1 status=$? test $status -ne 0 && fail "SIPp UAC job failed" uac_route=`sed -e '/^ACK /,/^$/!d;/^Route:/!d;s/[[:cntrl:]]//g' uac.log` uac_expected="Route:\ ,\ ,\ " uas_route=`sed -e '/^BYE /,/^$/!d;/^Route:/!d;s/[[:cntrl:]]//g' uas.log` uas_expected="Route:\ ,\ ,\ " if test "$uac_route" != "$uac_expected"; then fail "UAC Route unexpected, got: \"$uac_route\" (not \"$uac_expected\")" elif test "$uas_route" != "$uas_expected"; then fail "UAS Route unexpected, got: \"$uas_route\" (not \"$uas_expected\")" else ok fi sipp-3.7.2/regress/github-#0262/uac.xml0000664000000000000000000000456614525516253014271 0ustar Record-Route: Record-Route: From: sip:[service]@[local_ip]:[local_port];tag=[pid]SIPpTag00[call_number] To: sip:[service]@[remote_ip]:[remote_port] Call-ID: [call_id] CSeq: [cseq] INVITE Contact: Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 8 0 a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0262/uas.xml0000664000000000000000000000344514525516253014304 0ustar Content-Length: 0 ]]> Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 8 0 a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0276/0000775000000000000000000000000014525516253012771 5ustar sipp-3.7.2/regress/github-#0276/run0000775000000000000000000000114714525516253013526 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2018 . "`dirname "$0"`/../functions"; init # Listen on UDP port 6002 to block SIPp from getting that port. udplisten 6002 >tmp.log 2>&1 & listenjob=$! sippbg -sn uas -i 127.0.0.1 -p 5070 -m 1 \ -min_rtp_port 6200 # media port at 6200 sippfg -sn uac -i 127.0.0.1 -m 1 127.0.0.1:5070 >uac.log 2>&1 fgok=$? # Don't care about 6002 getting any traffic or not wait $listenjob # Our fg job succeeded; did not fail on missing 6002 if test $fgok -eq 0; then ok else fail "$(grep errno uac.log | sed -e 's/.*: //')" fi sipp-3.7.2/regress/github-#0337/0000775000000000000000000000000014525516253012767 5ustar sipp-3.7.2/regress/github-#0337/reply488.xml0000664000000000000000000000122414525516253015107 0ustar sipp-3.7.2/regress/github-#0337/run0000775000000000000000000000114014525516253013515 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2016 . "`dirname "$0"`/../functions"; init tcplisten 5070 >reply488.log 2>&1 & reply488=$! sleep 1 sippfg -m 1 -sf reply488.xml -t t1 127.0.0.1:5070 >/dev/null 2>&1 last_bytes_equal() { size=`stat -c%s $1` tail=`dd if=$1 bs=1 skip=$((size - $2)) 2>/dev/null` test "$tail" = "$3" } fail= last_bytes_equal reply488.log 5 "`printf 'ncy\r\n'`" || fail="$fail bad-EOF" grep -q 'Content-Length: *232[[:cntrl:]]$' reply488.log || fail="$fail bad-len" if test -z "$fail"; then ok else fail "on$fail" fi sipp-3.7.2/regress/github-#0364/0000775000000000000000000000000014525516253012767 5ustar sipp-3.7.2/regress/github-#0364/empty-keyword-allowed.xml0000664000000000000000000000105614525516253017760 0ustar sipp-3.7.2/regress/github-#0364/run0000775000000000000000000000067314525516253013527 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2019 . "`dirname "$0"`/../functions"; init tcplisten 5070 >empty-keyword-allowed.log 2>&1 & listen=$! sleep 1 ( sippbg -m 1 -sf empty-keyword-allowed.xml -t t1 127.0.0.1:5070 \ -key keyword '' ); ret=$? /bin/kill -9 $listen 2>&1 test $ret -ne 0 && exit 1 if grep -q "X-Keyword: --" empty-keyword-allowed.log; then ok else fail "on$fail" fi sipp-3.7.2/regress/github-#0458/0000775000000000000000000000000014525516253012773 5ustar sipp-3.7.2/regress/github-#0458/run0000775000000000000000000000074414525516253013532 0ustar #!/bin/sh # This regression test is a part of SIPp. . "`dirname "$0"`/../functions"; init sippfg -m 1 -sf uas.xml -p 5070 >/dev/null 2>&1 & job=$! sippfg -m 1 -sf uac.xml 127.0.0.1:5070 \ -trace_err -error_file err.log \ -timeout 4 -timeout_error >/dev/null 2>&1 status=$? wait $job || status=1 if test $status -eq 0; then ok else if grep -q 'Matching Error' err.log; then fail "matching error - escaping?" else fail "unknown failure" fi fi sipp-3.7.2/regress/github-#0458/uac.xml0000664000000000000000000000362314525516253014271 0ustar ;tag=[pid]SIPpTag00[call_number] To: "Fromage" Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Content-Length: 0 ]]> sipp-3.7.2/regress/github-#0458/uas.xml0000664000000000000000000000073214525516253014307 0ustar sipp-3.7.2/regress/github-#0544/0000775000000000000000000000000014525516253012767 5ustar sipp-3.7.2/regress/github-#0544/run0000775000000000000000000000133714525516253013525 0ustar #!/bin/sh # This regression test is a part of SIPp. . "`dirname "$0"`/../functions"; init sippfg -m 1 -sf ../../sipp_scenarios/pfca_uas_both_crypto_simple.xml \ -t u1 -i 127.0.0.3 -p 5060 -srtpcheck_debug -trace_msg \ >/dev/null 2>&1 & job=$! sippfg -m 1 -sf ../../sipp_scenarios/pfca_uac_bpattern_crypto_simple.xml \ -t u1 -i 127.0.0.2 -p 5060 -srtpcheck_debug 127.0.0.3:5060 \ >/dev/null 2>&1 status=$? wait $job || status=1 # Rename * to *.log here. The srtpcheck stuff spits out lots of files # without extensions. find . -type f '!' -name 'run' '!' -name '*.log' '!' -name '*.xml' -print0 | xargs --no-run-if-empty -0 -IX env file=X sh -c 'mv "$file" "$file.log"' if test $status -eq 0; then ok else fail fi sipp-3.7.2/regress/runtests0000775000000000000000000000235314525516253012670 0ustar #!/bin/sh # This regression test is a part of SIPp. # Author: Walter Doekes, OSSO B.V., 2014 # Don't run from anywhere else than here! cd `dirname "$0"` # Cleanup test remnants. find . -name '*.log' | xargs --no-run-if-empty -d\\n rm -v echo # Export custom location SIPP binary if supplied. export SIPP # Set flag that we want verbose exit codes. export VERBOSE_EXITSTATUS=1 echo "== RUNNING TESTS ==" ok=0 fail=0 unexpected_ok=0 expected_fail=0 skip=0 for path in github-*; do if test -x $path/run; then $path/run ret=$? case $ret in 0) ok=$((ok+1)) ;; 1) fail=$((fail+1)) ;; 2) unexpected_ok=$((unexpected_ok+1)) ;; 3) expected_fail=$((expected_fail+1)) ;; 4) skip=$((skip+1)) ;; *) echo "Unexpected return value: $ret" >&2 && exit 1 esac fi done echo "==" echo -n "$ok success" test $skip -ne 0 && echo -n ", $skip skipped" test $fail -ne 0 && echo -n ", $fail FAILURE" test $unexpected_ok -ne 0 && echo -n ", $unexpected_ok UNEXPECTED SUCCESS" test $expected_fail -ne 0 && echo -n ", $expected_fail expected failure" echo # Return our status. test $fail -ne 0 && exit 1 test $unexpected_ok -ne 0 && exit 2 exit 0 # vim: set ts=8 sw=4 sts=4 et ai: sipp-3.7.2/sipp.10000664000000000000000000004473014525516253010443 0ustar .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1. .TH SIPP "1" "November 2023" "sipp " "User Commands" .SH NAME sipp \- SIP testing tool and traffic generator .SH DESCRIPTION Usage: .IP sipp remote_host[:remote_port] [options] .PP Example: .IP Run SIPp with embedded server (uas) scenario: .IP \&./sipp \fB\-sn\fR uas .IP On the same host, run SIPp with embedded client (uac) scenario: .IP \&./sipp \fB\-sn\fR uac 127.0.0.1 .IP Available options: .PP *** Scenario file options: .TP \fB\-sd\fR : Dumps a default scenario (embedded in the SIPp executable) .TP \fB\-sf\fR : Loads an alternate XML scenario file. To learn more about XML scenario syntax, use the \fB\-sd\fR option to dump embedded scenarios. They contain all the necessary help. .TP \fB\-oocsf\fR : Load out\-of\-call scenario. .TP \fB\-oocsn\fR : Load out\-of\-call scenario. .TP \fB\-sn\fR : Use a default scenario (embedded in the SIPp executable). If this option is omitted, the Standard SipStone UAC scenario is loaded. Available values in this version: .TP \- 'uac' : Standard SipStone UAC (default). .TP \- 'uas' : Simple UAS responder. .TP \- 'regexp' : Standard SipStone UAC \- with regexp and variables. .TP \- 'branchc' : Branching and conditional branching in scenarios \- client. .TP \- 'branchs' : Branching and conditional branching in scenarios \- server. .IP Default 3pcc scenarios (see \fB\-3pcc\fR option): .TP \- '3pcc\-C\-A' : Controller A side (must be started after all other 3pcc scenarios) .TP \- '3pcc\-C\-B' : Controller B side. \- '3pcc\-A' : A side. \- '3pcc\-B' : B side. .PP *** IP, port and protocol options: .TP \fB\-t\fR : Set the transport mode: \- u1: UDP with one socket (default), \- un: UDP with one socket per call, \- ui: UDP with one socket per IP address. The IP addresses must be defined .TP in the injection file. \- t1: TCP with one socket, \- tn: TCP with one socket per call, \- c1: u1 + compression (only if compression plugin loaded), \- cn: un + compression (only if compression plugin loaded). This plugin is .IP not provided with SIPp. .TP \fB\-i\fR : Set the local IP address for 'Contact:','Via:', and 'From:' headers. Default is primary host IP address. .TP \fB\-p\fR : Set the local port number. Default is a random free port chosen by the system. .TP \fB\-bind_local\fR : Bind socket to local IP address, i.e. the local IP address is used as the source IP address. If SIPp runs in server mode it will only listen on the local IP address instead of all IP addresses. .TP \fB\-ci\fR : Set the local control IP address .TP \fB\-cp\fR : Set the local control port number. Default is 8888. .TP \fB\-max_socket\fR : Set the max number of sockets to open simultaneously. This option is significant if you use one socket per call. Once this limit is reached, traffic is distributed over the sockets already opened. Default value is 50000 .TP \fB\-max_reconnect\fR : Set the the maximum number of reconnection. .HP \fB\-reconnect_close\fR : Should calls be closed on reconnect? .HP \fB\-reconnect_sleep\fR : How long (in milliseconds) to sleep between the close and reconnect? .TP \fB\-rsa\fR : Set the remote sending address to host:port for sending the messages. .PP *** SIPp overall behavior options: .TP \fB\-v\fR : Display version and copyright information. .TP \fB\-bg\fR : Launch SIPp in background mode. .TP \fB\-nostdin\fR : Disable stdin. .TP \fB\-plugin\fR : Load a plugin. .TP \fB\-sleep\fR : How long to sleep for at startup. Default unit is seconds. .TP \fB\-skip_rlimit\fR : Do not perform rlimit tuning of file descriptor limits. Default: false. .TP \fB\-buff_size\fR : Set the send and receive buffer size. .HP \fB\-sendbuffer_warn\fR : Produce warnings instead of errors on SendBuffer failures. .TP \fB\-lost\fR : Set the number of packets to lose by default (scenario specifications override this value). .TP \fB\-key\fR : keyword value Set the generic parameter named "keyword" to "value". .TP \fB\-set\fR : variable value Set the global variable parameter named "variable" to "value". .TP \fB\-tdmmap\fR : Generate and handle a table of TDM circuits. A circuit must be available for the call to be placed. Format: \fB\-tdmmap\fR {0\-3}{99}{5\-8}{1\-31} .TP \fB\-dynamicStart\fR : variable value Set the start offset of dynamic_id variable .TP \fB\-dynamicMax\fR : variable value Set the maximum of dynamic_id variable .TP \fB\-dynamicStep\fR : variable value Set the increment of dynamic_id variable .PP *** Call behavior options: .TP \fB\-aa\fR : Enable automatic 200 OK answer for INFO, NOTIFY, OPTIONS and UPDATE. .TP \fB\-base_cseq\fR : Start value of [cseq] for each call. .TP \fB\-cid_str\fR : Call ID string (default %u\-%p@%s). %u=call_number, %s=ip_address, %p=process_number, %%=% (in any order). .TP \fB\-d\fR : Controls the length of calls. More precisely, this controls the duration of \&'pause' instructions in the scenario, if they do not have a 'milliseconds' section. Default value is 0 and default unit is milliseconds. .TP \fB\-deadcall_wait\fR : How long the Call\-ID and final status of calls should be kept to improve message and error logs (default unit is ms). .TP \fB\-auth_uri\fR : Force the value of the URI for authentication. By default, the URI is composed of remote_ip:remote_port. .TP \fB\-au\fR : Set authorization username for authentication challenges. Default is taken from \fB\-s\fR argument .TP \fB\-ap\fR : Set the password for authentication challenges. Default is 'password' .TP \fB\-s\fR : Set the username part of the request URI. Default is 'service'. .TP \fB\-default_behaviors\fR: Set the default behaviors that SIPp will use. Possible values are: \- all Use all default behaviors \- none Use no default behaviors \- bye Send byes for aborted calls \- abortunexp Abort calls on unexpected messages \- pingreply Reply to ping requests \- cseq Check CSeq of ACKs If a behavior is prefaced with a \-, then it is turned off. Example: all,\-bye .TP \fB\-nd\fR : 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 .TP 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 unexpected ACK CSeq do nothing \- On any other unexpected message, abort the call by sending a BYE or a .IP CANCEL .TP \fB\-pause_msg_ign\fR : Ignore the messages received during a pause defined in the scenario .HP \fB\-callid_slash_ign\fR: Don't treat a triple\-slash in Call\-IDs as indicating an extra SIPp prefix. .PP *** Injection file options: .TP \fB\-inf\fR : 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: \fB\-inf\fR f1.csv \fB\-inf\fR f2.csv ...) .TP \fB\-infindex\fR : file field Create an index of file using field. For example \fB\-inf\fR ../path/to/users.csv \fB\-infindex\fR users.csv 0 creates an index on the first key. .TP \fB\-ip_field\fR : Set which field from the injection file contains the IP address from which the client will send its messages. If this option is omitted and the '\-t ui' option is present, then field 0 is assumed. Use this option together with '\-t ui' .PP *** RTP behaviour options: .TP \fB\-mi\fR : Set the local media IP address (default: local primary host IP address) .TP \fB\-rtp_echo\fR : Enable RTP echo. RTP/UDP packets received on media port are echoed to their sender. RTP/UDP packets coming on this port + 2 are also echoed to their sender (used for sound and video echo). .TP \fB\-mb\fR : Set the RTP echo buffer size (default: 2048). .TP \fB\-min_rtp_port\fR : Minimum port number for RTP socket range. .TP \fB\-max_rtp_port\fR : Maximum port number for RTP socket range. .TP \fB\-rtp_payload\fR : RTP default payload type. .HP \fB\-rtp_threadtasks\fR : RTP number of playback tasks per thread. .TP \fB\-rtp_buffsize\fR : Set the rtp socket send/receive buffer size. .TP \fB\-rtpcheck_debug\fR : Write RTP check debug information to file .TP \fB\-audiotolerance\fR : Audio error tolerance for RTP checks (0.0\-1.0) \fB\-\-\fR default: 1.0 .TP \fB\-videotolerance\fR : Video error tolerance for RTP checks (0.0\-1.0) \fB\-\-\fR default: 1.0 .PP *** Call rate options: .TP \fB\-r\fR : Set the call rate (in calls per seconds). This value can bechanged during test by pressing '+', '_', '*' or '/'. Default is 10. pressing '+' key to increase call rate by 1 * rate_scale, pressing '\-' key to decrease call rate by 1 * rate_scale, pressing '*' key to increase call rate by 10 * rate_scale, pressing '/' key to decrease call rate by 10 * rate_scale. .TP \fB\-rp\fR : 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 \fB\-r\fR n \fB\-rp\fR m). Example: \fB\-r\fR 7 \fB\-rp\fR 2000 ==> 7 calls every 2 seconds. .IP \fB\-r\fR 10 \fB\-rp\fR 5s => 10 calls every 5 seconds. .TP \fB\-rate_scale\fR : Control the units for the '+', '\-', '*', and '/' keys. .TP \fB\-rate_increase\fR : Specify the rate increase every \fB\-rate_interval\fR units (default is seconds). This allows you to increase the load for each independent logging period. Example: \fB\-rate_increase\fR 10 \fB\-rate_interval\fR 10s .IP ==> increase calls by 10 every 10 seconds. .TP \fB\-rate_max\fR : If \fB\-rate_increase\fR is set, then quit after the rate reaches this value. Example: \fB\-rate_increase\fR 10 \fB\-rate_max\fR 100 .IP ==> increase calls by 10 until 100 cps is hit. .TP \fB\-rate_interval\fR : Set the interval by which the call rate is increased. Defaults to the value of \fB\-fd\fR. .TP \fB\-no_rate_quit\fR : If \fB\-rate_increase\fR is set, do not quit after the rate reaches \fB\-rate_max\fR. .TP \fB\-l\fR : Set the maximum number of simultaneous calls. Once this limit is reached, traffic is decreased until the number of open calls goes down. Default: .IP (3 * call_duration (s) * rate). .TP \fB\-m\fR : Stop the test and exit when 'calls' calls are processed .TP \fB\-users\fR : Instead of starting calls at a fixed rate, begin 'users' calls at startup, and keep the number of calls constant. .PP *** Retransmission and timeout options: .TP \fB\-recv_timeout\fR : Global receive timeout. Default unit is milliseconds. If the expected message is not received, the call times out and is aborted. .TP \fB\-send_timeout\fR : Global send timeout. Default unit is milliseconds. If a message is not sent (due to congestion), the call times out and is aborted. .TP \fB\-timeout\fR : Global timeout. Default unit is seconds. If this option is set, SIPp quits after nb units (\fB\-timeout\fR 20s quits after 20 seconds). .TP \fB\-timeout_error\fR : SIPp fails if the global timeout is reached is set (\fB\-timeout\fR option required). .TP \fB\-max_retrans\fR : Maximum number of UDP retransmissions before call ends on timeout. Default is 5 for INVITE transactions and 7 for others. .TP \fB\-max_invite_retrans\fR: Maximum number of UDP retransmissions for invite transactions before call ends on timeout. .TP \fB\-max_non_invite_retrans\fR: Maximum number of UDP retransmissions for non\-invite transactions before call ends on timeout. .TP \fB\-nr\fR : Disable retransmission in UDP mode. .TP \fB\-rtcheck\fR : Select the retransmission detection method: full (default) or loose. .TP \fB\-T2\fR : Global T2\-timer in milli seconds .PP *** Third\-party call control options: .TP \fB\-3pcc\fR : Launch the tool in 3pcc mode ("Third Party call control"). The passed IP address depends on the 3PCC role. \- When the first twin command is 'sendCmd' then this is the address of the .TP remote twin socket. SIPp will try to connect to this address:port to send .TP the twin command (This instance must be started after all other 3PCC scenarios). .TP Example: 3PCC\-C\-A scenario. \- When the first twin command is 'recvCmd' then this is the address of the .TP local twin socket. SIPp will open this address:port to listen for twin command. .IP Example: 3PCC\-C\-B scenario. .TP \fB\-master\fR : 3pcc extended mode: indicates the master number .TP \fB\-slave\fR : 3pcc extended mode: indicates the slave number .TP \fB\-slave_cfg\fR : 3pcc extended mode: indicates the file where the master and slave addresses are stored .PP *** Performance and watchdog options: .TP \fB\-timer_resol\fR : Set the timer resolution. Default unit is milliseconds. This option has an impact on timers precision.Small values allow more precise scheduling but impacts CPU usage.If the compression is on, the value is set to 50ms. The default value is 10ms. .TP \fB\-max_recv_loops\fR : Set the maximum number of messages received read per cycle. Increase this value for high traffic level. The default value is 1000. .TP \fB\-max_sched_loops\fR : Set the maximum number of calls run per event loop. Increase this value for high traffic level. The default value is 1000. .TP \fB\-watchdog_interval\fR: Set gap between watchdog timer firings. Default is 400. .TP \fB\-watchdog_reset\fR : If the watchdog timer has not fired in more than this time period, then reset the max triggers counters. Default is 10 minutes. .TP \fB\-watchdog_minor_threshold\fR: If it has been longer than this period between watchdog executions count a minor trip. Default is 500. .TP \fB\-watchdog_major_threshold\fR: If it has been longer than this period between watchdog executions count a major trip. Default is 3000. .TP \fB\-watchdog_major_maxtriggers\fR: How many times the major watchdog timer can be tripped before the test is terminated. Default is 10. .TP \fB\-watchdog_minor_maxtriggers\fR: How many times the minor watchdog timer can be tripped before the test is terminated. Default is 120. .PP *** Tracing, logging and statistics options: .TP \fB\-f\fR : Set the statistics report frequency on screen. Default is 1 and default unit is seconds. .TP \fB\-trace_stat\fR : Dumps all statistics in _.csv file. Use the '\-h stat' option for a detailed description of the statistics file content. .TP \fB\-stat_delimiter\fR : Set the delimiter for the statistics file .TP \fB\-stf\fR : Set the file name to use to dump statistics .TP \fB\-fd\fR : Set the statistics dump log report frequency. Default is 60 and default unit is seconds. .TP \fB\-periodic_rtd\fR : Reset response time partition counters each logging interval. .TP \fB\-trace_msg\fR : Displays sent and received SIP messages in __messages.log .TP \fB\-message_file\fR : Set the name of the message log file. .HP \fB\-message_overwrite\fR: Overwrite the message log file (default true). .TP \fB\-trace_shortmsg\fR : Displays sent and received SIP messages as CSV in __shortmessages.log .HP \fB\-shortmessage_file\fR: Set the name of the short message log file. .HP \fB\-shortmessage_overwrite\fR: Overwrite the short message log file (default true). .TP \fB\-trace_counts\fR : Dumps individual message counts in a CSV file. .TP \fB\-trace_err\fR : Trace all unexpected messages in __errors.log. .TP \fB\-error_file\fR : Set the name of the error log file. .HP \fB\-error_overwrite\fR : Overwrite the error log file (default true). .TP \fB\-trace_error_codes\fR: Dumps the SIP response codes of unexpected messages to __error_codes.log. .TP \fB\-trace_calldebug\fR : Dumps debugging information about aborted calls to __calldebug.log file. .TP \fB\-calldebug_file\fR : Set the name of the call debug file. .HP \fB\-calldebug_overwrite\fR: Overwrite the call debug file (default true). .TP \fB\-trace_screen\fR : Dump statistic screens in the __screens.log file when quitting SIPp. Useful to get a final status report in background mode (\fB\-bg\fR option). .TP \fB\-screen_file\fR : Set the name of the screen file. .HP \fB\-screen_overwrite\fR: Overwrite the screen file (default true). .TP \fB\-trace_rtt\fR : Allow tracing of all response times in __rtt.csv. .TP \fB\-rtt_freq\fR : freq is mandatory. Dump response times every freq calls in the log file defined by \fB\-trace_rtt\fR. Default value is 200. .TP \fB\-trace_logs\fR : Allow tracing of actions in __logs.log. .TP \fB\-log_file\fR : Set the name of the log actions log file. .TP \fB\-log_overwrite\fR : Overwrite the log actions log file (default true). .TP \fB\-ringbuffer_files\fR: How many error, message, shortmessage and calldebug files should be kept after rotation? .TP \fB\-ringbuffer_size\fR : How large should error, message, shortmessage and calldebug files be before they get rotated? .TP \fB\-max_log_size\fR : What is the limit for error, message, shortmessage and calldebug file sizes. .PP Signal handling: .IP SIPp can be controlled using POSIX signals. The following signals are handled: USR1: Similar to pressing the 'q' key. It triggers a soft exit .IP of SIPp. No more new calls are placed and all ongoing calls are finished before SIPp exits. Example: kill \fB\-SIGUSR1\fR 732 .IP USR2: Triggers a dump of all statistics screens in .IP __screens.log file. Especially useful in background mode to know what the current status is. Example: kill \fB\-SIGUSR2\fR 732 .PP Exit codes: .IP Upon exit (on fatal error or when the number of asked calls (\fB\-m\fR option) is reached, SIPp exits with one of the following exit code: .IP 0: All calls were successful 1: At least one call failed .IP 97: Exit on internal command. Calls may have been processed 99: Normal exit without calls processed .IP 253: RTP validation failure .HP \fB\-1\fR: Fatal error .HP \fB\-2\fR: Fatal error binding a socket .IP SIPp v3.7.2. .IP 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. .IP 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. .IP 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 .IP Author: see source files. sipp-3.7.2/sipp.dtd0000664000000000000000000001014314525516253011045 0ustar sipp-3.7.2/sipp_scenarios/0000775000000000000000000000000014525516253012417 5ustar sipp-3.7.2/sipp_scenarios/mcd_register.xml0000664000000000000000000000417614525516253015620 0ustar sipp-3.7.2/sipp_scenarios/pfca_uac.xml0000664000000000000000000001013614525516253014703 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 8 9 103 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern.xml0000664000000000000000000001177014525516253016606 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple.xml0000664000000000000000000001051114525516253021547 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_aescm128sha132.xml0000664000000000000000000001051114525516253024074 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000001057314525516253024575 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] [ueaescm128sha1321audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] [ueaescm128sha1802audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_aescm128sha180.xml0000664000000000000000000001051114525516253024077 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000001057314525516253024600 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] [ueaescm128sha1801audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] [ueaescm128sha1322audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_g711a.xml0000664000000000000000000001051114525516253022447 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 8 0 18 9 103 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_g711u.xml0000664000000000000000000001051114525516253022473 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_g722.xml0000664000000000000000000001051014525516253022307 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 9 0 18 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:9 G722/8000 a=fmtp:9 bitrate=64000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_g729.xml0000664000000000000000000001051214525516253022320 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 18 0 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:18 G729/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_nullsha132.xml0000664000000000000000000001050114525516253023522 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_nullsha180.xml0000664000000000000000000001050114525516253023525 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation.xml0000664000000000000000000001503514525516253024504 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000001503514525516253027031 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000001503514525516253027034 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000001501514525516253026456 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000001501514525516253026461 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_crypto_simple_renegotiation_reuse.xml0000664000000000000000000001504114525516253025704 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio-9] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio-9] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_g711a.xml0000664000000000000000000001177014525516253017506 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 8 0 18 9 103 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_g711u.xml0000664000000000000000000001177014525516253017532 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_g722.xml0000664000000000000000000001176714525516253017355 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 9 0 18 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:9 G722/8000 a=fmtp:9 bitrate=64000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_apattern_g729.xml0000664000000000000000000001177614525516253017364 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 18 0 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:18 G729/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_avpattern.xml0000664000000000000000000001413314525516253016770 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [media_port+2] RTP/AVP 99 98 96 97 b=TIAS:1536000 b=AS:1597 a=rtcp:[media_port+3] a=maxprate:192.0 a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple.xml0000664000000000000000000001241214525516253021552 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_aescm128sha132.xml0000664000000000000000000001241214525516253024077 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000001255614525516253024601 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] [ueaescm128sha1321audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] [ueaescm128sha1802audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] [ueaescm128sha1321video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] [ueaescm128sha1802video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_aescm128sha180.xml0000664000000000000000000001241214525516253024102 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000001255614525516253024604 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] [ueaescm128sha1801audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] [ueaescm128sha1322audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] [ueaescm128sha1801video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] [ueaescm128sha1322video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_nullsha132.xml0000664000000000000000000001237214525516253023533 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_nullsha180.xml0000664000000000000000000001237214525516253023536 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation.xml0000664000000000000000000002063714525516253024511 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000002063714525516253027036 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000002063714525516253027041 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000002057714525516253026470 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1802audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000002057714525516253026473 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuitenullsha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_bpattern_crypto_simple_renegotiation_reuse.xml0000664000000000000000000002064714525516253025715 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio-9] a=crypto:[cryptotag2audio] [cryptosuiteaescm128sha1322audio] inline:[cryptokeyparams2audio-9] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video-9] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video-9] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern.xml0000664000000000000000000001251114525516253016625 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [media_port] RTP/AVP 99 98 96 97 b=TIAS:1536000 b=AS:1597 a=rtcp:[media_port+1] a=maxprate:192.0 a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple.xml0000664000000000000000000001122014525516253021572 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_aescm128sha132.xml0000664000000000000000000001122014525516253024117 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000001130214525516253024611 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] [ueaescm128sha1321video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] [ueaescm128sha1802video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_aescm128sha180.xml0000664000000000000000000001122014525516253024122 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000001130214525516253024614 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] [ueaescm128sha1801video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] [ueaescm128sha1322video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_nullsha132.xml0000664000000000000000000001121014525516253023545 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_nullsha180.xml0000664000000000000000000001121014525516253023550 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation.xml0000664000000000000000000001625314525516253024534 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000001625314525516253027061 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000001625314525516253027064 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000001623314525516253026506 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1802video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000001623314525516253026511 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuitenullsha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uac_vpattern_crypto_simple_renegotiation_reuse.xml0000664000000000000000000001625714525516253025743 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 10 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 11 PRACK RAck: [$2][$1] Contact: Max-Forwards: 70 Subject: Conference Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 10 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 INVITE Contact: Content-Type: application/sdp Max-Forwards: 70 User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Subject: Conference Session-Expires: 3600;refresher=uas Min-SE: 90 Supported: 100rel Require: 100rel Content-Length: [len] v=0 o=16001 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video-9] a=crypto:[cryptotag2video] [cryptosuiteaescm128sha1322video] inline:[cryptokeyparams2video-9] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=sendrecv a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 12 ACK Content-Length: 0 ]]> ;tag=[call_number] To: [peer_tag_param] Call-ID: [call_id] CSeq: 13 BYE Contact: Max-Forwards: 70 Subject: Conference User-Agent: VIRTUAL Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas.xml0000664000000000000000000000531414525516253014725 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio.xml0000664000000000000000000000531414525516253016106 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple.xml0000664000000000000000000000613614525516253021062 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_aescm128sha132.xml0000664000000000000000000000577714525516253023421 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000000603014525516253024071 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] [ueaescm128sha1321audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_aescm128sha180.xml0000664000000000000000000000577714525516253023424 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000000603014525516253024074 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] [ueaescm128sha1801audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_g711a.xml0000664000000000000000000000577714525516253021774 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 8 0 18 9 103 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_g711u.xml0000664000000000000000000000577714525516253022020 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_g722.xml0000664000000000000000000000577614525516253021634 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 9 0 18 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:9 G722/8000 a=fmtp:9 bitrate=64000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_g729.xml0000664000000000000000000000600114525516253021621 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 18 0 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:18 G729/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_nullsha132.xml0000664000000000000000000000577314525516253023044 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_nullsha180.xml0000664000000000000000000000577314525516253023047 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation.xml0000664000000000000000000001076514525516253024014 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000001076514525516253026341 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000001076514525516253026344 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000001075514525516253025767 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000001075514525516253025772 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_crypto_simple_renegotiation_reuse.xml0000664000000000000000000001076714525516253025221 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio-5] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_g711a.xml0000664000000000000000000000531414525516253017006 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 8 0 18 9 103 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_g711u.xml0000664000000000000000000000531414525516253017032 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_g722.xml0000664000000000000000000000531314525516253016646 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 9 0 18 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:9 G722/8000 a=fmtp:9 bitrate=64000 a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audio_g729.xml0000664000000000000000000000531414525516253016656 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 18 0 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:18 G729/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_audiovideo.xml0000664000000000000000000000655514525516253017145 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 18 9 103 8 101 a=rtcp:[media_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [media_port+2] RTP/AVP 99 98 96 97 b=TIAS:1536000 b=AS:1597 a=rtcp:[media_port+3] a=maxprate:192.0 a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple.xml0000664000000000000000000001003014525516253020701 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_aescm128sha132.xml0000664000000000000000000000760114525516253023240 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000000766314525516253023741 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] [ueaescm128sha1321audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] [ueaescm128sha1321video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_aescm128sha180.xml0000664000000000000000000000760114525516253023243 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000000766314525516253023744 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] [ueaescm128sha1801audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] [ueaescm128sha1801video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_nullsha132.xml0000664000000000000000000000757114525516253022675 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_nullsha180.xml0000664000000000000000000000757114525516253022700 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation.xml0000664000000000000000000001430414525516253023640 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000001430414525516253026165 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000001430414525516253026170 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000001426414525516253025621 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1321audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000001426414525516253025624 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuitenullsha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_both_crypto_simple_renegotiation_reuse.xml0000664000000000000000000001431014525516253025040 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [rtpstream_audio_port] RTP/AVP 0 18 9 103 8 101 a=crypto:[cryptotag1audio] [cryptosuiteaescm128sha1801audio] inline:[cryptokeyparams1audio-5] a=rtcp:[rtpstream_audio_port+1] a=sendrecv a=rtpmap:0 PCMU/8000 a=rtpmap:18 G729/8000 a=rtpmap:9 G722/16000 a=fmtp:9 bitrate=64000 a=rtpmap:103 G7221/16000 a=fmtp:103 bitrate=32000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-11,16 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video-5] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video.xml0000664000000000000000000000600014525516253016104 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [media_port] RTP/AVP 99 98 96 97 b=TIAS:1536000 b=AS:1597 a=rtcp:[media_port+1] a=maxprate:192.0 a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple.xml0000664000000000000000000000662614525516253021073 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_aescm128sha132.xml0000664000000000000000000000646514525516253023421 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_aescm128sha132_ue.xml0000664000000000000000000000651614525516253024107 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] [ueaescm128sha1321video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_aescm128sha180.xml0000664000000000000000000000646514525516253023424 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_aescm128sha180_ue.xml0000664000000000000000000000651614525516253024112 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] [ueaescm128sha1801video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_nullsha132.xml0000664000000000000000000000646114525516253023044 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_nullsha180.xml0000664000000000000000000000646114525516253023047 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation.xml0000664000000000000000000001214114525516253024007 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation_aescm128sha132.xml0000664000000000000000000001214114525516253026334 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation_aescm128sha180.xml0000664000000000000000000001214114525516253026337 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation_nullsha132.xml0000664000000000000000000001213114525516253025762 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1321video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation_nullsha180.xml0000664000000000000000000001213114525516253025765 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuitenullsha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/pfca_uas_video_crypto_simple_renegotiation_reuse.xml0000664000000000000000000001214314525516253025214 0ustar Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP-12.0.1.99 Content-Length: 0 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 0 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> Server: VIRTUAL Mitel-3300-ICP 12.0.1.99 Content-Type: application/sdp Content-Length: [len] v=0 o=16002 0 1 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=video [rtpstream_video_port] RTP/AVP 99 98 96 97 a=crypto:[cryptotag1video] [cryptosuiteaescm128sha1801video] inline:[cryptokeyparams1video-5] b=TIAS:1536000 b=AS:1597 a=maxprate:192.0 a=rtcp:[rtpstream_video_port+1] a=rtpmap:99 H264/90000 a=fmtp:99 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 a=rtpmap:98 H264/90000 a=fmtp:98 profile-level-id=64000d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:96 H264/90000 a=fmtp:96 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=0 a=rtpmap:97 H264/90000 a=fmtp:97 profile-level-id=42800d; max-mbps=108000; max-fs=3600; max-br=1280; packetization-mode=1 ]]> sipp-3.7.2/sipp_scenarios/uc360_register.xml0000664000000000000000000000374514525516253015716 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 1 REGISTER Contact: Allow: INVITE, ACK, BYE, OPTIONS, CANCEL, UPDATE, SUBSCRIBE, NOTIFY, INFO, MESSAGE Max-Forwards: 70 User-Agent: Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Expires: 3600 Supported: path Content-Length: 0 ]]> sipp-3.7.2/sipp_scenarios/uc360_register_challenge.xml0000664000000000000000000000373014525516253017712 0ustar ;tag=[call_number] To: Call-ID: [call_id] CSeq: 1 REGISTER Contact: Allow: INVITE, ACK, BYE, OPTIONS, CANCEL, UPDATE, SUBSCRIBE, NOTIFY, INFO, MESSAGE Max-Forwards: 70 User-Agent: Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Expires: 3600 Supported: path Content-Length: 0 ]]> ;tag=[call_number] To: Call-ID: [call_id] CSeq: 2 REGISTER Contact: [authentication username=16001 password=asdfzxcvqwer] Allow: INVITE, ACK, BYE, OPTIONS, CANCEL, UPDATE, SUBSCRIBE, NOTIFY, INFO, MESSAGE Max-Forwards: 70 User-Agent: Mitel-UC-Endpoint (Mitel UC360 Collaboration Point/2.1.0.99; 08:00:0F:74:80:E1) Expires: 3600 Supported: path Content-Length: 0 ]]> sipp-3.7.2/src/0000775000000000000000000000000014525516253010165 5ustar sipp-3.7.2/src/actions.cpp0000664000000000000000000011136314525516253012336 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * Guillaume Teissier from FTR&D */ #include "sipp.hpp" #include "fileutil.h" #include #ifdef PCAPPLAY #include "prepare_pcap.h" #endif static const char* strIntCmd(CAction::T_IntCmdType type) { switch (type) { case CAction::E_INTCMD_STOPCALL: return "stop_call"; case CAction::E_INTCMD_STOP_ALL: return "stop_gracefully"; case CAction::E_INTCMD_STOP_NOW: return "stop_now"; default: case CAction::E_INTCMD_INVALID: return "invalid"; } return "invalid"; } const char * CAction::comparatorToString(T_Comparator comp) { switch(comp) { case E_C_EQ: return "=="; case E_C_NE: return "!="; case E_C_GT: return ">"; case E_C_LT: return "<"; case E_C_GEQ: return ">="; case E_C_LEQ: return "<="; default: return "invalid"; } } bool CAction::compare(VariableTable *variableTable) { double lhs = variableTable->getVar(M_varInId)->getDouble(); double rhs = M_varIn2Id ? variableTable->getVar(M_varIn2Id)->getDouble() : M_doubleValue; switch(M_comp) { case E_C_EQ: return lhs == rhs; case E_C_NE: return lhs != rhs; case E_C_GT: return lhs > rhs; case E_C_LT: return lhs < rhs; case E_C_GEQ: return lhs >= rhs; case E_C_LEQ: return lhs <= rhs; default: ERROR("Internal error: Invalid comparison type %d", M_comp); return false; /* Shut up warning. */ } } void CAction::printInfo(char* buf, int len) { if (M_action == E_AT_ASSIGN_FROM_REGEXP) { if (M_lookingPlace == E_LP_MSG) { snprintf(buf, len, "Type[%d] - regexp[%s] where[%s] - checkIt[%d] - checkItInverse[%d] - $%s", M_action, M_regularExpression, "Full Msg", M_checkIt, M_checkItInverse, display_scenario->allocVars->getName(M_varId)); } else { snprintf(buf, len, "Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - checkItInverse[%d] - $%s", M_action, M_regularExpression, "Header", M_lookingChar, M_checkIt, M_checkItInverse, display_scenario->allocVars->getName(M_varId)); } } else if (M_action == E_AT_EXECUTE_CMD) { snprintf(buf, len, "Type[%d] - command[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_EXEC_INTCMD) { snprintf(buf, len, "Type[%d] - intcmd[%-32.32s]", M_action, strIntCmd(M_IntCmd)); } else if (M_action == E_AT_LOG_TO_FILE) { snprintf(buf, len, "Type[%d] - message[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_LOG_WARNING) { snprintf(buf, len, "Type[%d] - warning[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_LOG_ERROR) { snprintf(buf, len, "Type[%d] - error[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_ASSIGN_FROM_SAMPLE) { char tmp[40]; M_distribution->textDescr(tmp, sizeof(tmp)); snprintf(buf, len, "Type[%d] - sample varId[%s] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp); } else if (M_action == E_AT_ASSIGN_FROM_VALUE) { snprintf(buf, len, "Type[%d] - assign varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_ASSIGN_FROM_INDEX) { snprintf(buf, len, "Type[%d] - assign index[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_ASSIGN_FROM_GETTIMEOFDAY) { snprintf(buf, len, "Type[%d] - assign gettimeofday[%s, %s]", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_subVarId[0])); } else if (M_action == E_AT_ASSIGN_FROM_STRING) { snprintf(buf, len, "Type[%d] - string assign varId[%s] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message_str[0]); } else if (M_action == E_AT_JUMP) { snprintf(buf, len, "Type[%d] - jump varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); } else if (M_action == E_AT_PAUSE_RESTORE) { snprintf(buf, len, "Type[%d] - restore pause varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); } else if (M_action == E_AT_VAR_ADD) { snprintf(buf, len, "Type[%d] - add varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_MULTIPLY) { snprintf(buf, len, "Type[%d] - multiply varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_DIVIDE) { snprintf(buf, len, "Type[%d] - divide varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_TRIM) { snprintf(buf, len, "Type[%d] - trim varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_VAR_URLDECODE) { snprintf(buf, len, "Type[%d] - urldecode varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_VAR_URLENCODE) { snprintf(buf, len, "Type[%d] - urlencode varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_VAR_TEST) { snprintf(buf, len, "Type[%d] - divide varId[%s] varInId[%s] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue); } else if (M_action == E_AT_VAR_TO_DOUBLE) { snprintf(buf, len, "Type[%d] - toDouble varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); #ifdef PCAPPLAY } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_IMAGE) || (M_action == E_AT_PLAY_PCAP_VIDEO)) { snprintf(buf, len, "Type[%d] - file[%s]", M_action, M_pcapArgs->file); } else if (M_action == E_AT_PLAY_DTMF) { snprintf(buf, len, "Type[%d] - play DTMF digits [%s]", M_action, M_message_str[0]); #endif } else if (M_action == E_AT_RTP_STREAM_PLAY) { snprintf(buf, len, "Type[%d] - rtp_stream playfile file %s pattern_id %d loop=%d payload %d bytes per packet=%d ms per packet=%d ticks per packet=%d", M_action, M_rtpstream_actinfo.filename, M_rtpstream_actinfo.pattern_id, M_rtpstream_actinfo.loop_count, M_rtpstream_actinfo.payload_type, M_rtpstream_actinfo.bytes_per_packet, M_rtpstream_actinfo.ms_per_packet, M_rtpstream_actinfo.ticks_per_packet); } else if (M_action == E_AT_RTP_STREAM_PAUSE) { snprintf(buf, len, "Type[%d] - rtp_stream pause", M_action); } else if (M_action == E_AT_RTP_STREAM_RESUME) { snprintf(buf, len, "Type[%d] - rtp_stream resume", M_action); } else if (M_action == E_AT_RTP_STREAM_PLAYAPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream playapattern file %s pattern_id %d loop=%d payload %d bytes per packet=%d ms per packet=%d ticks per packet=%d", M_action, M_rtpstream_actinfo.filename, M_rtpstream_actinfo.pattern_id, M_rtpstream_actinfo.loop_count, M_rtpstream_actinfo.payload_type, M_rtpstream_actinfo.bytes_per_packet, M_rtpstream_actinfo.ms_per_packet, M_rtpstream_actinfo.ticks_per_packet); } else if (M_action == E_AT_RTP_STREAM_PAUSEAPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream pauseapattern", M_action); } else if (M_action == E_AT_RTP_STREAM_RESUMEAPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream resumeapattern", M_action); } else if (M_action == E_AT_RTP_STREAM_PLAYVPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream playvpattern file %s pattern_id %d loop=%d payload %d bytes per packet=%d ms per packet=%d ticks per packet=%d", M_action, M_rtpstream_actinfo.filename, M_rtpstream_actinfo.pattern_id, M_rtpstream_actinfo.loop_count, M_rtpstream_actinfo.payload_type, M_rtpstream_actinfo.bytes_per_packet, M_rtpstream_actinfo.ms_per_packet, M_rtpstream_actinfo.ticks_per_packet); } else if (M_action == E_AT_RTP_STREAM_PAUSEVPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream pausevpattern", M_action); } else if (M_action == E_AT_RTP_STREAM_RESUMEVPATTERN) { snprintf(buf, len, "Type[%d] - rtp_stream resumevpattern", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_STARTAUDIO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho startaudio", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_UPDATEAUDIO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho updateaudio", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_STOPAUDIO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho stopaudio", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_STARTVIDEO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho startvideo", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_UPDATEVIDEO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho updatevideo", M_action); } else if (M_action == E_AT_RTP_STREAM_RTPECHO_STOPVIDEO) { snprintf(buf, len, "Type[%d] - rtp_stream rtpecho stopvideo", M_action); } else { snprintf(buf, len, "Type[%d] - unknown action type ... ", M_action); } } CAction::T_ActionType CAction::getActionType() { return(M_action); } CAction::T_LookingPlace CAction::getLookingPlace() { return(M_lookingPlace); } CAction::T_IntCmdType CAction::getIntCmd () { return(M_IntCmd); } CAction::T_Comparator CAction::getComparator () { return(M_comp); } bool CAction::getCheckIt() { return(M_checkIt); } bool CAction::getCheckItInverse() { return(M_checkItInverse); } bool CAction::getCaseIndep() { return(M_caseIndep); } bool CAction::getHeadersOnly() { return(M_headersOnly); } int CAction::getOccurrence() { return(M_occurrence); } int CAction::getVarId() { return(M_varId); } int CAction::getVarInId() { return(M_varInId); } int CAction::getVarIn2Id() { return(M_varIn2Id); } char* CAction::getLookingChar() { return(M_lookingChar); } SendingMessage *CAction::getMessage(int n) { return(M_message[n]); } CSample* CAction::getDistribution() { return(M_distribution); } double CAction::getDoubleValue() { return(M_doubleValue); } char* CAction::getStringValue() { return(M_stringValue); } #ifdef PCAPPLAY pcap_pkts *CAction::getPcapPkts() { return(M_pcapArgs); } #endif rtpecho_actinfo_t* CAction::getRTPEchoActInfo() { return &M_rtpecho_actinfo; } rtpstream_actinfo_t* CAction::getRTPStreamActInfo() { return &M_rtpstream_actinfo; } void CAction::setActionType (CAction::T_ActionType P_value) { M_action = P_value; } void CAction::setLookingPlace (CAction::T_LookingPlace P_value) { M_lookingPlace = P_value; } void CAction::setCheckIt (bool P_value) { M_checkIt = P_value; } void CAction::setCheckItInverse (bool P_value) { M_checkItInverse = P_value; } void CAction::setVarId (int P_value) { M_varId = P_value; } void CAction::setVarInId (int P_value) { M_varInId = P_value; } void CAction::setVarIn2Id (int P_value) { M_varIn2Id = P_value; } void CAction::setCaseIndep (bool P_value) { M_caseIndep = P_value; } void CAction::setOccurrence (int P_value) { M_occurrence = P_value; } void CAction::setHeadersOnly (bool P_value) { M_headersOnly = P_value; } void CAction::setIntCmd (T_IntCmdType P_type) { M_IntCmd = P_type; } void CAction::setComparator (T_Comparator P_value) { M_comp = P_value; } /* sample specific function. */ void CAction::setDistribution (CSample *P_value) { M_distribution = P_value; } /* assign from value specific function. */ void CAction::setDoubleValue (double P_value) { M_doubleValue = P_value; } /* strcmp specific function. */ void CAction::setStringValue (char *P_value) { M_stringValue = P_value; } void CAction::setSubVarId (int P_value) { if ( M_nbSubVarId < M_maxNbSubVarId ) { M_subVarId[M_nbSubVarId] = P_value; M_nbSubVarId++; } } int CAction::getSubVarId(int P_index) { return(M_subVarId[P_index]); } int* CAction::getSubVarId() { return(M_subVarId); } void CAction::setNbSubVarId (int P_value) { M_maxNbSubVarId = P_value; if (M_subVarId != NULL) { delete [] M_subVarId; M_subVarId = NULL; } M_subVarId = new int[M_maxNbSubVarId] ; M_nbSubVarId = 0 ; } int CAction::getNbSubVarId () { return(M_nbSubVarId); } void CAction::setLookingChar(const char* P_value) { if (M_lookingChar != NULL) { delete [] M_lookingChar; M_lookingChar = NULL; } if (P_value != NULL) { M_lookingChar = new char[strlen(P_value)+1]; strcpy(M_lookingChar, P_value); } } void CAction::setMessage(const char* P_value, int n) { if (M_message[n] != NULL) { delete M_message[n]; M_message[n] = NULL; } free(M_message_str[n]); M_message_str[n] = NULL; if (P_value != NULL) { M_message_str[n] = strdup(P_value); M_message[n] = new SendingMessage(M_scenario, P_value, true /* skip sanity */); } } void CAction::setRegExp(const char *P_value) { int errorCode; free(M_regularExpression); M_regularExpression = strdup(P_value); M_regExpSet = true; errorCode = regcomp(&M_internalRegExp, P_value, REGCOMP_PARAMS); if (errorCode != 0) { char buffer[MAX_HEADER_LEN]; regerror(errorCode, &M_internalRegExp, buffer, sizeof(buffer)); ERROR("recomp error : regular expression '%s' - error '%s'", M_regularExpression, buffer); } } char *CAction::getRegularExpression() { if (!M_regExpSet) { ERROR("Trying to get a regular expression for an action that does not have one!"); } return M_regularExpression; } int CAction::executeRegExp(const char* P_string, VariableTable *P_callVarTable) { regmatch_t pmatch[10]; int error; int nbOfMatch = 0; char* result = NULL ; if (!M_regExpSet) { ERROR("Trying to perform regular expression match on action that does not have one!"); } if (getNbSubVarId() > 9) { ERROR("You can only have nine sub expressions!"); } memset((void*)pmatch, 0, sizeof(regmatch_t)*10); error = regexec(&M_internalRegExp, P_string, 10, pmatch, REGEXEC_PARAMS); if (error == 0) { CCallVariable* L_callVar = P_callVarTable->getVar(getVarId()); for (int i = 0; i <= getNbSubVarId(); i++) { if (pmatch[i].rm_eo != -1) { setSubString(&result, P_string, pmatch[i].rm_so, pmatch[i].rm_eo); L_callVar->setMatchingValue(result); nbOfMatch++; } if (i == getNbSubVarId()) { break; } L_callVar = P_callVarTable->getVar(getSubVarId(i)); } } return(nbOfMatch); } void CAction::setSubString(char** P_target, const char* P_source, int P_start, int P_stop) { int sizeOf; if (P_source != NULL) { sizeOf = P_stop - P_start; (*P_target) = new char[sizeOf + 1]; if (sizeOf > 0) { memcpy((*P_target), &(P_source[P_start]), sizeOf); } (*P_target)[sizeOf] = '\0'; } else { *P_target = NULL ; } } #ifdef PCAPPLAY void CAction::setPcapArgs (pcap_pkts * P_value) { if (M_pcapArgs != NULL) { free(M_pcapArgs); M_pcapArgs = NULL; } if (P_value != NULL) { M_pcapArgs = (pcap_pkts *)malloc(sizeof(*M_pcapArgs)); memcpy(M_pcapArgs, P_value, sizeof(*M_pcapArgs)); } } void CAction::setPcapArgs(const char* P_value) { if (M_pcapArgs != NULL) { free(M_pcapArgs); M_pcapArgs = NULL; } if (P_value != NULL) { M_pcapArgs = (pcap_pkts *) malloc(sizeof(*M_pcapArgs)); if (parse_play_args(P_value, M_pcapArgs) == -1) { ERROR("Play pcap error"); } if (access(M_pcapArgs->file, F_OK)) { ERROR("Cannot read file %s", M_pcapArgs->file); } } } #endif void CAction::setRTPEchoActInfo(const char* P_value) { char* param_str; char* next_comma; char actionstring[RTPECHO_MAX_FILENAMELEN]; if (strlen(P_value) >= 512) { ERROR("RTPEcho keyword %s is too long -- maximum supported length %u\n", P_value, 512); } memset(actionstring, 0, sizeof(actionstring)); // Initialize M_rtpecho_actinfo struct members M_rtpecho_actinfo.payload_type = rtp_default_payload; memset(M_rtpecho_actinfo.payload_name, 0, sizeof(M_rtpecho_actinfo.payload_name)); M_rtpecho_actinfo.bytes_per_packet = 0; M_rtpecho_actinfo.audio_active = 0; M_rtpecho_actinfo.video_active = 0; strcpy (actionstring,P_value); param_str = strchr(actionstring,','); next_comma = NULL; // Comma found for payload_type parameter if (param_str) { *(param_str++) = 0; // skip comma /* we have a payload type parameter */ next_comma = strchr (param_str, ','); if (next_comma) { *(next_comma++) = 0; } M_rtpecho_actinfo.payload_type = atoi(param_str); param_str = next_comma; } // Comma found for payload_name parameter if (param_str) { /* we have a payload_name parameter */ next_comma = strchr(param_str, ','); if (next_comma) { *(next_comma++) = 0; } strcpy(M_rtpecho_actinfo.payload_name, param_str); param_str = next_comma; } if (!strcmp(M_rtpecho_actinfo.payload_name, "")) { static const std::map default_parameters{ {0, "PCMU/8000"}, {8, "PCMA/8000"}, {9, "G722/8000"}, {18, "G729/8000"}}; auto const it = default_parameters.find(M_rtpecho_actinfo.payload_type); if (it != default_parameters.end()) { strncat(M_rtpecho_actinfo.payload_name, it->second, sizeof(M_rtpecho_actinfo.payload_name) - 1); } else { ERROR("Missing mandatory payload_name parameter in rtp_echo action"); } } /* Setup based on what we know of payload types */ switch (M_rtpecho_actinfo.payload_type) { case 0: if (!strcmp(M_rtpecho_actinfo.payload_name, "PCMU/8000")) { M_rtpecho_actinfo.bytes_per_packet = 160; M_rtpecho_actinfo.audio_active = 1; } break; case 8: if (!strcmp(M_rtpecho_actinfo.payload_name, "PCMA/8000")) { M_rtpecho_actinfo.bytes_per_packet = 160; M_rtpecho_actinfo.audio_active = 1; } break; case 9: if (!strcmp(M_rtpecho_actinfo.payload_name, "G722/8000")) { M_rtpecho_actinfo.bytes_per_packet = 160; M_rtpecho_actinfo.audio_active = 1; } break; case 18: if (!strcmp(M_rtpecho_actinfo.payload_name, "G729/8000")) { M_rtpecho_actinfo.bytes_per_packet = 20; M_rtpecho_actinfo.audio_active = 1; } break; default: if (M_rtpecho_actinfo.payload_type >= 0 && M_rtpecho_actinfo.payload_type <= 95) { M_rtpecho_actinfo.bytes_per_packet = -1; M_rtpecho_actinfo.audio_active = 0; M_rtpecho_actinfo.video_active = 0; ERROR("Unknown static rtp payload type %d - cannot set playback parameters\n", M_rtpecho_actinfo.payload_type); } else if (M_rtpecho_actinfo.payload_type >= 96 && M_rtpecho_actinfo.payload_type <= 127) { if (!strcmp(M_rtpecho_actinfo.payload_name, "H264/90000")) { M_rtpecho_actinfo.bytes_per_packet = 1280; // ARBITRARY H264 PACKET SIZE M_rtpecho_actinfo.video_active = 1; } else if (!strcmp(M_rtpecho_actinfo.payload_name, "iLBC/8000")) { M_rtpecho_actinfo.bytes_per_packet = 50; M_rtpecho_actinfo.audio_active = 1; } else { M_rtpecho_actinfo.bytes_per_packet = -1; M_rtpecho_actinfo.audio_active = 0; M_rtpecho_actinfo.video_active = 0; ERROR("Unknown dynamic rtp payload type %d - cannot set playback parameters\n",M_rtpecho_actinfo.payload_type); } } else { ERROR("Invalid rtp payload type %d - cannot set playback parameters\n",M_rtpecho_actinfo.payload_type); } } } void CAction::setRTPEchoActInfo(rtpecho_actinfo_t *P_value) { /* At this stage the entire rtpecho action info structure can simply be */ /* copied. No members need to be individually duplicated/processed. */ memcpy(&M_rtpecho_actinfo, P_value, sizeof(M_rtpecho_actinfo)); } void CAction::setRTPStreamActInfo(const char *P_value) { char* param_str; char* next_comma; int pattern_mode = 0; int stream_type = 0; /* 0: AUDIO / 1: VIDEO */ char argument_buf[sizeof(M_rtpstream_actinfo.filename)]; const char* found_file; if (strlen(P_value) >= sizeof(argument_buf)) { ERROR("Filename/Pattern keyword %s is too long -- maximum supported length %zu", P_value, sizeof(argument_buf) - 1); } // Initialize M_rtpstream_actinfo struct members M_rtpstream_actinfo.filename[0] = '\0'; M_rtpstream_actinfo.pattern_id = -1; M_rtpstream_actinfo.loop_count = -1; M_rtpstream_actinfo.bytes_per_packet = 0; M_rtpstream_actinfo.ms_per_packet = 0; M_rtpstream_actinfo.ticks_per_packet = 0; M_rtpstream_actinfo.payload_type = 0; M_rtpstream_actinfo.payload_name[0] = '\0'; M_rtpstream_actinfo.audio_active = 0; M_rtpstream_actinfo.video_active = 0; if (!strncmp(P_value, "apattern", 8) || !strncmp(P_value, "vpattern", 8)) { pattern_mode = 1; } // Extract filename strcpy(argument_buf, P_value); if ((param_str = strchr(argument_buf, ','))) { *param_str++ = '\0'; } // Lookup best file match if (pattern_mode == 0) { found_file = find_file(argument_buf); if (found_file) { if (strlen(found_file) >= sizeof(M_rtpstream_actinfo.filename)) { ERROR("Filename/Pattern keyword %s is too long -- maximum supported length %zu", found_file, sizeof(M_rtpstream_actinfo.filename) - 1); } strcpy(M_rtpstream_actinfo.filename, found_file); free(const_cast(found_file)); } } // Set default values of pattern_id/loop_count depending on whether we are in PATTERN or FILE mode if (pattern_mode) { M_rtpstream_actinfo.pattern_id = 1; M_rtpstream_actinfo.loop_count = -1; } else { M_rtpstream_actinfo.pattern_id = -1; M_rtpstream_actinfo.loop_count = 1; } // Comma found for loop_count (FILE MODE) or pattern_id (PATTERN MODE) if (param_str) { /* we have a loop count parameter (FILE MODE) or pattern id parameter (PATTERN MODE) */ next_comma = strchr(param_str, ','); if (next_comma) { *(next_comma++) = 0; } if (pattern_mode) { M_rtpstream_actinfo.pattern_id = atoi(param_str); } else { M_rtpstream_actinfo.loop_count = atoi(param_str); } param_str = next_comma; } // Set default RTP payload type value M_rtpstream_actinfo.payload_type = rtp_default_payload; // Comma found for payload_type parameter if (param_str) { /* we have a payload type parameter */ next_comma = strchr(param_str, ','); if (next_comma) { *(next_comma++) = 0; } M_rtpstream_actinfo.payload_type = atoi(param_str); param_str = next_comma; } // Comma found for payload_name parameter if (param_str) { /* we have a payload_name parameter */ next_comma = strchr(param_str, ','); if (next_comma) { *(next_comma++) = 0; } strcpy(M_rtpstream_actinfo.payload_name, param_str); param_str = next_comma; } if (!strcmp(M_rtpstream_actinfo.payload_name, "")) { static const std::map default_parameters { {0, "PCMU/8000"}, {8, "PCMA/8000"}, {9, "G722/8000"}, {18, "G729/8000"}}; auto const it = default_parameters.find(M_rtpstream_actinfo.payload_type); if (it != default_parameters.end()) { strncat(M_rtpstream_actinfo.payload_name, it->second, sizeof(M_rtpstream_actinfo.payload_name) - 1); } else { ERROR("Missing mandatory payload_name parameter in rtp_stream action"); } } // JLTAG NOTE: // Reworked code from commit // "f3b6fe8cd60dd3d1dcca0ab373dc9d289f123f43" which violated the RFC // standards for payload_type IDs by using dynamic payload type 98 // incorrectly as a static one for the iLBC audio codec which rather // uses dynamic payload types and should rather be determined by // payload_name (0-95 is the payload_type range reserved for static // codecs, 96-127 is the payload_type range is reserved for dynamic // codecs) /* Setup based on what we know of payload types */ switch (M_rtpstream_actinfo.payload_type) { case 0: if (!strcmp(M_rtpstream_actinfo.payload_name, "PCMU/8000")) { M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; M_rtpstream_actinfo.audio_active = 1; stream_type = 0; } break; case 8: if (!strcmp(M_rtpstream_actinfo.payload_name, "PCMA/8000")) { M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; M_rtpstream_actinfo.audio_active = 1; stream_type = 0; } break; case 9: if (!strcmp(M_rtpstream_actinfo.payload_name, "G722/8000")) { M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; M_rtpstream_actinfo.audio_active = 1; stream_type = 0; } break; case 18: if (!strcmp(M_rtpstream_actinfo.payload_name, "G729/8000")) { M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 20; M_rtpstream_actinfo.ticks_per_packet = 160; M_rtpstream_actinfo.audio_active = 1; stream_type = 0; } break; default: if ((M_rtpstream_actinfo.payload_type >= 0) && (M_rtpstream_actinfo.payload_type <= 95)) { M_rtpstream_actinfo.ms_per_packet = -1; M_rtpstream_actinfo.bytes_per_packet = -1; M_rtpstream_actinfo.ticks_per_packet = -1; M_rtpstream_actinfo.audio_active = 0; M_rtpstream_actinfo.video_active = 0; ERROR("Unknown static rtp payload type %d - cannot set playback parameters\n", M_rtpstream_actinfo.payload_type); } else if (M_rtpstream_actinfo.payload_type >= 96 && M_rtpstream_actinfo.payload_type <= 127) { if (!strcmp(M_rtpstream_actinfo.payload_name, "H264/90000")) { M_rtpstream_actinfo.ms_per_packet = 160; // ARBITRARY H264 PACKET TIME M_rtpstream_actinfo.bytes_per_packet = 1280; // ARBITRARY H264 PACKET SIZE M_rtpstream_actinfo.ticks_per_packet = 1280; // ARBITRARY H264 PACKET TICKS M_rtpstream_actinfo.video_active = 1; stream_type = 1; } else if (!strcmp(M_rtpstream_actinfo.payload_name, "iLBC/8000")) { M_rtpstream_actinfo.ms_per_packet = 30; M_rtpstream_actinfo.bytes_per_packet = 50; M_rtpstream_actinfo.ticks_per_packet = 240; M_rtpstream_actinfo.audio_active = 1; stream_type = 0; } else { M_rtpstream_actinfo.ms_per_packet = -1; M_rtpstream_actinfo.bytes_per_packet = -1; M_rtpstream_actinfo.ticks_per_packet = -1; M_rtpstream_actinfo.audio_active = 0; M_rtpstream_actinfo.video_active = 0; ERROR("Unknown dynamic rtp payload type %d - cannot set playback parameters\n", M_rtpstream_actinfo.payload_type); } } else { ERROR("Invalid rtp payload type %d - cannot set playback parameters\n", M_rtpstream_actinfo.payload_type); } break; } if (rtpstream_cache_file( M_rtpstream_actinfo.filename, pattern_mode /* 0: FILE - 1: PATTERN */, M_rtpstream_actinfo.pattern_id, M_rtpstream_actinfo.bytes_per_packet, stream_type) < 0) { ERROR("Cannot read/cache rtpstream file %s", M_rtpstream_actinfo.filename); } } void CAction::setRTPStreamActInfo(rtpstream_actinfo_t *P_value) { /* At this stage the entire rtpstream action info structure can simply be */ /* copied. No members need to be individually duplicated/processed. */ memcpy(&M_rtpstream_actinfo, P_value, sizeof(M_rtpstream_actinfo)); } void CAction::setScenario(scenario * P_scenario) { M_scenario = P_scenario; } void CAction::setAction(CAction P_action) { if (P_action.getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { assert(P_action.getDistribution() != NULL); } int L_i; setActionType ( P_action.getActionType() ); setLookingPlace ( P_action.getLookingPlace() ); setVarId ( P_action.getVarId() ); setVarInId ( P_action.getVarInId() ); setDoubleValue ( P_action.getDoubleValue() ); setDistribution ( P_action.getDistribution() ); setScenario ( P_action.M_scenario ); setNbSubVarId ( P_action.getNbSubVarId() ); for (L_i = 0; L_i < P_action.getNbSubVarId() ; L_i++ ) { setSubVarId (P_action.getSubVarId(L_i)); } setLookingChar ( P_action.getLookingChar() ); setCheckIt ( P_action.getCheckIt() ); setCheckItInverse ( P_action.getCheckItInverse() ); setCaseIndep ( P_action.getCaseIndep() ); setOccurrence ( P_action.getOccurrence() ); setHeadersOnly ( P_action.getHeadersOnly() ); for (L_i = 0; L_i < MAX_ACTION_MESSAGE; L_i++) { setMessage(P_action.M_message_str[L_i], L_i); } setRegExp ( P_action.M_regularExpression); setIntCmd ( P_action.M_IntCmd ); #ifdef PCAPPLAY setPcapArgs ( P_action.M_pcapArgs ); #endif setRTPEchoActInfo(&(P_action.M_rtpecho_actinfo)); setRTPStreamActInfo(&(P_action.M_rtpstream_actinfo)); } CAction::CAction(scenario *scenario) { M_action = E_AT_NO_ACTION; M_varId = 0; M_varInId = 0; M_varIn2Id = 0; M_nbSubVarId = 0; M_maxNbSubVarId = 0; M_subVarId = NULL; M_checkIt = false; M_checkItInverse = false; M_lookingPlace = E_LP_MSG; M_lookingChar = NULL; M_caseIndep = false; M_occurrence = 1; M_headersOnly = true; for (int i = 0; i < MAX_ACTION_MESSAGE; i++) { M_message[i] = NULL; M_message_str[i] = NULL; } M_IntCmd = E_INTCMD_INVALID; M_doubleValue = 0; M_stringValue = NULL; M_distribution = NULL; #ifdef PCAPPLAY M_pcapArgs = NULL; #endif memset(&M_rtpecho_actinfo, 0, sizeof(M_rtpecho_actinfo)); memset(&M_rtpstream_actinfo, 0, sizeof(M_rtpstream_actinfo)); M_scenario = scenario; M_regExpSet = false; M_regularExpression = NULL; } CAction::~CAction() { if (M_lookingChar != NULL) { delete [] M_lookingChar; M_lookingChar = NULL; } for (int i = 0; i < MAX_ACTION_MESSAGE; i++) { if (M_message[i] != NULL) { delete M_message[i]; M_message[i] = NULL; } free(M_message_str[i]); M_message_str[i] = NULL; } if (M_subVarId != NULL) { delete [] M_subVarId; M_subVarId = NULL; } free(M_stringValue); #ifdef PCAPPLAY if (M_pcapArgs != NULL) { free_pcaps(M_pcapArgs); M_pcapArgs = NULL; } #endif if (M_regExpSet) { regfree(&M_internalRegExp); free(M_regularExpression); } if (M_distribution) { delete M_distribution; } } /****************************** CActions class ************************/ void CActions::printInfo() { printf("Action Size = [%d]\n", M_nbAction); for (int i = 0; i < M_nbAction; i++) { printf("actionlist[%d] : \n", i); char buf[80]; M_actionList[i]->printInfo(buf, 80); printf("%s\n", buf); } } void CActions::reset() { for (int i = 0; i < M_nbAction; i++) { delete M_actionList[i]; M_actionList[i] = NULL; } M_nbAction = 0; } int CActions::getActionSize() { return(M_nbAction); } void CActions::setAction(CAction *P_action) { CAction **newActions = new CAction*[M_nbAction + 1]; if (!newActions) { ERROR("Could not allocate new action list."); } for (int i = 0; i < M_nbAction; i++) { newActions[i] = M_actionList[i]; } if (M_actionList) { delete [] M_actionList; } M_actionList = newActions; M_actionList[M_nbAction] = P_action; M_nbAction++; } CAction* CActions::getAction(int i) { if (i < M_nbAction) { return(M_actionList[i]); } else return(NULL); } CActions::CActions() { M_nbAction = 0; M_actionList = NULL; } CActions::~CActions() { for (int i = 0; i < M_nbAction; i++) { delete M_actionList[i]; } delete [] M_actionList; M_actionList = NULL; } #ifdef GTEST #include "gtest/gtest.h" TEST(actions, MatchingRegexp) { AllocVariableTable vt(NULL); int id = vt.find("1", true); int sub1_id = vt.find("2", true); int sub2_id = vt.find("3", true); int sub3_id = vt.find("4", true); int sub4_id = vt.find("5", true); CAction re(NULL); re.setVarId(id); re.setNbSubVarId(4); re.setSubVarId(sub1_id); re.setSubVarId(sub2_id); re.setSubVarId(sub3_id); re.setSubVarId(sub4_id); re.setRegExp("(.+)(o) (.+)(d)"); int results = re.executeRegExp("hello world", &vt); ASSERT_EQ(5, results); ASSERT_STREQ("hello world", vt.getVar(id)->getString()); ASSERT_STREQ("hell", vt.getVar(sub1_id)->getString()); ASSERT_STREQ("o", vt.getVar(sub2_id)->getString()); ASSERT_STREQ("worl", vt.getVar(sub3_id)->getString()); ASSERT_STREQ("d", vt.getVar(sub4_id)->getString()); } TEST(actions, NonMatchingRegexp) { AllocVariableTable vt(NULL); int id = vt.find("1", true); int sub1_id = vt.find("2", true); int sub2_id = vt.find("3", true); int sub3_id = vt.find("4", true); int sub4_id = vt.find("5", true); CAction re(NULL); re.setVarId(id); re.setNbSubVarId(4); re.setSubVarId(sub1_id); re.setSubVarId(sub2_id); re.setSubVarId(sub3_id); re.setSubVarId(sub4_id); re.setRegExp("(.+)(o) (.+)(d)"); int results = re.executeRegExp("", &vt); ASSERT_EQ(0, results); ASSERT_STREQ("", vt.getVar(id)->getString()); ASSERT_STREQ("", vt.getVar(sub1_id)->getString()); } #endif sipp-3.7.2/src/auth.cpp0000664000000000000000000006120214525516253011633 0ustar /* * 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 * * Author : F. Tarek Rogers - 01 Sept 2004 * Russell Roy * Wolfgang Beck * Dragos Vingarzan - 02 February 2006 vingarzan@gmail.com * - split in the auth architecture * - introduced AKAv1-MD5 * Frederique Aurouet */ #if defined( __FreeBSD__) || defined(__DARWIN) || defined(__SUNOS) #include #endif #include #include #include #include #include "md5.h" #include "milenage.h" #include "screen.hpp" #include "logger.hpp" #include "auth.hpp" #define MAX_HEADER_LEN 2049 #define MD5_HASH_SIZE 16 #define HASH_HEX_SIZE 2*MD5_HASH_SIZE /* AKA */ #define KLEN 16 typedef u_char K[KLEN]; #define RANDLEN 16 typedef u_char RAND[RANDLEN]; #define AUTNLEN 16 typedef u_char AUTN[AUTNLEN]; #define AKLEN 6 typedef u_char AK[AKLEN]; #define AMFLEN 2 typedef u_char AMF[AMFLEN]; #define MACLEN 8 typedef u_char MAC[MACLEN]; #define CKLEN 16 typedef u_char CK[CKLEN]; #define IKLEN 16 typedef u_char IK[IKLEN]; #define SQNLEN 6 typedef u_char SQN[SQNLEN]; #define AUTSLEN 14 typedef char AUTS[AUTSLEN]; #define AUTS64LEN 29 typedef char AUTS64[AUTS64LEN]; #define RESLEN 8 typedef unsigned char RES[RESLEN+1]; #define RESHEXLEN 17 typedef char RESHEX[RESHEXLEN]; #define OPLEN 16 typedef u_char OP[OPLEN]; AMF amfstar="\0"; SQN sqn_he= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* end AKA */ static int createAuthHeaderMD5( const char* user, const char* password, int password_len, const char* method, const char* uri, const char* msgbody, const char* auth, const char* algo, unsigned int nonce_count, char* result, size_t result_len); static int createAuthHeaderAKAv1MD5( const char* user, const char* OP, const char* AMF, const char* K, const char* method, const char* uri, const char* msgbody, const char* auth, const char* algo, unsigned int nonce_count, char* result, size_t result_len); /* This function is from RFC 2617 Section 5 */ static void hashToHex(md5_byte_t* _b_raw, unsigned char* _h) { unsigned short i; unsigned char j; unsigned char *_b = (unsigned char *) _b_raw; for (i = 0; i < MD5_HASH_SIZE; i++) { j = (_b[i] >> 4) & 0xf; if (j <= 9) { _h[i * 2] = (j + '0'); } else { _h[i * 2] = (j + 'a' - 10); } j = _b[i] & 0xf; if (j <= 9) { _h[i * 2 + 1] = (j + '0'); } else { _h[i * 2 + 1] = (j + 'a' - 10); } }; _h[HASH_HEX_SIZE] = '\0'; } static char *stristr(const char* s1, const char* s2) { char *cp = (char*) s1; char *p1, *p2, *endp; char l, r; endp = (char*)s1 + (strlen(s1) - strlen(s2)) ; while (*cp && (cp <= endp)) { p1 = cp; p2 = (char*)s2; while (*p1 && *p2) { l = toupper(*p1); r = toupper(*p2); if (l != r) { break; } p1++; p2++; } if (*p2 == 0) { return cp; } cp++; } return 0; } int createAuthHeader( const char* user, const char* password, const char* method, const char* uri, const char* msgbody, const char* auth, const char* aka_OP, const char* aka_AMF, const char* aka_K, unsigned int nonce_count, char* result, size_t result_len) { char algo[32] = "MD5"; char *start, *end; if ((start = stristr(auth, "Digest")) == NULL) { snprintf(result, result_len, "createAuthHeader: authentication must be digest"); return 0; } if (!method) { snprintf(result, result_len, "createAuthHeader: authentication requires a method"); return 0; } if ((start = stristr(auth, "algorithm=")) != NULL) { start = start + strlen("algorithm="); if (*start == '"') { start++; } end = start + strcspn(start, " ,\"\r\n"); strncpy(algo, start, end - start); algo[end - start] ='\0'; } if (strncasecmp(algo, "MD5", 3)==0) { return createAuthHeaderMD5( user, password, strlen(password), method, uri, msgbody, auth, algo, nonce_count, result, result_len); } else if (strncasecmp(algo, "AKAv1-MD5", 9)==0) { if (!aka_K) { snprintf(result, result_len, "createAuthHeader: AKAv1-MD5 authentication requires a key"); return 0; } return createAuthHeaderAKAv1MD5( user, aka_OP, aka_AMF, aka_K, method, uri, msgbody, auth, algo, nonce_count, result, result_len); } else { snprintf(result, result_len, "createAuthHeader: authentication must use MD5 or AKAv1-MD5"); return 0; } } int getAuthParameter(const char *name, const char *header, char *result, int len) { char *start, *end; start = stristr(header, name); while (start) { // Ensure that the preceding character is "," or whitespace - this // stops us finding "cnonce" when we search for "nonce". char preceding_char = start[-1]; if ((preceding_char == ',') || isspace(preceding_char)) { break; } start = stristr(start+1, name); } if (!start) { result[0] = '\0'; return 0; } start += strlen(name); if (*start++ != '=') { return getAuthParameter(name, start, result, len); } if (*start == '"') { start++; end = start; while (*end != '"' && *end) { end++; } } else { end = start + strcspn(start, " ,\"\r\n"); } if (end - start >= len) { strncpy(result, start, len - 1); result[len - 1] = '\0'; } else { strncpy(result, start, end - start); result[end - start] = '\0'; } return end - start; } static int createAuthResponseMD5( const char* user, const char* password, int password_len, const char* method, const char* uri, const char* authtype, const char* msgbody, const char* realm, const char* nonce, const char* cnonce, const char* nc, unsigned char* result) { md5_byte_t ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE]; md5_byte_t resp[MD5_HASH_SIZE], body[MD5_HASH_SIZE]; unsigned char body_hex[HASH_HEX_SIZE+1]; unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1]; char tmp[MAX_HEADER_LEN]; md5_state_t Md5Ctx; // Load in A1 md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) user, strlen(user)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) realm, strlen(realm)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) password, password_len); md5_finish(&Md5Ctx, ha1); hashToHex(&ha1[0], &ha1_hex[0]); if (auth_uri) { snprintf(tmp, sizeof(tmp), "sip:%s", auth_uri); } else { strncpy(tmp, uri, sizeof(tmp) - 1); } // If using Auth-Int make a hash of the body - which is NULL for REG if (stristr(authtype, "auth-int") != NULL) { md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) msgbody, strlen(msgbody)); md5_finish(&Md5Ctx, body); hashToHex(&body[0], &body_hex[0]); } // Load in A2 md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) method, strlen(method)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) tmp, strlen(tmp)); if (stristr(authtype, "auth-int") != NULL) { md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) &body_hex, HASH_HEX_SIZE); } md5_finish(&Md5Ctx, ha2); hashToHex(&ha2[0], &ha2_hex[0]); md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) &ha1_hex, HASH_HEX_SIZE); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) nonce, strlen(nonce)); if (cnonce[0] != '\0') { md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) nc, strlen(nc)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) cnonce, strlen(cnonce)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) authtype, strlen(authtype)); } md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) &ha2_hex, HASH_HEX_SIZE); md5_finish(&Md5Ctx, resp); hashToHex(&resp[0], result); return 1; } int createAuthHeaderMD5( const char* user, const char* password, int password_len, const char* method, const char* uri, const char* msgbody, const char* auth, const char* algo, unsigned int nonce_count, char* result, size_t result_len) { unsigned char resp_hex[HASH_HEX_SIZE+1]; char realm[MAX_HEADER_LEN], sipuri[MAX_HEADER_LEN], nonce[MAX_HEADER_LEN], authtype[16], cnonce[32], nc[32], opaque[64]; int has_opaque = 0; int written = 0; // Extract the Auth Type - If not present, using 'none' cnonce[0] = '\0'; if (getAuthParameter("qop", auth, authtype, sizeof(authtype))) { // Sloppy auth type recognition (may be "auth,auth-int") if (stristr(authtype, "auth-int")) { strncpy(authtype, "auth-int", sizeof(authtype) - 1); } else if (stristr(authtype, "auth")) { strncpy(authtype, "auth", sizeof(authtype) - 1); } sprintf(cnonce, "%x", rand()); sprintf(nc, "%08x", nonce_count); } // Extract the Opaque value - if present if (getAuthParameter("opaque", auth, opaque, sizeof(opaque))) { has_opaque = 1; } // Extract the Realm if (!getAuthParameter("realm", auth, realm, sizeof(realm))) { snprintf(result, result_len, "createAuthHeaderMD5: couldn't parse realm in '%s'", auth); return 0; } written += snprintf( result + written, result_len - written, "Digest username=\"%s\",realm=\"%s\"", user, realm); // Construct the URI if (auth_uri == NULL) { snprintf(sipuri, sizeof(sipuri), "sip:%s", uri); } else { snprintf(sipuri, sizeof(sipuri), "sip:%s", auth_uri); } if (cnonce[0] != '\0') { // No double quotes around nc and qop (RFC3261): // // dig-resp = username / realm / nonce / digest-uri / dresponse // / algorithm / cnonce / opaque / message-qop // message-qop = "qop" EQUAL ("auth" / "auth-int" / token) // nonce-count = "nc" EQUAL 8LHEX // // The digest challenge does have double quotes however: // // digest-cln = realm / domain / nonce / opaque / stale / algorithm // / qop-options / auth-param // qop-options = "qop" EQUAL LDQUOT qop-value *("," qop-value) RDQUOT written += snprintf( result + written, result_len - written, ",cnonce=\"%s\",nc=%s,qop=%s", cnonce, nc, authtype); } written += snprintf( result + written, result_len - written, ",uri=\"%s\"", sipuri); // Extract the Nonce if (!getAuthParameter("nonce", auth, nonce, sizeof(nonce))) { snprintf(result, result_len, "createAuthHeader: couldn't parse nonce"); return 0; } createAuthResponseMD5( user, password, password_len, method, sipuri, authtype, msgbody, realm, nonce, cnonce, nc, &resp_hex[0]); written += snprintf( result + written, result_len - written, ",nonce=\"%s\",response=\"%s\",algorithm=%s", nonce, resp_hex, algo); if (has_opaque) { written += snprintf( result + written, result_len - written, ",opaque=\"%s\"", opaque); } return written; } int verifyAuthHeader(const char *user, const char *password, const char *method, const char *auth, const char *msgbody) { char algo[MAX_HEADER_LEN]; unsigned char result[HASH_HEX_SIZE + 1]; char response[HASH_HEX_SIZE + 1]; char realm[MAX_HEADER_LEN]; char nonce[MAX_HEADER_LEN]; char cnonce[MAX_HEADER_LEN]; char authtype[MAX_HEADER_LEN]; char nc[MAX_HEADER_LEN]; char uri[MAX_HEADER_LEN]; char *start; if ((start = stristr(auth, "Digest")) == NULL) { WARNING("verifyAuthHeader: authentication must be digest is %s", auth); return 0; } getAuthParameter("algorithm", auth, algo, sizeof(algo)); if (algo[0] == '\0') { strcpy(algo, "MD5"); } if (strncasecmp(algo, "MD5", 3)==0) { getAuthParameter("realm", auth, realm, sizeof(realm)); getAuthParameter("uri", auth, uri, sizeof(uri)); getAuthParameter("nonce", auth, nonce, sizeof(nonce)); getAuthParameter("cnonce", auth, cnonce, sizeof(cnonce)); getAuthParameter("nc", auth, nc, sizeof(nc)); getAuthParameter("qop", auth, authtype, sizeof(authtype)); createAuthResponseMD5( user, password, strlen(password), method, uri, authtype, msgbody, realm, nonce, cnonce, nc, result); getAuthParameter("response", auth, response, sizeof(response)); TRACE_CALLDEBUG("Processing verifyauth command - user %s, password %s, method %s, uri %s, realm %s, nonce %s, result expected %s, response from user %s\n", user, password, method, uri, realm, nonce, (char*)result, response); return !strcmp((char *)result, response); } else { WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo); return 0; } } /*" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ static int base64_val(char x) { switch(x) { case '=': return -1; case 'A': return 0; case 'B': return 1; case 'C': return 2; case 'D': return 3; case 'E': return 4; case 'F': return 5; case 'G': return 6; case 'H': return 7; case 'I': return 8; case 'J': return 9; case 'K': return 10; case 'L': return 11; case 'M': return 12; case 'N': return 13; case 'O': return 14; case 'P': return 15; case 'Q': return 16; case 'R': return 17; case 'S': return 18; case 'T': return 19; case 'U': return 20; case 'V': return 21; case 'W': return 22; case 'X': return 23; case 'Y': return 24; case 'Z': return 25; case 'a': return 26; case 'b': return 27; case 'c': return 28; case 'd': return 29; case 'e': return 30; case 'f': return 31; case 'g': return 32; case 'h': return 33; case 'i': return 34; case 'j': return 35; case 'k': return 36; case 'l': return 37; case 'm': return 38; case 'n': return 39; case 'o': return 40; case 'p': return 41; case 'q': return 42; case 'r': return 43; case 's': return 44; case 't': return 45; case 'u': return 46; case 'v': return 47; case 'w': return 48; case 'x': return 49; case 'y': return 50; case 'z': return 51; case '0': return 52; case '1': return 53; case '2': return 54; case '3': return 55; case '4': return 56; case '5': return 57; case '6': return 58; case '7': return 59; case '8': return 60; case '9': return 61; case '+': return 62; case '/': return 63; } return 0; } static char* base64_decode_string(const char* buf, unsigned int len, int* newlen) { unsigned long i; int j, x1, x2, x3, x4; char *out; out = (char *)malloc( ( len * 3/4 ) + 8 ); for(i=0, j=0; i + 3 < len; i += 4) { x1=base64_val(buf[i]); x2=base64_val(buf[i+1]); x3=base64_val(buf[i+2]); x4=base64_val(buf[i+3]); out[j++]=(x1<<2) | ((x2 & 0x30)>>4); out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2); out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F); } if (i>4); if (x3==-1) { out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2); if (x4==-1) { out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F); } } } } out[j++] = 0; *newlen=j; return out; } char base64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char hexa[17] = "0123456789abcdef"; static int createAuthHeaderAKAv1MD5( const char* user, const char* aka_OP, const char* aka_AMF, const char* aka_K, const char* method, const char* uri, const char* msgbody, const char* auth, const char* algo, unsigned int nonce_count, char* result, size_t result_len) { char tmp[MAX_HEADER_LEN]; char *start, *end; int has_auts = 0; int written = 0; char *nonce64, *nonce; int noncelen; AMF amf; OP op; RAND rnd; AUTS auts_bin; AUTS64 auts_hex; MAC mac, xmac; SQN sqn, sqnxoraka, sqn_ms; K k; RES res; CK ck; IK ik; AK ak; int i; // Extract the Nonce if ((start = stristr(auth, "nonce=")) == NULL) { snprintf(result, result_len, "createAuthHeaderAKAv1MD5: couldn't parse nonce"); return 0; } start = start + strlen("nonce="); if (*start == '"') { start++; } end = start + strcspn(start, " ,\"\r\n"); strncpy(tmp, start, end - start); tmp[end - start] ='\0'; /* Compute the AKA RES */ nonce64 = tmp; nonce = base64_decode_string(nonce64, end-start, &noncelen); if (noncelen < RANDLEN + AUTNLEN) { if (nonce) free(nonce); snprintf( result, result_len, "createAuthHeaderAKAv1MD5 : Nonce is too short %d < %d expected\n", noncelen, RANDLEN + AUTNLEN); return 0; } memcpy(rnd, nonce, RANDLEN); memcpy(sqnxoraka, nonce + RANDLEN, SQNLEN); memcpy(mac, nonce + RANDLEN + SQNLEN + AMFLEN, MACLEN); memcpy(k, aka_K, KLEN); memcpy(amf, aka_AMF, AMFLEN); memcpy(op, aka_OP, OPLEN); /* Compute the AK, response and keys CK IK */ f2345(k, rnd, res, ck, ik, ak, op); res[RESLEN] = '\0'; /* Compute sqn encoded in AUTN */ for (i=0; i < SQNLEN; i++) sqn[i] = sqnxoraka[i] ^ ak[i]; /* compute XMAC */ f1(k, rnd, sqn, (unsigned char *) aka_AMF, xmac, op); if (memcmp(mac, xmac, MACLEN) != 0) { free(nonce); snprintf( result, result_len, "createAuthHeaderAKAv1MD5 : MAC != expectedMAC -> Server might not know the secret (man-in-the-middle attack?)\n"); return 0; } /* Check SQN, compute AUTS if needed and authorization parameter */ /* the condition below is wrong. * Should trigger synchronization when sqn_ms>>3!=sqn_he>>3 for example. * Also, we need to store the SQN per user or put it as auth parameter. */ if (1/*sqn[5] > sqn_he[5]*/) { sqn_he[5] = sqn[5]; has_auts = 0; /* RES has to be used as password to compute response */ written = createAuthHeaderMD5( user, (const char *)res, RESLEN, method, uri, msgbody, auth, algo, nonce_count, result, result_len); if (written == 0) { free(nonce); snprintf( result, result_len, "createAuthHeaderAKAv1MD5 : Unexpected return value from createAuthHeaderMD5\n"); return 0; } } else { sqn_ms[5] = sqn_he[5] + 1; f5star(k, rnd, ak, op); for(i=0; i>4]; auts_hex[2*i+1] = hexa[auts_bin[i]&0x0F]; } auts_hex[AUTS64LEN-1] = 0; written += snprintf( result + written, result_len - written, ",auts=\"%s\"", auts_hex); } free(nonce); return written; } #ifdef GTEST #include "gtest/gtest.h" TEST(DigestAuth, nonce) { char nonce[40]; getAuthParameter("nonce", " Authorization: Digest cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", nonce, sizeof(nonce)); EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce); getAuthParameter("nonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", nonce, sizeof(nonce)); EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce); } TEST(DigestAuth, cnonce) { char cnonce[10]; getAuthParameter("cnonce", " Authorization: Digest cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce, sizeof(cnonce)); EXPECT_STREQ("c7e1249f", cnonce); getAuthParameter("cnonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", cnonce, sizeof(cnonce)); EXPECT_STREQ("c7e1249f", cnonce); } TEST(DigestAuth, MissingParameter) { char cnonce[10]; getAuthParameter("cnonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce, sizeof(cnonce)); EXPECT_EQ('\0', cnonce[0]); } TEST(DigestAuth, BasicVerification) { char* header = strdup(("Digest \r\n" " realm=\"testrealm@host.com\",\r\n" " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n," " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")); char result[255]; createAuthHeader("testuser", "secret", "REGISTER", "sip:example.com", "hello world", header, NULL, NULL, NULL, 1, result, 255); EXPECT_STREQ("Digest username=\"testuser\",realm=\"testrealm@host.com\",uri=\"sip:sip:example.com\",nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",response=\"db94e01e92f2b09a52a234eeca8b90f7\",algorithm=MD5,opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", result); EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result, "hello world")); free(header); } TEST(DigestAuth, qop) { char result[1024]; char* header = strdup(("Digest \r\n" "\trealm=\"testrealm@host.com\",\r\n" "\tqop=\"auth,auth-int\",\r\n" "\tnonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n," "\topaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")); createAuthHeader("testuser", "secret", "REGISTER", "sip:example.com", "hello world", header, NULL, NULL, NULL, 1, result, 1024); EXPECT_EQ(1, !!strstr(result, ",qop=auth-int,")); // no double quotes around qop-value EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result, "hello world")); free(header); } #endif //GTEST sipp-3.7.2/src/call.cpp0000664000000000000000000112555514525516253011622 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation * Roland Meub * Andy Aicken * Martin H. VanLeeuwen */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PCAPPLAY #include "send_packets.h" #endif #include "sipp.hpp" #include "auth.hpp" #include "urlcoder.hpp" #include "deadcall.hpp" #include "config.h" #include "version.h" template void split(const std::string &s, char delim, Out result) { std::stringstream ss; ss.str(s); std::string item; while (std::getline(ss, item, delim)) { *(result++) = item; } } std::vector split(const std::string &s, char delim) { std::vector elems; split(s, delim, std::back_inserter(elems)); return elems; } std::string join(const std::vector &s, const char* delim) { std::ostringstream imploded; std::copy(s.begin(), s.end(), std::ostream_iterator(imploded, delim)); std::string ret = imploded.str(); if (ret.length()) { ret.resize(ret.length() - strlen(delim)); } return ret; } std::string trim(const std::string &s) { size_t first = s.find_first_not_of(' '); if (first == string::npos) { return s; } size_t last = s.find_last_not_of(' '); return s.substr(first, (last - first + 1)); } #define callDebug(...) do { if (useCallDebugf) { _callDebug( __VA_ARGS__ ); } } while (0) extern map map_perip_fd; #ifdef PCAPPLAY /* send_packets pthread wrapper */ void *send_wrapper(void *); #endif int call::dynamicId = 0; int call::maxDynamicId = 10000+2000*4; // FIXME both param to be in command line !!!! int call::startDynamicId = 10000; // FIXME both param to be in command line !!!! int call::stepDynamicId = 4; // FIXME both param to be in command line !!!! /************** Call map and management routines **************/ static const int SM_UNUSED = -1; static unsigned int next_number = 1; static unsigned int get_tdm_map_number() { unsigned int nb = 0; unsigned int i=0; unsigned int interval=0; unsigned int random=0; bool found = false; /* Find a number in the tdm_map which is not in use */ interval = (tdm_map_a+1) * (tdm_map_b+1) * (tdm_map_c+1); random = rand() % interval; while ((iss_ipv6, false /* Not Auto. */, false); } call::call(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true) { init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic, isInitialization); } call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest) { static char call_id[MAX_HEADER_LEN]; const char * src = call_id_string; int count = 0; if(!next_number) { next_number ++; } while (*src && count < MAX_HEADER_LEN-1) { if (*src == '%') { ++src; switch(*src++) { case 'u': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1, "%u", next_number); break; case 'p': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1, "%u", pid); break; case 's': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1, "%s", local_ip); break; default: // treat all unknown sequences as %% call_id[count++] = '%'; break; } } else { call_id[count++] = *src++; } } call_id[count] = 0; return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */, false); } void call::init(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall) { #ifdef USE_TLS _srtpctxdebugfile = NULL; if (srtpcheck_debug) { if (sendMode == MODE_CLIENT) { _srtpctxdebugfile = fopen("srtpctxdebugfile_uac", "w"); } else if (sendMode == MODE_SERVER) { _srtpctxdebugfile = fopen("srtpctxdebugfile_uas", "w"); } if (_srtpctxdebugfile == NULL) { /* error encountered opening srtp ctx debug file */ WARNING("Error encountered opening srtp ctx debug file"); } } #endif // USE_TLS _sessionStateCurrent = eNoSession; _sessionStateOld = eNoSession; this->call_scenario = call_scenario; zombie = false; debugBuffer = NULL; debugLength = 0; msg_index = 0; last_send_index = 0; last_send_msg = NULL; last_send_len = 0; last_recv_hash = 0; last_recv_index = -1; last_recv_msg = NULL; last_recv_invite_cseq = 0; recv_retrans_hash = 0; recv_retrans_recv_index = -1; recv_retrans_send_index = -1; dialog_route_set = NULL; next_req_url = NULL; cseq = 0; next_retrans = 0; nb_retrans = 0; nb_last_delay = 0; paused_until = 0; call_port = 0; comp_state = NULL; start_time = clock_tick; call_established=false ; ack_is_pending=false ; last_recv_msg = NULL; cseq = base_cseq; nb_last_delay = 0; use_ipv6 = ipv6; queued_msg = NULL; dialog_authentication = NULL; dialog_challenge_type = 0; next_nonce_count = 1; #ifdef USE_TLS // // JLSRTP CLIENT context constants // if (srtpcheck_debug) { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::init(): (a) TX-UAC-AUDIO SRTP context - CLIENT setting SRTP header size to 12\n"); _txUACAudio.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (a) TX-UAC-VIDEO SRTP context - CLIENT setting SRTP header size to 12\n"); _txUACVideo.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (b) RX-UAC-AUDIO SRTP context - CLIENT setting SRTP header size to 12\n"); _rxUACAudio.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (b) RX-UAC-VIDEO SRTP context - CLIENT setting SRTP header size to 12\n"); _rxUACVideo.setSrtpHeaderSize(12); } } // // JLSRTP SERVER context constants // if (srtpcheck_debug) { if (sendMode == MODE_SERVER) { logSrtpInfo("call::init(): (c) RX-UAS-AUDIO SRTP context - SERVER setting SRTP header size to 12\n"); _rxUASAudio.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (c) RX-UAS-VIDEO SRTP context - SERVER setting SRTP header size to 12\n"); _rxUASVideo.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (d) TX-UAS-AUDIO SRTP context - SERVER setting SRTP header size to 12\n"); _txUASAudio.setSrtpHeaderSize(12); logSrtpInfo("call::init(): (d) TX-UAS-VIDEO SRTP context - SERVER setting SRTP header size to 12\n"); _txUASVideo.setSrtpHeaderSize(12); } } memset(_pref_audio_cs_out, 0, sizeof(_pref_audio_cs_out)); memset(_pref_video_cs_out, 0, sizeof(_pref_video_cs_out)); #endif // USE_TLS /* check and warn on rtpstream_new_call result? -> error alloc'ing mem */ rtpstream_new_call(&rtpstream_callinfo); #ifdef PCAPPLAY hasMediaInformation = 0; play_args_a.last_seq_no = 1200; play_args_v.last_seq_no = 2400; #endif call_remote_socket = NULL; if (socket) { associate_socket(socket); socket->ss_count++; } else { call_socket = NULL; } if (dest) { memcpy(&call_peer, dest, sizeof(call_peer)); } else { memset(&call_peer, 0, sizeof(call_peer)); } // initialising the CallVariable with the Scenario variable int i; VariableTable *userVars = NULL; bool putUserVars = false; if (userId) { int_vt_map::iterator it = userVarMap.find(userId); if (it != userVarMap.end()) { userVars = it->second; } } else { userVars = new VariableTable(userVariables); /* Creating this table creates a reference to it, but if it is really used, * then the refcount will be increased. */ putUserVars = true; } if (call_scenario->allocVars->size > 0) { M_callVariableTable = new VariableTable(userVars, call_scenario->allocVars->size); } else if (userVars && userVars->size > 0) { M_callVariableTable = userVars->getTable(); } else if (globalVariables->size > 0) { M_callVariableTable = globalVariables->getTable(); } else { M_callVariableTable = NULL; } if (putUserVars) { userVars->putTable(); } if (call_scenario->transactions.size() > 0) { transactions = (struct txnInstanceInfo *)malloc(sizeof(txnInstanceInfo) * call_scenario->transactions.size()); memset(transactions, 0, sizeof(struct txnInstanceInfo) * call_scenario->transactions.size()); } else { transactions = NULL; } // If not updated by a message we use the start time // information to compute rtd information start_time_rtd = (unsigned long long *)malloc(sizeof(unsigned long long) * call_scenario->stats->nRtds()); if (!start_time_rtd) { ERROR("Could not allocate RTD times!"); } rtd_done = (bool *)malloc(sizeof(bool) * call_scenario->stats->nRtds()); if (!start_time_rtd) { ERROR("Could not allocate RTD done!"); } for (i = 0; i < call_scenario->stats->nRtds(); i++) { start_time_rtd[i] = getmicroseconds(); rtd_done[i] = false; } // by default, last action result is NO_ERROR last_action_result = call::E_AR_NO_ERROR; this->userId = userId; /* For automatic answer calls to an out of call request, we must not */ /* increment the input files line numbers to not disturb */ /* the input files read mechanism (otherwise some lines risk */ /* to be systematically skipped */ if (!isAutomatic) { m_lineNumber = new file_line_map(); for (file_map::iterator file_it = inFiles.begin(); file_it != inFiles.end(); file_it++) { (*m_lineNumber)[file_it->first] = file_it->second->nextLine(userId); } } else { m_lineNumber = NULL; } this->initCall = isInitCall; #ifdef PCAPPLAY memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_i.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_a.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_i.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.from), 0, sizeof(struct sockaddr_storage)); hasMediaInformation = 0; media_thread = 0; #endif peer_tag = NULL; recv_timeout = 0; send_timeout = 0; timewait = false; if (!isAutomatic) { /* Not advancing the number is safe, because for automatic calls we do not * assign the identifier, the only other place it is used is for the auto * media port. */ number = next_number++; if (use_tdmmap) { tdm_map_number = get_tdm_map_number(); if (tdm_map_number == 0) { /* Can't create the new call */ WARNING("Can't create new outgoing call: all tdm_map circuits busy"); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_OUTBOUND_CONGESTION); this->zombie = true; return; } /* Mark the entry in the list as busy */ tdm_map[tdm_map_number - 1] = true; } else { tdm_map_number = 0; } } callDebug("Starting call %s\n", id); setRunning(); } bool call::checkAckCSeq(const char* msg) { static char request[65]; unsigned long int rcseq = 0; const char* ptr = NULL; rcseq = get_cseq_value(msg); memset(request, 0, sizeof(request)); if ((msg[0] == 'S') && (msg[1] == 'I') && (msg[2] == 'P') && (msg[3] == '/') && (msg[4] == '2') && (msg[5] == '.') && (msg[6] == '0')) { request[0]=0; } else if ((ptr = strchr(msg, ' '))) { if ((ptr - msg) < 64) { memcpy(request, msg, ptr - msg); request[ptr - msg] = 0; } else { ERROR("SIP method too long in received message '%s'", msg); } } else { ERROR("Invalid SIP message received '%s'", msg); } if ((default_behaviors & DEFAULT_BEHAVIOR_BADCSEQ) && !strncmp(request, "ACK", 3) && (rcseq != last_recv_invite_cseq)) { return false; } else { return true; } } int call::_callDebug(const char *fmt, ...) { va_list ap; if (!useCallDebugf) { return 0; } /* First we figure out how much to allocate. */ va_start(ap, fmt); int ret = vsnprintf(NULL, 0, fmt, ap); va_end(ap); debugBuffer = (char *)realloc(debugBuffer, debugLength + ret + TIME_LENGTH + 2); if (!debugBuffer) { ERROR("Could not allocate buffer (%d bytes) for callDebug file!", debugLength + ret + TIME_LENGTH + 2); } struct timeval now; gettimeofday(&now, NULL); debugLength += snprintf(debugBuffer + debugLength, TIME_LENGTH + 2, "%s ", CStat::formatTime(&now)); va_start(ap, fmt); debugLength += vsnprintf(debugBuffer + debugLength, ret + 1, fmt, ap); va_end(ap); return ret; } call::~call() { computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time); if(comp_state) { comp_free(&comp_state); } if (call_remote_socket && (call_remote_socket != main_remote_socket)) { call_remote_socket->close(); } /* Deletion of the call variable */ if(M_callVariableTable) { M_callVariableTable->putTable(); } if (m_lineNumber) { delete m_lineNumber; } if (userId) { CallGenerationTask::free_user(userId); } if (transactions) { for (unsigned int i = 0; i < call_scenario->transactions.size(); i++) { free(transactions[i].txnID); } free(transactions); } if (last_recv_msg) { free(last_recv_msg); } if (last_send_msg) { free(last_send_msg); } if (peer_tag) { free(peer_tag); } if (dialog_route_set) { free(dialog_route_set); } if (next_req_url) { free(next_req_url); } rtpstream_end_call(&rtpstream_callinfo); if (dialog_authentication) { free(dialog_authentication); } if (use_tdmmap) { tdm_map[tdm_map_number] = false; } # ifdef PCAPPLAY if (media_thread != 0) { pthread_cancel(media_thread); pthread_join(media_thread, NULL); } #endif free(start_time_rtd); free(rtd_done); free(debugBuffer); #ifdef USE_TLS if (srtpcheck_debug) { fclose(_srtpctxdebugfile); _srtpctxdebugfile = NULL; } #endif // USE_TLS } void call::setRtpEchoErrors(int value) { if (!initCall) { call_scenario->stats->setRtpEchoErrors(value); } } int call::getRtpEchoErrors() { if (!initCall) { return call_scenario->stats->getRtpEchoErrors(); } else { return 0; } } void call::computeStat (CStat::E_Action P_action) { if (initCall) { return; } call_scenario->stats->computeStat(P_action); } void call::computeStat (CStat::E_Action P_action, unsigned long P_value) { if (initCall) { return; } call_scenario->stats->computeStat(P_action, P_value); } void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) { if (initCall) { return; } call_scenario->stats->computeStat(P_action, P_value, which); } /* Dump call info to error log. */ void call::dump() { char s[MAX_HEADER_LEN]; int slen = sizeof(s); int written; written = snprintf(s, slen, "%s: State %d", id, msg_index); if (next_retrans) { written += snprintf(s + written, slen - written, " (next retrans %u)", next_retrans); } if (paused_until) { written += snprintf(s + written, slen - written, " (paused until %u)", paused_until); } if (recv_timeout) { written += snprintf(s + written, slen - written, " (recv timeout %u)", recv_timeout); } if (send_timeout) { written += snprintf(s + written, slen - written, " (send timeout %u)", send_timeout); } WARNING("%s", s); } bool call::connect_socket_if_needed() { bool existing; if(call_socket) return true; if(!multisocket) return true; if(transport == T_UDP) { struct sockaddr_storage saddr; if(sendMode != MODE_CLIENT) return true; char peripaddr[256]; if (!peripsocket) { if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket (1)"); } } else { char *tmp = peripaddr; getFieldFromInputFile(ip_file, peripfield, NULL, tmp); map::iterator i; i = map_perip_fd.find(peripaddr); if (i == map_perip_fd.end()) { // Socket does not exist if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket (2)"); } else { /* Ensure that it stays persistent, because it is recorded in the map. */ call_socket->ss_count++; map_perip_fd[peripaddr] = call_socket; } } else { // Socket exists already associate_socket(i->second); existing = true; i->second->ss_count++; } } if (existing) { return true; } memcpy(&saddr, &local_addr_storage, sizeof(struct sockaddr_storage)); if (use_ipv6) { saddr.ss_family = AF_INET6; } else { saddr.ss_family = AF_INET; } if (peripsocket) { gai_getsockaddr(&saddr, peripaddr, local_port, AI_PASSIVE, AF_UNSPEC); } if (sipp_bind_socket(call_socket, &saddr, &call_port)) { ERROR_NO("Unable to bind UDP socket"); } } else { /* TCP, SCTP or TLS. */ struct sockaddr_storage *L_dest = &remote_sockaddr; if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a TCP/SCTP/TLS socket"); } call_socket->ss_count++; if (existing) { return true; } sipp_customize_socket(call_socket); if (use_remote_sending_addr) { L_dest = &remote_sending_sockaddr; } if (call_socket->connect(L_dest)) { if (reconnect_allowed()) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { WARNING("Unable to connect a TCP/SCTP/TLS socket"); } /* This connection failed. We must be in multisocket mode, because * otherwise we would already have a call_socket. This call can not * succeed, but does not affect any of our other calls. We do decrement * the reconnection counter however. */ if (reset_number != -1) { reset_number--; } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TCP_CONNECT); delete this; return false; } else { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { ERROR_NO("Unable to connect a TCP/SCTP/TLS socket"); } } } call_port = call_socket->ss_port; } return true; } bool call::lost(int index) { static int inited = 0; double percent = global_lost; if(!lose_packets) return false; if (call_scenario->messages[index]->lost >= 0) { percent = call_scenario->messages[index]->lost; } if (percent == 0) { return false; } if(!inited) { srand((unsigned int) time(NULL)); inited = 1; } return (((double)rand() / (double)RAND_MAX) < (percent / 100.0)); } int call::send_raw(const char * msg, int index, int len) { SIPpSocket *sock; int rc; callDebug("Sending %s message for call %s (index %d, hash %lu):\n%s\n\n", TRANSPORT_TO_STRING(transport), id, index, hash(msg), msg); if((index!=-1) && (lost(index))) { TRACE_MSG("%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport)); callDebug("%s message voluntary lost (while sending) (index %d, hash %lu).\n", TRANSPORT_TO_STRING(transport), index, hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[index] -> nb_lost++; return 0; } sock = call_socket; if ((use_remote_sending_addr) && (sendMode == MODE_SERVER)) { if (!call_remote_socket) { if (multisocket || !main_remote_socket) { struct sockaddr_storage *L_dest = &remote_sending_sockaddr; if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) { ERROR_NO("Unable to get a socket for rsa option"); } sipp_customize_socket(call_remote_socket); if(transport != T_UDP) { if (call_remote_socket->connect(L_dest)) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport)); } else { ERROR_NO("Unable to connect a socket for rsa option"); } } } if (!multisocket) { main_remote_socket = call_remote_socket; } } if (!multisocket) { call_remote_socket = main_remote_socket; main_remote_socket->ss_count++; } } sock=call_remote_socket ; } // If the length hasn't been explicitly specified, treat the message as a string if (len==0) { len = strlen(msg); } assert(sock); rc = sock->write(msg, len, WS_BUFFER, &call_peer); if(rc < 0 && errno == EWOULDBLOCK) { return rc; } if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CANNOT_SEND_MSG); delete this; } return rc; /* OK */ } /* This method is used to send messages that are not */ /* part of the XML scenario */ void call::sendBuffer(char * msg, int len) { /* call send_raw but with a special scenario index */ if (send_raw(msg, -1, len) < 0) { if (sendbuffer_warn) { ERROR_NO("Error sending raw message"); } else { WARNING_NO("Error sending raw message"); } } } char * call::get_header_field_code(const char *msg, const char * name) { static char code[MAX_HEADER_LEN]; const char * last_header; int i; last_header = NULL; i = 0; /* If we find the field in msg */ last_header = get_header_content(msg, name); if(last_header) { /* Extract the integer value of the field */ while(isspace(*last_header)) last_header++; sscanf(last_header, "%d", &i); sprintf(code, "%s %d", name, i); } return code; } char * call::get_last_header(const char * name) { int len; if((!last_recv_msg) || (!strlen(last_recv_msg))) { return NULL; } len = strlen(name); /* Ideally this check should be moved to the XML parser so that it is not * along a critical path. We could also handle lowercasing there. */ if (len > MAX_HEADER_LEN) { ERROR("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name)); } if (name[len - 1] == ':') { return get_header(last_recv_msg, name, false); } else { char with_colon[MAX_HEADER_LEN]; sprintf(with_colon, "%s:", name); return get_header(last_recv_msg, with_colon, false); } } /* Return the last request URI from the To header. On any error returns the * empty string. The caller must free the result. */ char * call::get_last_request_uri() { char * tmp; char * tmp2; char * last_request_uri; int tmp_len; char * last_To = get_last_header("To:"); if (!last_To) { return strdup(""); } tmp = strchr(last_To, '<'); if (!tmp) { return strdup(""); } tmp++; tmp2 = strchr(last_To, '>'); if (!tmp2) { return strdup(""); } tmp_len = strlen(tmp) - strlen(tmp2); if (tmp_len < 0) { return strdup(""); } if (!(last_request_uri = (char *)malloc(tmp_len + 1))) { ERROR("Cannot allocate!"); } last_request_uri[0] = '\0'; if (tmp && (tmp_len > 0)) { strncpy(last_request_uri, tmp, tmp_len); } last_request_uri[tmp_len] = '\0'; return last_request_uri; } char * call::send_scene(int index, int *send_status, int *len) { #define MAX_MSG_NAME_SIZE 30 static char msg_name[MAX_MSG_NAME_SIZE]; char *L_ptr1 ; char *L_ptr2 ; int uselen = 0; assert(send_status); /* Socket port must be known before string substitution */ if (!connect_socket_if_needed()) { *send_status = -2; return NULL; } assert(call_socket); assert(call_scenario->messages[index]->send_scheme); if (!len) { len = &uselen; } char * dest; dest = createSendingMessage(call_scenario->messages[index]->send_scheme, index, len); if (!dest) { *send_status = -2; return NULL; } L_ptr1=msg_name ; L_ptr2=dest ; while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t')) { *L_ptr1 = *L_ptr2; L_ptr1 ++; L_ptr2 ++; } *L_ptr1 = '\0' ; if (strcmp(msg_name, "ACK") == 0) { call_established = true ; ack_is_pending = false ; } *send_status = send_raw(dest, index, *len); return dest; } void call::do_bookkeeping(message *curmsg) { /* If this message increments a counter, do it now. */ if (curmsg -> counter) { computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, curmsg->counter - 1); } /* If this message can be used to compute RTD, do it now */ if (curmsg->start_rtd) { start_time_rtd[curmsg->start_rtd - 1] = getmicroseconds(); } if (curmsg->stop_rtd) { int rtd = curmsg->stop_rtd; if (!rtd_done[rtd - 1]) { unsigned long long start = start_time_rtd[rtd - 1]; unsigned long long end = getmicroseconds(); if (dumpInRtt) { call_scenario->stats->computeRtt(start, end, rtd); } computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION, (end - start) / 1000, rtd - 1); if (!curmsg->repeat_rtd) { rtd_done[rtd - 1] = true; } } } } void call::tcpClose() { terminate(CStat::E_FAILED_TCP_CLOSED); } void call::terminate(CStat::E_Action reason) { char reason_str[100]; stopListening(); // Call end -> was it successful? if(call::last_action_result != call::E_AR_NO_ERROR) { switch(call::last_action_result) { case call::E_AR_REGEXP_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp match failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_REGEXP_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp matched, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_HDR_NOT_FOUND: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp header not found at index %d", msg_index); new deadcall(id, reason_str); } break; case E_AR_CONNECT_FAILED: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TCP_CONNECT); if (deadcall_wait && !initCall) { sprintf(reason_str, "connection failed %d", msg_index); new deadcall(id, reason_str); } break; case E_AR_RTPECHO_ERROR: computeStat(CStat::E_CALL_FAILED); setRtpEchoErrors(1); if (deadcall_wait && !initCall) { sprintf(reason_str, "rtp echo error %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_NO_ERROR: case call::E_AR_STOP_CALL: /* Do nothing. */ break; case call::E_AR_TEST_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TEST_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_TEST_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TEST_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test succeeded, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_STRCMP_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_STRCMP_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_STRCMP_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_STRCMP_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test succeeded, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; } } else { if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) { computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED); if (deadcall_wait && !initCall) { new deadcall(id, "successful"); } } else { computeStat(CStat::E_CALL_FAILED); if (reason != CStat::E_NO_ACTION) { computeStat(reason); } if (deadcall_wait && !initCall) { sprintf(reason_str, "failed at index %d", msg_index); new deadcall(id, reason_str); } } } delete this; } bool call::next() { msgvec * msgs = &call_scenario->messages; if (initCall) { msgs = &call_scenario->initmessages; } int test; /* What is the next message index? */ /* Default without branching: use the next message */ int new_msg_index = msg_index + 1; /* If branch needed, overwrite this default */ if (msg_index >= 0 && ((*msgs)[msg_index]->next >= 0) && (((test = ((*msgs)[msg_index]->test)) == -1) || M_callVariableTable->getVar(test)->isSet())) { /* Branching possible, check the probability */ int chance = (*msgs)[msg_index]->chance; if ((chance <= 0) || (rand() > chance )) { /* Branch == overwrite with the 'next' attribute value */ new_msg_index = (*msgs)[msg_index]->next; } } msg_index = new_msg_index; recv_timeout = 0; if (msg_index >= (int)((*msgs).size())) { terminate(CStat::E_CALL_SUCCESSFULLY_ENDED); return false; } return true; } bool call::executeMessage(message *curmsg) { T_ActionResult actionResult = E_AR_NO_ERROR; if (curmsg->pause_distribution || curmsg->pause_variable != -1) { unsigned int pause; if (curmsg->pause_distribution) { double actualpause = curmsg->pause_distribution->sample(); if (actualpause < 1) { // Protect against distribution samples that give // negative results (and so pause for ~50 hours when // cast to a unsigned int). pause = 0; } else { pause = (unsigned int)actualpause; }; } else { int varId = curmsg->pause_variable; pause = (int) M_callVariableTable->getVar(varId)->getDouble(); } if (pause > INT_MAX) { pause = INT_MAX; } paused_until = clock_tick + pause; /* This state is used as the last message of a scenario, just for handling * final retransmissions. If the connection closes, we do not mark it is * failed. */ this->timewait = curmsg->timewait; /* Increment the number of sessions in pause state */ curmsg->sessions++; do_bookkeeping(curmsg); executeAction(NULL, curmsg); callDebug("Pausing call until %d (is now %ld).\n", paused_until, clock_tick); setPaused(); return true; } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) { int send_status; if(next_retrans) { return true; } send_status = sendCmdMessage(curmsg); if(send_status != 0) { /* Send error */ return false; /* call deleted */ } curmsg -> M_nbCmdSent++; next_retrans = 0; do_bookkeeping(curmsg); executeAction(NULL, curmsg); return(next()); } else if(curmsg -> M_type == MSG_TYPE_NOP) { callDebug("Executing NOP at index %d.\n", curmsg->index); do_bookkeeping(curmsg); actionResult = executeAction(NULL, curmsg); last_action_result = actionResult; if (actionResult == E_AR_RTPECHO_ERROR) { terminate(CStat::E_CALL_FAILED); return false; } else { return next(); } } else if(curmsg -> send_scheme) { char * msg_snd; int msgLen; int send_status; /* Do not send a new message until the previous one which had * retransmission enabled is acknowledged */ if(next_retrans) { setPaused(); return true; } /* Handle counters and RTDs for this message. */ do_bookkeeping(curmsg); /* decide whether to increment cseq or not * basically increment for anything except response, ACK or CANCEL * Note that cseq is only used by the [cseq] keyword, and * not by default */ int incr_cseq = 0; if (!curmsg->send_scheme->isAck() && !curmsg->send_scheme->isCancel() && !curmsg->send_scheme->isResponse()) { ++cseq; incr_cseq = 1; } msg_snd = send_scene(msg_index, &send_status, &msgLen); if (!msg_snd) { /* This will hit connect_if_needed, and if it fails, the entire call is deleted... */ ERROR("Call failed, cannot continue safely..."); } if(send_status < 0 && errno == EWOULDBLOCK) { if (incr_cseq) --cseq; /* Have we set the timeout yet? */ if (send_timeout) { /* If we have actually timed out. */ if (clock_tick > send_timeout) { WARNING("Call-Id: %s, send timeout on message %s:%d: aborting call", id, curmsg->desc, curmsg->index); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } } else if (curmsg->timeout) { /* Initialize the send timeout to the per message timeout. */ send_timeout = clock_tick + curmsg->timeout; } else if (defl_send_timeout) { /* Initialize the send timeout to the global timeout. */ send_timeout = clock_tick + defl_send_timeout; } return true; /* No step, nothing done, retry later */ } else if(send_status < 0) { /* Send error */ /* The call was already deleted by connect_socket_if_needed or send_raw, * so we should no longer access members. */ return false; } /* We have sent the message, so the timeout is no longer needed. */ send_timeout = 0; last_send_index = curmsg->index; last_send_len = msgLen; realloc_ptr = (char *) realloc(last_send_msg, msgLen+1); if (realloc_ptr) { last_send_msg = realloc_ptr; } else { free(last_send_msg); ERROR("Out of memory!"); return false; } memcpy(last_send_msg, msg_snd, msgLen); last_send_msg[msgLen] = '\0'; if (curmsg->start_txn) { transactions[curmsg->start_txn - 1].txnID = (char *)realloc(transactions[curmsg->start_txn - 1].txnID, MAX_HEADER_LEN); extract_transaction(transactions[curmsg->start_txn - 1].txnID, last_send_msg); } if (curmsg->ack_txn) { transactions[curmsg->ack_txn - 1].ackIndex = curmsg->index; } if(last_recv_index >= 0) { /* We are sending just after msg reception. There is a great * chance that we will be asked to retransmit this message */ recv_retrans_hash = last_recv_hash; recv_retrans_recv_index = last_recv_index; recv_retrans_send_index = curmsg->index; callDebug("Set Retransmission Hash: %lu (recv index %d, send index %d)\n", recv_retrans_hash, recv_retrans_recv_index, recv_retrans_send_index); /* Prevent from detecting the cause relation between send and recv * in the next valid send */ last_recv_hash = 0; } /* Update retransmission information */ if(curmsg -> retrans_delay) { if((transport == T_UDP) && (retrans_enabled)) { next_retrans = clock_tick + curmsg -> retrans_delay; nb_retrans = 0; nb_last_delay = curmsg->retrans_delay; } } else { next_retrans = 0; } executeAction(msg_snd, curmsg); /* Update scenario statistics */ curmsg -> nb_sent++; return next(); } else if (curmsg->M_type == MSG_TYPE_RECV || curmsg->M_type == MSG_TYPE_RECVCMD ) { if (queued_msg) { char *msg = queued_msg; queued_msg = NULL; bool ret = process_incoming(msg); free(msg); return ret; } else if (recv_timeout) { if(recv_timeout > getmilliseconds()) { setPaused(); return true; } recv_timeout = 0; curmsg->nb_timeout++; if (curmsg->on_timeout < 0) { // if you set a timeout but not a label, the call is aborted WARNING("Call-Id: %s, receive timeout on message %s:%d without label to jump to (ontimeout attribute): aborting call", id, curmsg->desc, curmsg->index); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } WARNING("Call-Id: %s, receive timeout on message %s:%d, jumping to label %d", id, curmsg->desc, curmsg->index, curmsg->on_timeout); /* FIXME: We should do something like set index here, but it probably * does not matter too much as only nops are allowed in the init stanza. */ msg_index = curmsg->on_timeout; recv_timeout = 0; if (msg_index < (int)call_scenario->messages.size()) return true; // special case - the label points to the end - finish the call computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } else if (curmsg->timeout || defl_recv_timeout) { if (curmsg->timeout) // If timeout is specified on message receive, use it recv_timeout = getmilliseconds() + curmsg->timeout; else // Else use the default timeout if specified recv_timeout = getmilliseconds() + defl_recv_timeout; return true; } else { /* We are going to wait forever. */ setPaused(); } } else { WARNING("Unknown message type at %s:%d: %d", curmsg->desc, curmsg->index, curmsg->M_type); } return true; } bool call::run() { bool bInviteTransaction = false; assert(running); if (zombie) { delete this; return false; } update_clock_tick(); message *curmsg; if (initCall) { if(msg_index >= (int)call_scenario->initmessages.size()) { ERROR("Scenario initialization overrun for call %s (%p) (index = %d)", id, _RCAST(void*, this), msg_index); } curmsg = call_scenario->initmessages[msg_index]; } else { if(msg_index >= (int)call_scenario->messages.size()) { ERROR("Scenario overrun for call %s (%p) (index = %d)", id, _RCAST(void*, this), msg_index); } curmsg = call_scenario->messages[msg_index]; } callDebug("Processing message %d of type %d for call %s at %lu.\n", msg_index, curmsg->M_type, id, clock_tick); if (curmsg->condexec != -1) { bool exec = M_callVariableTable->getVar(curmsg->condexec)->isSet(); if (curmsg->condexec_inverse) { exec = !exec; } if (!exec) { callDebug("Conditional variable %s %s set, so skipping message %d.\n", call_scenario->allocVars->getName(curmsg->condexec), curmsg->condexec_inverse ? "" : "not", msg_index); return next(); } } /* Manages retransmissions or delete if max retrans reached */ if(next_retrans && (next_retrans < clock_tick)) { nb_retrans++; if ( (0 == strncmp (last_send_msg, "INVITE", 6)) ) { bInviteTransaction = true; } int rtAllowed = min(bInviteTransaction ? max_invite_retrans : max_non_invite_retrans, max_udp_retrans); callDebug("Retransmisison required (%d retransmissions, max %d)\n", nb_retrans, rtAllowed); if(nb_retrans > rtAllowed) { call_scenario->messages[last_send_index] -> nb_timeout ++; if (call_scenario->messages[last_send_index]->on_timeout >= 0) { // action on timeout WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", id, msg_index, call_scenario->messages[last_send_index]->on_timeout); msg_index = call_scenario->messages[last_send_index]->on_timeout; next_retrans = 0; recv_timeout = 0; if (msg_index < (int)call_scenario->messages.size()) { return true; } // here if asked to go to the last label delete the call computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { // Abort the call by sending proper SIP message return(abortCall(true)); } else { // Just delete existing call delete this; return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { // Abort the call by sending proper SIP message WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id); return(abortCall(true)); } else { // Just delete existing call delete this; return false; } } else { nb_last_delay *= 2; if (global_t2 < nb_last_delay) { if (!bInviteTransaction) { nb_last_delay = global_t2; } } if(send_raw(last_send_msg, last_send_index, last_send_len) < -1) { return false; } call_scenario->messages[last_send_index] -> nb_sent_retrans++; computeStat(CStat::E_RETRANSMISSION); next_retrans = clock_tick + nb_last_delay; } } if(paused_until) { /* Process a pending pause instruction until delay expiration */ if(paused_until > clock_tick) { callDebug("Call is paused until %d (now %ld).\n", paused_until, clock_tick); setPaused(); callDebug("Running: %d (wake %d).\n", running, wake()); return true; } /* Our pause is over. */ callDebug("Pause complete, waking up.\n"); paused_until = 0; return next(); } return executeMessage(curmsg); } const char *default_message_names[] = { "3pcc_abort", "ack", "ack2", "bye", "cancel", "200", }; const char *default_message_strings[] = { /* 3pcc_abort */ "call-id: [call_id]\ninternal-cmd: abort_call\n\n", /* ack */ "ACK [last_Request_URI] SIP/2.0\n" "[last_Via]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] ACK\n" "Contact: \n" "Max-Forwards: 70\n" "Subject: Performance Test\n" "Content-Length: 0\n\n", /* ack2, the only difference is Via, I don't quite know why. */ "ACK [last_Request_URI] SIP/2.0\n" "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] ACK\n" "Contact: \n" "Max-Forwards: 70\n" "Subject: Performance Test\n" "Content-Length: 0\n\n", /* bye */ "BYE [last_Request_URI] SIP/2.0\n" "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number+1] BYE\n" "Max-Forwards: 70\n" "Contact: \n" "Content-Length: 0\n\n", /* cancel */ "CANCEL [last_Request_URI] SIP/2.0\n" "[last_Via]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] CANCEL\n" "Max-Forwards: 70\n" "Contact: \n" "Content-Length: 0\n\n", /* 200 */ "SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_From:]\n" "[last_To:]\n" "[last_Call-ID:]\n" "[last_CSeq:]\n" "Contact: \n" "Content-Length: 0\n\n" }; SendingMessage **default_messages; void init_default_messages() { int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]); default_messages = new SendingMessage* [messages]; for (int i = 0; i < messages; i++) { default_messages[i] = new SendingMessage(main_scenario, const_cast(default_message_strings[i])); } } void free_default_messages() { int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]); if (!default_messages) { return; } for (int i = 0; i < messages; i++) { delete default_messages[i]; } delete [] default_messages; } SendingMessage *get_default_message(const char *which) { int messages = sizeof(default_message_names)/sizeof(default_message_names[0]); for (int i = 0; i < messages; i++) { if (!strcmp(which, default_message_names[i])) { return default_messages[i]; } } ERROR("Internal Error: Unknown default message: %s!", which); } void set_default_message(const char *which, char *msg) { int messages = sizeof(default_message_names)/sizeof(default_message_names[0]); for (int i = 0; i < messages; i++) { if (!strcmp(which, default_message_names[i])) { default_message_strings[i] = msg; return; } } ERROR("Internal Error: Unknown default message: %s!", which); } bool call::process_unexpected(const char* msg) { char buffer[MAX_HEADER_LEN]; char *desc = buffer; int res = 0; message *curmsg = call_scenario->messages[msg_index]; curmsg->nb_unexp++; if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Aborting "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Continuing "); } desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "call on unexpected message for Call-Id '%s': ", id); if (curmsg -> M_type == MSG_TYPE_RECV) { if (curmsg -> recv_request) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%s' ", curmsg -> recv_request); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%d' ", curmsg -> recv_response); } } else if (curmsg -> M_type == MSG_TYPE_SEND) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending "); } else if (curmsg -> M_type == MSG_TYPE_PAUSE) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while pausing "); } else if (curmsg -> M_type == MSG_TYPE_SENDCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending command "); } else if (curmsg -> M_type == MSG_TYPE_RECVCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting command "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while in message type %d ", curmsg->M_type); } snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "(index %d)", msg_index); WARNING("%s, received '%s'", buffer, msg); TRACE_MSG("-----------------------------------------------\n" "Unexpected %s message received:\n\n%s\n", TRANSPORT_TO_STRING(transport), msg); callDebug("Unexpected %s message received (index %d, hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), msg_index, hash(msg), msg); if (get_reply_code(msg)) { this->call_scenario->stats->error_codes.push_back(get_reply_code(msg)); } if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"))); if (res < 0) { WARNING("sendCmdBuffer returned %d", res); return false; } } // usage of last_ keywords => for call aborting realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, msg); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } else { // Do not abort call nor send anything in reply if default behavior is disabled return false; } } void call::abort() { WARNING("Aborted call with Call-ID '%s'", id); abortCall(false); } bool call::abortCall(bool writeLog) { int is_inv; char * src_recv = NULL ; callDebug("Aborting call %s (index %d).\n", id, msg_index); if (last_send_msg != NULL) { is_inv = !strncmp(last_send_msg, "INVITE", 6); } else { is_inv = false; } if ((creationMode != MODE_SERVER) && (msg_index > 0)) { if ((call_established == false) && (is_inv)) { src_recv = last_recv_msg ; // Answer unexpected errors (4XX, 5XX and beyond) with an ACK // Contributed by F. Tarek Rogers if((src_recv) && (get_reply_code(src_recv) >= 400)) { sendBuffer(createSendingMessage(get_default_message("ack"))); } else if (src_recv) { /* Call is not established and the reply is not a 4XX, 5XX */ /* And we already received a message. */ if (ack_is_pending == true) { /* If an ACK is expected from the other side, send it * and send a BYE afterwards. */ ack_is_pending = false; sendBuffer(createSendingMessage(get_default_message("ack"))); /* Send the BYE */ sendBuffer(createSendingMessage(get_default_message("bye"))); } else { /* Send a CANCEL */ sendBuffer(createSendingMessage(get_default_message("cancel"))); } } else { /* Call is not established and the reply is not a 4XX, 5XX */ /* and we didn't received any message. This is the case when */ /* we are aborting after having send an INVITE and not received */ /* any answer. */ /* Do nothing ! */ } } else if (last_recv_msg) { /* The call may not be established, if we haven't yet received a message, * because the earlier check depends on the first message being an INVITE * (although it could be something like a message message, therefore we * check that we received a message. */ sendBuffer(createSendingMessage(get_default_message("bye"))); } } if (writeLog && useCallDebugf) { TRACE_CALLDEBUG ("-------------------------------------------------------------------------------\n"); TRACE_CALLDEBUG ("Call debugging information for call %s:\n", id); TRACE_CALLDEBUG("%s", debugBuffer); } stopListening(); if (deadcall_wait && !initCall) { char reason[100]; sprintf(reason, "aborted at index %d", msg_index); new deadcall(id, reason); } delete this; return false; } bool call::rejectCall() { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CALL_REJECTED); delete this; return false; } int call::sendCmdMessage(message *curmsg) { char * dest; char delimitor[2]; delimitor[0]=27; delimitor[1]=0; /* 3pcc extended mode */ char * peer_dest; SIPpSocket **peer_socket; if(curmsg -> M_sendCmdData) { // WARNING("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData); dest = createSendingMessage(curmsg->M_sendCmdData); strcat(dest, delimitor); //WARNING("---SEND_TWIN_CMD---%s---", dest); int rc; /* 3pcc extended mode */ peer_dest = curmsg->peer_dest; if(peer_dest) { peer_socket = get_peer_socket(peer_dest); rc = (*peer_socket)->write(dest, strlen(dest), WS_BUFFER, &call_peer); } else { rc = twinSippSocket->write(dest, strlen(dest), WS_BUFFER, &call_peer); } if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete this; return(-1); } return(0); } else return(-1); } int call::sendCmdBuffer(char* cmd) { char * dest; char delimitor[2]; int rc; delimitor[0]=27; delimitor[1]=0; dest = cmd ; strcat(dest, delimitor); rc = twinSippSocket->write(dest, strlen(dest), WS_BUFFER, &twinSippSocket->ss_dest); if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete this; return(-1); } return(0); } char* call::createSendingMessage(SendingMessage *src, int P_index, int *msgLen) { static char msg_buffer[SIPP_MAX_MSG_SIZE+2]; return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer), msgLen); } char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len, int *msgLen) { char * length_marker = NULL; char * auth_marker = NULL; MessageComponent *auth_comp = NULL; bool auth_comp_allocated = false; int len_offset = 0; char *dest = msg_buffer; bool suppresscrlf = false; #ifdef USE_TLS bool srtp_audio_updated = false; bool srtp_video_updated = false; // OUTGOING SRTP PARAM CONTEXT SrtpAudioInfoParams pA; SrtpVideoInfoParams pV; pA.audio_found = false; pA.primary_audio_cryptotag = 0; memset(pA.primary_audio_cryptosuite, 0, sizeof(pA.primary_audio_cryptosuite)); memset(pA.primary_audio_cryptokeyparams, 0, sizeof(pA.primary_audio_cryptokeyparams)); pA.secondary_audio_cryptotag = 0; memset(pA.secondary_audio_cryptosuite, 0, sizeof(pA.secondary_audio_cryptosuite)); memset(pA.secondary_audio_cryptokeyparams, 0, sizeof(pA.secondary_audio_cryptokeyparams)); pA.primary_unencrypted_audio_srtp = false; pA.secondary_unencrypted_audio_srtp = false; pV.video_found = false; pV.primary_video_cryptotag = 0; memset(pV.primary_video_cryptosuite, 0, sizeof(pV.primary_video_cryptosuite)); memset(pV.primary_video_cryptokeyparams, 0, sizeof(pV.primary_video_cryptokeyparams)); pV.secondary_video_cryptotag = 0; memset(pV.secondary_video_cryptosuite, 0, sizeof(pV.secondary_video_cryptosuite)); memset(pV.secondary_video_cryptokeyparams, 0, sizeof(pV.secondary_video_cryptokeyparams)); pV.primary_unencrypted_video_srtp = false; pV.secondary_unencrypted_video_srtp = false; #endif // USE_TLS msg_buffer[0] = '\0'; for (int i = 0; i < src->numComponents(); i++) { MessageComponent *comp = src->getComponent(i); int left = buf_len - (dest - msg_buffer); switch(comp->type) { case E_Message_Literal: if (suppresscrlf) { char *ptr = comp->literal; while (isspace(*ptr)) ptr++; dest += snprintf(dest, left, "%s", ptr); suppresscrlf = false; } else { memcpy(dest, comp->literal, comp->literalLen); dest += comp->literalLen; *dest = '\0'; } break; case E_Message_Remote_IP: dest += snprintf(dest, left, "%s", remote_ip_w_brackets); break; case E_Message_Remote_Host: dest += snprintf(dest, left, "%s", remote_host); break; case E_Message_Remote_Port: dest += snprintf(dest, left, "%d", remote_port + comp->offset); break; case E_Message_Local_IP: dest += snprintf(dest, left, "%s", local_ip_w_brackets); break; case E_Message_Local_Port: int port; if((multisocket) && (sendMode != MODE_SERVER)) { port = call_port; } else { port = local_port; } dest += snprintf(dest, left, "%d", port + comp->offset); break; case E_Message_Transport: dest += snprintf(dest, left, "%s", TRANSPORT_TO_STRING(transport)); break; case E_Message_Local_IP_Type: dest += snprintf(dest, left, "%s", (local_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Server_IP: { /* We should do this conversion once per socket creation, rather than * repeating it every single time. */ struct sockaddr_storage server_sockaddr; sipp_socklen_t len = sizeof(server_sockaddr); getsockname(call_socket->ss_fd, (sockaddr *)(void *)&server_sockaddr, &len); char address[INET6_ADDRSTRLEN]; if (getnameinfo(_RCAST(sockaddr*, &server_sockaddr), len, address, sizeof(address), NULL, 0, NI_NUMERICHOST) < 0) { ERROR_NO("Unable to get socket name information"); } dest += snprintf(dest, left, "%s", address); } break; case E_Message_Media_IP: dest += snprintf(dest, left, "%s", media_ip); break; case E_Message_Media_Port: { int port = media_port + comp->offset; #ifdef PCAPPLAY char *begin = dest; while (begin > msg_buffer) { if (*begin == '\n') { break; } begin--; } if (begin == msg_buffer) { ERROR("Can not find beginning of a line for the media port!"); } play_args_t* play_args = NULL; if (strstr(begin, "audio")) { play_args = &play_args_a; } else if (strstr(begin, "image")) { play_args = &play_args_i; } else if (strstr(begin, "video")) { play_args = &play_args_v; } else { // This check will not do, as we use the media_port in other places too. //ERROR("media_port keyword with no audio or video on the current line (%s)", begin); } if (play_args != NULL) { if (media_ip_is_ipv6) { (_RCAST(struct sockaddr_in6 *, &(play_args->from)))->sin6_port = htons(port); } else { (_RCAST(struct sockaddr_in *, &(play_args->from)))->sin_port = htons(port); } } #endif dest += snprintf(dest, left, "%u", port); break; } case E_Message_RTPStream_Audio_Port: { int temp_audio_port = 0; // Only obtain port for RTP ([rtpstream_audio_port+0]) *BUT NOT* RTCP ([rtpstream_audio_port+1]) if (comp->offset == 0) { temp_audio_port = rtpstream_get_local_audioport(&rtpstream_callinfo); if (!temp_audio_port) { /* Make this a warning instead? */ ERROR("cannot assign a free audio port to this call - using 0 for [rtpstream_audio_port]"); } } else if (comp->offset >= 1) { temp_audio_port = rtpstream_callinfo.local_audioport + comp->offset; } #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): E_Message_RTPStream_Audio_Port: %d\n", temp_audio_port); #endif // USE_TLS dest += snprintf(dest, left, "%d", temp_audio_port); } break; case E_Message_RTPStream_Video_Port: { int temp_video_port = 0; // Only obtain port for RTP ([rtpstream_video_port+0]) *BUT NOT* RTCP ([rtpstream_video_port+1]) if (comp->offset == 0) { temp_video_port = rtpstream_get_local_videoport(&rtpstream_callinfo); if (!temp_video_port) { /* Make this a warning instead? */ ERROR("cannot assign a free video port to this call - using 0 for [rtpstream_video_port]"); } } else if (comp->offset >= 1) { temp_video_port = rtpstream_callinfo.local_videoport + comp->offset; } #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): E_Message_RTPStream_Video_Port: %d\n", temp_video_port); #endif // USE_TLS dest += snprintf(dest, left, "%d", temp_video_port); } break; #ifdef USE_TLS case E_Message_CryptoTag1Audio: { pA.audio_found = true; pA.primary_audio_cryptotag = 1; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag1Audio() - PRIMARY - CLIENT: %d\n", pA.primary_audio_cryptotag); _txUACAudio.setCryptoTag(pA.primary_audio_cryptotag, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag1Audio() - PRIMARY - SERVER: %d\n", pA.primary_audio_cryptotag); _txUASAudio.setCryptoTag(pA.primary_audio_cryptotag, PRIMARY_CRYPTO); } dest += snprintf(dest, left, "%d", pA.primary_audio_cryptotag); srtp_audio_updated = true; } break; case E_Message_CryptoTag2Audio: { pA.audio_found = true; pA.secondary_audio_cryptotag = 2; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag2Audio() - SECONDARY - CLIENT: %d\n", pA.secondary_audio_cryptotag); _txUACAudio.setCryptoTag(pA.secondary_audio_cryptotag, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag2Audio() - SECONDARY - SERVER: %d\n", pA.secondary_audio_cryptotag); _txUASAudio.setCryptoTag(pA.secondary_audio_cryptotag, SECONDARY_CRYPTO); } dest += snprintf(dest, left, "%d", pA.secondary_audio_cryptotag); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1801Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1801Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1801Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_audio_cs_out, "AES_CM_128_HMAC_SHA1_80", sizeof(_pref_audio_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACAudio.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_80", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACAudio.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASAudio.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_80", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASAudio.swapCrypto(); } } } pA.audio_found = true; strncat(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_80"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1802Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1802Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1802Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pA.audio_found = true; strncat(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_80"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1321Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1321Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1321Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_audio_cs_out, "AES_CM_128_HMAC_SHA1_32", sizeof(_pref_audio_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACAudio.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_32", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACAudio.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASAudio.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_32", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASAudio.swapCrypto(); } } } pA.audio_found = true; strncat(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_32"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1322Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1322Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1322Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pA.audio_found = true; strncat(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_32"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteNullSha1801Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1801Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1801Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_audio_cs_out, "NULL_HMAC_SHA1_80", sizeof(_pref_audio_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACAudio.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_80", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACAudio.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASAudio.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_80", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASAudio.swapCrypto(); } } } pA.audio_found = true; strncat(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_80", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_80"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteNullSha1802Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1802Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1802Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pA.audio_found = true; strncat(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_80", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_80"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteNullSha1321Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1321Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1321Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_audio_cs_out, "NULL_HMAC_SHA1_32", sizeof(_pref_audio_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACAudio.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_32", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACAudio.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASAudio.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_32", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASAudio.swapCrypto(); } } } pA.audio_found = true; strncat(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_32", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_32"); srtp_audio_updated = true; } break; case E_Message_CryptoSuiteNullSha1322Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1322Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1322Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pA.audio_found = true; strncat(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_32", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_32"); srtp_audio_updated = true; } break; case E_Message_CryptoKeyParams1Audio: { std::string mks; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - CLIENT - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUACAudio.generateMasterKey(PRIMARY_CRYPTO); _txUACAudio.generateMasterSalt(PRIMARY_CRYPTO); _txUACAudio.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - CLIENT - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUACAudio.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - CLIENT - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - SERVER - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUASAudio.generateMasterKey(PRIMARY_CRYPTO); _txUASAudio.generateMasterSalt(PRIMARY_CRYPTO); _txUASAudio.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - SERVER - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUASAudio.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Audio() - PRIMARY - SERVER - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } pA.audio_found = true; strncpy(pA.primary_audio_cryptokeyparams, mks.c_str(), 40); dest += snprintf(dest, left, "%s", pA.primary_audio_cryptokeyparams); srtp_audio_updated = true; } break; case E_Message_CryptoKeyParams2Audio: { std::string mks; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - CLIENT - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUACAudio.generateMasterKey(SECONDARY_CRYPTO); _txUACAudio.generateMasterSalt(SECONDARY_CRYPTO); _txUACAudio.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - CLIENT - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUACAudio.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - CLIENT - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - SERVER - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUASAudio.generateMasterKey(SECONDARY_CRYPTO); _txUASAudio.generateMasterSalt(SECONDARY_CRYPTO); _txUASAudio.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - SERVER - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUASAudio.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Audio() - SECONDARY - SERVER - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } pA.audio_found = true; strncpy(pA.secondary_audio_cryptokeyparams, mks.c_str(), 40); dest += snprintf(dest, left, "%s", pA.secondary_audio_cryptokeyparams); srtp_audio_updated = true; } break; case E_Message_UEAesCm128Sha1801Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1801Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1801Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } pA.audio_found = true; pA.primary_unencrypted_audio_srtp = true; strncat(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_audio_updated = true; } break; case E_Message_UEAesCm128Sha1802Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1802Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1802Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pA.audio_found = true; pA.secondary_unencrypted_audio_srtp = true; strncat(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_audio_updated = true; } break; case E_Message_UEAesCm128Sha1321Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1321Audio() - PRIMARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1321Audio() - PRIMARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } pA.audio_found = true; pA.primary_unencrypted_audio_srtp = true; strncat(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pA.primary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_audio_updated = true; } break; case E_Message_UEAesCm128Sha1322Audio: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1322Audio() - SECONDARY - CLIENT\n"); _txUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1322Audio() - SECONDARY - SERVER\n"); _txUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pA.audio_found = true; pA.secondary_unencrypted_audio_srtp = true; strncat(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pA.secondary_audio_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_audio_updated = true; } break; case E_Message_CryptoTag1Video: { pV.video_found = true; pV.primary_video_cryptotag = 1; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag1Video() - PRIMARY - CLIENT: %d\n", pV.primary_video_cryptotag); _txUACVideo.setCryptoTag(pV.primary_video_cryptotag, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag1Video() - PRIMARY - SERVER: %d\n", pV.primary_video_cryptotag); _txUASVideo.setCryptoTag(pV.primary_video_cryptotag, PRIMARY_CRYPTO); } dest += snprintf(dest, left, "%d", pV.primary_video_cryptotag); srtp_video_updated = true; } break; case E_Message_CryptoTag2Video: { pV.video_found = true; pV.secondary_video_cryptotag = 2; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag2Video() - SECONDARY - CLIENT: %d\n", pV.secondary_video_cryptotag); _txUACVideo.setCryptoTag(pV.secondary_video_cryptotag, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoTag2Video() - SECONDARY - SERVER: %d\n", pV.secondary_video_cryptotag); _txUASVideo.setCryptoTag(pV.secondary_video_cryptotag, SECONDARY_CRYPTO); } dest += snprintf(dest, left, "%d", pV.secondary_video_cryptotag); srtp_video_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1801Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1801Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1801Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_video_cs_out, "AES_CM_128_HMAC_SHA1_80", sizeof(_pref_video_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACVideo.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_80", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACVideo.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASVideo.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_80", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASVideo.swapCrypto(); } } } pV.video_found = true; strncat(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_80"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1802Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1802Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1802Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pV.video_found = true; strncat(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_80"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1321Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1321Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1321Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_video_cs_out, "AES_CM_128_HMAC_SHA1_32", sizeof(_pref_video_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACVideo.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_32", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACVideo.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASVideo.getCryptoSuite().c_str(), "AES_CM_128_HMAC_SHA1_32", 23)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASVideo.swapCrypto(); } } } pV.video_found = true; strncat(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_32"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteAesCm128Sha1322Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1322Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteAesCm128Sha1322Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pV.video_found = true; strncat(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "AES_CM_128_HMAC_SHA1_32"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteNullSha1801Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1801Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1801Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_video_cs_out, "NULL_HMAC_SHA1_80", sizeof(_pref_video_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACVideo.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_80", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACVideo.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASVideo.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_80", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASVideo.swapCrypto(); } } } pV.video_found = true; strncat(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_80", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_80"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteNullSha1802Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1802Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1802Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pV.video_found = true; strncat(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_80", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_80"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteNullSha1321Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1321Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1321Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } if ((getSessionStateCurrent() == eNoSession) || (getSessionStateCurrent() == eCompleted)) { logSrtpInfo("call::createSendingMessage(): Marking preferred OFFER cryptosuite...\n"); strncat(_pref_video_cs_out, "NULL_HMAC_SHA1_32", sizeof(_pref_video_cs_out) - 1); } else if (getSessionStateCurrent() == eOfferReceived) { if (sendMode == MODE_CLIENT) { if (!strncmp(_rxUACVideo.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_32", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- CLIENT -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- CLIENT -- SWAPPING...\n"); _rxUACVideo.swapCrypto(); } } else if (sendMode == MODE_SERVER) { if (!strncmp(_rxUASVideo.getCryptoSuite().c_str(), "NULL_HMAC_SHA1_32", 17)) { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite match -- SERVER -- NO-OP...\n"); } else { logSrtpInfo("call::createSendingMessage(): Preferred ANSWER cryptosuite mismatch -- SERVER -- SWAPPING...\n"); _rxUASVideo.swapCrypto(); } } } pV.video_found = true; strncat(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_32", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_32"); srtp_video_updated = true; } break; case E_Message_CryptoSuiteNullSha1322Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1322Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoSuiteNullSha1322Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pV.video_found = true; strncat(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_32", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "NULL_HMAC_SHA1_32"); srtp_video_updated = true; } break; case E_Message_CryptoKeyParams1Video: { std::string mks; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - CLIENT - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUACVideo.generateMasterKey(PRIMARY_CRYPTO); _txUACVideo.generateMasterSalt(PRIMARY_CRYPTO); _txUACVideo.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - CLIENT - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUACVideo.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - CLIENT - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - SERVER - component offset:\n", comp->offset); if (comp->offset >= 0) { _txUASVideo.generateMasterKey(PRIMARY_CRYPTO); _txUASVideo.generateMasterSalt(PRIMARY_CRYPTO); _txUASVideo.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - SERVER - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUASVideo.encodeMasterKeySalt(mks, PRIMARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams1Video() - PRIMARY - SERVER - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } pV.video_found = true; strncpy(pV.primary_video_cryptokeyparams, mks.c_str(), 40); dest += snprintf(dest, left, "%s", pV.primary_video_cryptokeyparams); srtp_video_updated = true; } break; case E_Message_CryptoKeyParams2Video: { std::string mks; if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - CLIENT - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUACVideo.generateMasterKey(SECONDARY_CRYPTO); _txUACVideo.generateMasterSalt(SECONDARY_CRYPTO); _txUACVideo.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - CLIENT - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUACVideo.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - CLIENT - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - SERVER - component offset:%d\n", comp->offset); if (comp->offset >= 0) { _txUASVideo.generateMasterKey(SECONDARY_CRYPTO); _txUASVideo.generateMasterSalt(SECONDARY_CRYPTO); _txUASVideo.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - SERVER - generating new concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } else { _txUASVideo.encodeMasterKeySalt(mks, SECONDARY_CRYPTO); logSrtpInfo("call::createSendingMessage(): E_Message_CryptoKeyParams2Video() - SECONDARY - SERVER - reusing old concatenated base64-encoded master key/salt:%s\n", mks.c_str()); } } pV.video_found = true; strncpy(pV.secondary_video_cryptokeyparams, mks.c_str(), 40); dest += snprintf(dest, left, "%s", pV.secondary_video_cryptokeyparams); srtp_video_updated = true; } break; case E_Message_UEAesCm128Sha1801Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1801Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1801Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } pV.video_found = true; pV.primary_unencrypted_video_srtp = true; strncat(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_video_updated = true; } break; case E_Message_UEAesCm128Sha1802Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1802Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1802Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } pV.video_found = true; pV.secondary_unencrypted_video_srtp = true; strncat(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_video_updated = true; } break; case E_Message_UEAesCm128Sha1321Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1321Video() - PRIMARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1321Video() - PRIMARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } pV.video_found = true; pV.primary_unencrypted_video_srtp = true; strncat(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pV.primary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_video_updated = true; } break; case E_Message_UEAesCm128Sha1322Video: { if (sendMode == MODE_CLIENT) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1322Video() - SECONDARY - CLIENT\n"); _txUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (sendMode == MODE_SERVER) { logSrtpInfo("call::createSendingMessage(): E_Message_UEAesCm128Sha1322Video() - SECONDARY - SERVER\n"); _txUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to encrypt */ _txUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } pV.video_found = true; pV.secondary_unencrypted_video_srtp = true; strncat(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32", sizeof(pV.secondary_video_cryptosuite) - 1); dest += snprintf(dest, left, "%s", "UNENCRYPTED_SRTP"); srtp_video_updated = true; } break; #endif // USE_TLS case E_Message_Media_IP_Type: dest += snprintf(dest, left, "%s", (media_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Call_Number: dest += snprintf(dest, left, "%u", number); break; case E_Message_DynamicId: dest += snprintf(dest, left, "%u", call::dynamicId); // increment at each request dynamicId += stepDynamicId; if ( this->dynamicId > maxDynamicId ) { call::dynamicId = call::startDynamicId; } ; break; case E_Message_Call_ID: dest += snprintf(dest, left, "%s", id); break; case E_Message_CSEQ: dest += snprintf(dest, left, "%u", cseq + comp->offset); break; case E_Message_PID: dest += snprintf(dest, left, "%d", pid); break; case E_Message_Service: dest += snprintf(dest, left, "%s", service); break; case E_Message_Branch: /* Branch is magic cookie + call number + message index in scenario */ if (P_index == -1) { dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, msg_index - 1 + comp->offset); } else { dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, P_index + comp->offset); } break; case E_Message_Index: dest += snprintf(dest, left, "%d", P_index); break; case E_Message_Next_Url: if (next_req_url) { dest += sprintf(dest, "%s", next_req_url); } break; case E_Message_Len: length_marker = dest; dest += snprintf(dest, left, " "); len_offset = comp->offset; break; case E_Message_Authentication: if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!"); } auth_marker = dest; dest += snprintf(dest, left, "[authentication place holder]"); auth_comp = comp; break; case E_Message_Peer_Tag_Param: if (peer_tag) { dest += snprintf(dest, left, ";tag=%s", peer_tag); } break; case E_Message_Routes: if (dialog_route_set) { dest += sprintf(dest, "Route: %s", dialog_route_set); } else if (*(dest - 1) == '\n') { suppresscrlf = true; } break; case E_Message_ClockTick: dest += snprintf(dest, left, "%lu", clock_tick); break; case E_Message_Timestamp: struct timeval currentTime; gettimeofday(¤tTime, NULL); dest += snprintf(dest, left, "%s", CStat::formatTime(¤tTime)); break; case E_Message_Date: char buf[256]; time_t t; struct tm *tm; t = time(NULL); tm = gmtime(&t); /* changed %Z to hardcoded GMT since in some OS like FreeBSD it could return UTC instead, see issue #535 */ strftime(buf, 256, "%a, %d %b %Y %T GMT", tm); dest += snprintf(dest, left, "%s", buf); break; case E_Message_Users: dest += snprintf(dest, left, "%d", users); break; case E_Message_UserID: dest += snprintf(dest, left, "%d", userId); break; case E_Message_SippVersion: /* Drop the initial "v" from the SIPP_VERSION string for legacy reasons. */ dest += snprintf(dest, left, "%s", (const char*)SIPP_VERSION + 1); break; case E_Message_Variable: { int varId = comp->varId; CCallVariable *var = M_callVariableTable->getVar(varId); if(var->isSet()) { if (var->isRegExp()) { dest += sprintf(dest, "%s", var->getMatchingValue()); } else if (var->isDouble()) { dest += sprintf(dest, "%lf", var->getDouble()); } else if (var->isString()) { dest += sprintf(dest, "%s", var->getString()); } else if (var->isBool()) { dest += sprintf(dest, "true"); } } else if (var->isBool()) { dest += sprintf(dest, "false"); } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Fill: { int varId = comp->varId; int length = (int) M_callVariableTable->getVar(varId)->getDouble(); if (length < 0) { length = 0; } char *filltext = comp->literal; int filllen = strlen(filltext); if (filllen == 0) { ERROR("Internal error: [fill] keyword has zero-length text."); } for (int i = 0, j = 0; i < length; i++, j++) { *dest++ = filltext[j % filllen]; } *dest = '\0'; break; } case E_Message_File: { char buffer[MAX_HEADER_LEN]; createSendingMessage(comp->comp_param.filename, SM_UNUSED, buffer, sizeof(buffer)); FILE *f = fopen(buffer, "r"); if (!f) { ERROR("Could not open '%s': %s", buffer, strerror(errno)); } int ret; while ((ret = fread(dest, 1, left, f)) > 0) { left -= ret; dest += ret; } if (ret < 0) { ERROR("Error reading '%s': %s", buffer, strerror(errno)); } fclose(f); break; } case E_Message_Injection: { char *orig_dest = dest; getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest); /* We are injecting an authentication line. */ if (char *tmp = strstr(orig_dest, "[authentication")) { if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!"); } auth_marker = tmp; auth_comp = (struct MessageComponent *)calloc(1, sizeof(struct MessageComponent)); if (!auth_comp) { ERROR("Out of memory!"); } auth_comp_allocated = true; tmp = strchr(auth_marker, ']'); char c = *tmp; *tmp = '\0'; SendingMessage::parseAuthenticationKeyword(call_scenario, auth_comp, auth_marker); *tmp = c; } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Last_Header: { char * last_header = get_last_header(comp->literal); if(last_header) { dest += sprintf(dest, "%s", last_header); } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Custom: { dest += comp->comp_param.fxn(this, comp, dest, left); break; } case E_Message_Last_Message: if(last_recv_msg && strlen(last_recv_msg)) { dest += sprintf(dest, "%s", last_recv_msg); } break; case E_Message_Last_Request_URI: { char * last_request_uri = get_last_request_uri(); dest += sprintf(dest, "%s", last_request_uri); free(last_request_uri); break; } case E_Message_Last_CSeq_Number: { int last_cseq = 0; char *last_header = get_last_header("CSeq:"); if(last_header) { last_header += 5; /* Extract the integer value of the field */ while(isspace(*last_header)) last_header++; sscanf(last_header, "%d", &last_cseq); } dest += sprintf(dest, "%d", last_cseq + comp->offset); break; } case E_Message_TDM_Map: if (!use_tdmmap) ERROR("[tdmmap] keyword without -tdmmap parameter on command line"); dest += snprintf(dest, left, "%d.%d.%d/%d", tdm_map_x+(int((tdm_map_number)/((tdm_map_b+1)*(tdm_map_c+1))))%(tdm_map_a+1), tdm_map_h, tdm_map_y+(int((tdm_map_number)/(tdm_map_c+1)))%(tdm_map_b+1), tdm_map_z+(tdm_map_number)%(tdm_map_c+1) ); break; } } /* Need the body for length and auth-int calculation */ char *body = NULL; const char *auth_body = NULL; if (length_marker || auth_marker) { body = strstr(msg_buffer, "\r\n\r\n"); if (body) { auth_body = body; auth_body += strlen("\r\n\r\n"); } } if (!auth_body) { auth_body = ""; } /* Fix up the length. */ if (length_marker) { if (auth_marker > body) { ERROR("The authentication keyword should appear in the message header, not the body!"); } if (body && dest - body > 4 && dest - body < 100004) { char tmp = length_marker[5]; sprintf(length_marker, "%5u", (unsigned)(dest - body - 4 + len_offset)); length_marker[5] = tmp; } else { // Other cases: Content-Length is 0 sprintf(length_marker, " 0\r\n\r\n"); } } if (msgLen) { *msgLen = dest - msg_buffer; } /* * The authentication substitution must be done outside the above * loop because auth-int will use the body (which must have already * been keyword substituted) to build the md5 hash */ if (auth_marker) { if (!dialog_authentication) { ERROR("Authentication keyword without dialog_authentication!"); } int auth_marker_len; int authlen; auth_marker_len = (strchr(auth_marker, ']') + 1) - auth_marker; /* Determine the type of credentials. */ char result[MAX_HEADER_LEN]; if (dialog_challenge_type == 401) { /* Registrars use Authorization */ authlen = sprintf(result, "Authorization: "); } else { /* Proxies use Proxy-Authorization */ authlen = sprintf(result, "Proxy-Authorization: "); } /* Build the auth credenticals */ char uri[MAX_HEADER_LEN]; sprintf (uri, "%s:%d", remote_ip, remote_port); /* These cause this function to not be reentrant. */ static char my_auth_user[MAX_HEADER_LEN + 2]; static char my_auth_pass[MAX_HEADER_LEN + 2]; static char my_aka_OP[MAX_HEADER_LEN + 2]; static char my_aka_AMF[MAX_HEADER_LEN + 2]; static char my_aka_K[MAX_HEADER_LEN + 2]; createSendingMessage(auth_comp->comp_param.auth_param.auth_user, SM_UNUSED, my_auth_user, sizeof(my_auth_user)); createSendingMessage(auth_comp->comp_param.auth_param.auth_pass, SM_UNUSED, my_auth_pass, sizeof(my_auth_pass)); createSendingMessage(auth_comp->comp_param.auth_param.aka_K, SM_UNUSED, my_aka_K, sizeof(my_aka_K)); createSendingMessage(auth_comp->comp_param.auth_param.aka_AMF, SM_UNUSED, my_aka_AMF, sizeof(my_aka_AMF)); createSendingMessage(auth_comp->comp_param.auth_param.aka_OP, SM_UNUSED, my_aka_OP, sizeof(my_aka_OP)); if (createAuthHeader( my_auth_user, my_auth_pass, src->getMethod(), uri, auth_body, dialog_authentication, my_aka_OP, my_aka_AMF, my_aka_K, next_nonce_count++, result + authlen, MAX_HEADER_LEN - authlen) == 0) { ERROR("%s", result + authlen); } authlen = strlen(result); /* Shift the end of the message to its rightful place. */ memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1); /* Copy our result into the hole. */ memcpy(auth_marker, result, authlen); if (msgLen) { *msgLen += (authlen - auth_marker_len); } } if (auth_comp_allocated) { SendingMessage::freeMessageComponent(auth_comp); } #ifdef USE_TLS // PASS OUTGOING SRTP PARAMETERS... if (srtp_audio_updated && (pA.primary_audio_cryptotag != 0)) { rtpstream_set_srtp_audio_local(&rtpstream_callinfo, pA); if (sendMode == MODE_CLIENT) { // // RX-UAC-AUDIO SRTP context (b) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUACA; rxUACA.ssrc = rtpstream_callinfo.taskinfo->audio_ssrc_id; rxUACA.address = media_ip; rxUACA.port = rtpstream_callinfo.local_audioport; logSrtpInfo("call::createSendingMessage(): (b) RX-UAC-AUDIO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUACA.ssrc, rxUACA.address.c_str(), rxUACA.port); _rxUACAudio.setID(rxUACA); } } if (srtp_video_updated && (pV.primary_video_cryptotag != 0)) { rtpstream_set_srtp_video_local(&rtpstream_callinfo, pV); if (sendMode == MODE_CLIENT) { // // RX-UAC-VIDEO SRTP context (b) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUACV; rxUACV.ssrc = rtpstream_callinfo.taskinfo->video_ssrc_id; rxUACV.address = media_ip; rxUACV.port = rtpstream_callinfo.local_videoport; logSrtpInfo("call::createSendingMessage(): (b) RX-UAC-VIDEO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUACV.ssrc, rxUACV.address.c_str(), rxUACV.port); _rxUACVideo.setID(rxUACV); } } #endif // USE_TLS if (body && !strcmp(get_header_content(msg_buffer, (char*)"Content-Type:"), "application/sdp")) { if (getSessionStateCurrent() == eNoSession) { #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): Switching session state: eNoSession --> eOfferSent\n"); #endif // USE_TLS setSessionState(eOfferSent); } else if (getSessionStateCurrent() == eCompleted) { #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): Switching session state: eCompleted --> eOfferSent\n"); #endif // USE_TLS setSessionState(eOfferSent); } else if (getSessionStateCurrent() == eOfferReceived) { #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): Switching session state: eOfferReceived --> eAnswerSent\n"); #endif // USE_TLS setSessionState(eAnswerSent); #ifdef USE_TLS logSrtpInfo("call::createSendingMessage(): Switching session state: eAnswerSent --> eCompleted\n"); #endif // USE_TLS setSessionState(eCompleted); } } return msg_buffer; } bool call::process_twinSippCom(char * msg) { int search_index; bool found = false; T_ActionResult actionResult; callDebug("Processing incoming command for call-ID %s:\n%s\n\n", id, msg); setRunning(); if (checkInternalCmd(msg) == false) { for(search_index = msg_index; search_index < (int)call_scenario->messages.size(); search_index++) { if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) { if ((call_scenario->messages[search_index] -> optional) || (call_scenario->messages[search_index] -> M_type == MSG_TYPE_NOP)) { continue; } /* The received message is different from the expected one */ TRACE_MSG("Unexpected control message received (I was expecting a different type of message):\n%s\n", msg); callDebug("Unexpected control message received (I was expecting a different type of message):\n%s\n\n", msg); return rejectCall(); } else { if(extendedTwinSippMode) { // 3pcc extended mode if(check_peer_src(msg, search_index)) { found = true; break; } else { WARNING("Unexpected sender for the received peer message\n%s\n", msg); return rejectCall(); } } else { found = true; break; } } } if (found) { call_scenario->messages[search_index]->M_nbCmdRecv ++; do_bookkeeping(call_scenario->messages[search_index]); // variable treatment // Remove \r, \n at the end of a received command // (necessary for transport, to be removed for usage) while ( (msg[strlen(msg)-1] == '\n') && (msg[strlen(msg)-2] == '\r') ) { msg[strlen(msg)-2] = 0; } actionResult = executeAction(msg, call_scenario->messages[search_index]); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } else if (actionResult == E_AR_CONNECT_FAILED) { terminate(CStat::E_FAILED_TCP_CONNECT); return false; } } } else { TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg); callDebug("Unexpected control message received (no such message found):\n%s\n\n", msg); return rejectCall(); } msg_index = search_index; //update the state machine return(next()); } else { return (false); } } bool call::checkInternalCmd(char * cmd) { char * L_ptr1, * L_ptr2, L_backup; L_ptr1 = strstr(cmd, "internal-cmd:"); if (!L_ptr1) { return (false); } L_ptr1 += 13 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) { return (false); } L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, "abort_call") == 0) { *L_ptr2 = L_backup; computeStat(CStat::E_CALL_FAILED); abortCall(true); return (true); } *L_ptr2 = L_backup; return (false); } bool call::check_peer_src(char * msg, int search_index) { char * L_ptr1, * L_ptr2, L_backup ; L_ptr1 = strstr(msg, "From:"); if (!L_ptr1) { return (false); } L_ptr1 += 5 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) { return (false); } L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, call_scenario->messages[search_index] -> peer_src) == 0) { *L_ptr2 = L_backup; return(true); } *L_ptr2 = L_backup; return (false); } void call::extract_cseq_method(char* method, const char* msg) { const char* cseq; if ((cseq = strstr (msg, "CSeq"))) { const char* value; if ((value = strchr(cseq, ':'))) { value++; while (isspace(*value)) value++; // ignore any white spaces after the : while (!isspace(*value)) value++; // ignore the CSEQ number while (isspace(*value)) value++; // ignore spaces after CSEQ number const char* end = value; int nbytes = 0; /* A '\r' terminates the line, so we want to catch that too. */ while ((*end != '\r') && (*end != '\n')) { end++; nbytes++; } if (nbytes > 0) strncpy (method, value, nbytes); method[nbytes] = '\0'; } } } void call::extract_transaction(char* txn, const char* msg) { char *via = get_header_content(msg, "via:"); if (!via) { txn[0] = '\0'; return; } char *branch = strstr(via, ";branch="); if (!branch) { txn[0] = '\0'; return; } branch += strlen(";branch="); while (*branch && *branch != ';' && *branch != ',' && !isspace(*branch)) { *txn++ = *branch++; } *txn = '\0'; } void call::formatNextReqUrl(const char* contact) { /* clean up the next_req_url */ while (*contact != '\0' && (*contact == ' ' || *contact == '\t')) { ++contact; } const char* start = strchr(contact, '<'); const char* end = strchr(contact, '>'); if ((start && end) && (start < end)) { contact = start; contact++; next_req_url[0] = '\0'; strncat(next_req_url, contact, min(MAX_HEADER_LEN - 1, (int)(end - contact))); /* fits MAX_HEADER_LEN */ } else { next_req_url[0] = '\0'; strncat(next_req_url, contact, MAX_HEADER_LEN - 1); } } void call::computeRouteSetAndRemoteTargetUri(const char* rr, const char* contact, bool bRequestIncoming) { if (!*contact) { WARNING("Cannot record route set if there is no Contact"); return; } if (!*rr) { /* There are no RR headers. Simply set up the contact as our * target uri. Note that this is only called if there was no * dialog_route_set at the moment. And in either case, we * wouldn't want to clear the dialog_route_set because changing * RR mid-dialog is not allowed. */ formatNextReqUrl(contact); return; } std::vector headers = split(rr, ','); std::vector::iterator it; std::vector::iterator end; int direction; if (bRequestIncoming) { it = headers.begin(); end = headers.end(); direction = 1; } else { it = headers.end() - 1; end = headers.begin() - 1; direction = -1; } std::vector routes; std::string targetUri; bool first = true; for (; it != end; it += direction) { const std::string& header = *it; if (first && header.find(";lr") == std::string::npos) { /* If the next hop is a static router, set target URI to * that router. We'll push the original contact onto the end * of the route set. We won't need to record this route, * because we've set the target to it. */ targetUri = header; } else { first = false; routes.push_back(trim(header)); } } /* If target URI is set, the first hop is a strict router. Add the * Contact as tailing route. */ if (targetUri.length()) { routes.push_back(trim(contact)); } else { targetUri = contact; } if (routes.size()) { dialog_route_set = strdup(join(routes, ", ").c_str()); } formatNextReqUrl(targetUri.c_str()); } bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn) { message *curmsg = call_scenario->messages[index]; if ((curmsg->recv_request)) { if (curmsg->regexp_match) { if (curmsg->regexp_compile == NULL) { regex_t *re = new regex_t; /* No regex match position needed (NOSUB), we're simply * looking for the * regex. */ if (regcomp(re, curmsg->recv_request, REGCOMP_PARAMS|REG_NOSUB)) { ERROR("Invalid regular expression for index %d: %s", index, curmsg->recv_request); } curmsg->regexp_compile = re; } return !regexec(curmsg->regexp_compile, request, (size_t)0, NULL, REGEXEC_PARAMS); } else { return !strcmp(curmsg->recv_request, request); } } else if (curmsg->recv_response && (curmsg->recv_response == reply_code)) { /* This is a potential candidate, we need to match transactions. */ if (curmsg->response_txn) { if (transactions[curmsg->response_txn - 1].txnID && !strcmp(transactions[curmsg->response_txn - 1].txnID, txn)) { return true; } else { return false; } } else if (index == 0) { /* Always true for the first message. */ return true; } else if (curmsg->recv_response_for_cseq_method_list && strstr(curmsg->recv_response_for_cseq_method_list, responsecseqmethod)) { /* If we do not have a transaction defined, we just check the CSEQ method. */ return true; } else { return false; } } return false; } void call::queue_up(const char* msg) { free(queued_msg); queued_msg = strdup(msg); } bool call::process_incoming(const char* msg, const struct sockaddr_storage* src) { int reply_code = 0; static char request[65]; char responsecseqmethod[65]; char txn[MAX_HEADER_LEN]; unsigned long cookie = 0; const char* ptr; int search_index; bool found = false; T_ActionResult actionResult; unsigned long int invite_cseq = 0; update_clock_tick(); callDebug("Processing %zu byte incoming message for call-ID %s (hash %lu):\n%s\n\n", strlen(msg), id, hash(msg), msg); setRunning(); message *curmsg = call_scenario->messages[msg_index]; /* Ignore the messages received during a pause if -pause_msg_ign is set */ if (curmsg->M_type == MSG_TYPE_PAUSE && pause_msg_ign) { return true; } /* Get our destination if we have none. */ if (call_peer.ss_family == AF_UNSPEC && src) { memcpy(&call_peer, src, sizeof(call_peer)); } /* Authorize nop as a first command, even in server mode */ if (msg_index == 0 && curmsg->M_type == MSG_TYPE_NOP) { queue_up(msg); paused_until = 0; return run(); } responsecseqmethod[0] = '\0'; txn[0] = '\0'; if (!checkAckCSeq(msg)) { WARNING("ACK CSeq value does NOT match value of related INVITE CSeq -- aborting call\n"); computeStat(CStat::E_CALL_FAILED); delete this; return false; } /* Check that we have a To:-header */ if (!get_header(msg, "To:", false)[0] && !process_unexpected(msg)) { return false; } if ((transport == T_UDP) && (retrans_enabled)) { /* Detects retransmissions from peer and retransmit the * message which was sent just after this one was received */ cookie = hash(msg); if((recv_retrans_recv_index >= 0) && (recv_retrans_hash == cookie)) { int status; if(lost(recv_retrans_recv_index)) { TRACE_MSG("%s message (retrans) lost (recv).", TRANSPORT_TO_STRING(transport)); callDebug("%s message (retrans) lost (recv) (hash %lu)\n", TRANSPORT_TO_STRING(transport), hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[recv_retrans_recv_index] -> nb_lost++; return true; } call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++; send_scene(recv_retrans_send_index, &status, NULL); if(status >= 0) { call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++; computeStat(CStat::E_RETRANSMISSION); } else if(status < 0) { return false; } return true; } if((last_recv_index >= 0) && (last_recv_hash == cookie)) { /* This one has already been received, but not processed * yet => (has not triggered something yet) so we can discard. * * This case appears when the UAS has send a 200 but not received * a ACK yet. Thus, the UAS retransmit the 200 (invite transaction) * until it receives a ACK. In this case, it nevers sends the 200 * from the BYE, until it has reveiced the previous 200. Thus, * the UAC retransmit the BYE, and this BYE is considered as an * unexpected. * * This case can also appear in case of message duplication by * the network. This should not be considered as an unexpected. */ call_scenario->messages[last_recv_index]->nb_recv_retrans++; return true; } } /* Check if message has a SDP in it; and extract media information. */ if (!strcmp(get_header_content(msg, "Content-Type:"), "application/sdp") && (hasMedia == 1) && (!curmsg->ignoresdp)) { const char* ptr = 0; int ip_ver = 0; int audio_port = 0; int video_port = 0; std::string host; #ifdef USE_TLS int audio_answer_ciphersuite_match = -1; int video_answer_ciphersuite_match = -1; #endif // USE_TLS ptr = get_header_content(msg, "Content-Length:"); if (ptr && atoll(ptr) > 0) { if (getSessionStateCurrent() == eNoSession) { #ifdef USE_TLS logSrtpInfo("call::process_incoming(): Switching session state: eNoSession --> eOfferReceived\n"); #endif // USE_TLS setSessionState(eOfferReceived); } else if (getSessionStateCurrent() == eCompleted) { #ifdef USE_TLS logSrtpInfo("call::process_incoming(): Switching session state: eCompleted --> eOfferReceived\n"); #endif // USE_TLS setSessionState(eOfferReceived); } else if (getSessionStateCurrent() == eOfferSent) { #ifdef USE_TLS logSrtpInfo("call::process_incoming(): Switching session state: eOfferSent --> eAnswerReceived\n"); #endif // USE_TLS setSessionState(eAnswerReceived); #ifdef USE_TLS logSrtpInfo("call::process_incoming(); Switching session state: eAnswerReceived --> eCompleted\n"); #endif // USE_TLS setSessionState(eCompleted); } #ifdef USE_TLS // INCOMING SRTP PARAM CONTEXT SrtpAudioInfoParams pA; SrtpVideoInfoParams pV; pA.audio_found = false; pA.primary_audio_cryptotag = 0; memset(pA.primary_audio_cryptosuite, 0, sizeof(pA.primary_audio_cryptosuite)); memset(pA.primary_audio_cryptokeyparams, 0, sizeof(pA.primary_audio_cryptokeyparams)); pA.secondary_audio_cryptotag = 0; memset(pA.secondary_audio_cryptosuite, 0, sizeof(pA.secondary_audio_cryptosuite)); memset(pA.secondary_audio_cryptokeyparams, 0, sizeof(pA.secondary_audio_cryptokeyparams)); pA.primary_unencrypted_audio_srtp = false; pA.secondary_unencrypted_audio_srtp = false; pV.video_found = false; pV.primary_video_cryptotag = 0; memset(pV.primary_video_cryptosuite, 0, sizeof(pV.primary_video_cryptosuite)); memset(pV.primary_video_cryptokeyparams, 0, sizeof(pV.primary_video_cryptokeyparams)); pV.secondary_video_cryptotag = 0; memset(pV.secondary_video_cryptosuite, 0, sizeof(pV.secondary_video_cryptosuite)); memset(pV.secondary_video_cryptokeyparams, 0, sizeof(pV.secondary_video_cryptokeyparams)); pV.primary_unencrypted_video_srtp = false; pV.secondary_unencrypted_video_srtp = false; #endif // USE_TLS host = extract_rtp_remote_addr(msg, ip_ver, audio_port, video_port); #ifdef USE_TLS extract_srtp_remote_info(msg, pA, pV); #endif // USE_TLS if ((audio_port==0) && (video_port==0)) { WARNING("extract_rtp_remote_addr: no m=audio or m=video or m=image line found in SDP message body"); } else { rtpstream_set_remote(&rtpstream_callinfo, ip_ver, host.c_str(), audio_port, video_port); } #ifdef USE_TLS // PASS INCOMING SRTP PARAMETERS... if (pA.audio_found && (pA.primary_audio_cryptotag != 0)) { // // INCOMING OFFER -- PERFORM PRIMARY/SECONDARY AUDIO SWAPS IF NEEDED/APPLICABLE // if ((getSessionStateCurrent() == eOfferReceived) && ((getSessionStateOld() == eNoSession || getSessionStateOld() == eCompleted))) { // NO-OP... } // // INCOMING ANSWER -- PERFORM PRIMARY/SECONDARY AUDIO SWAPS IF NEEDED/APPLICABLE // else if ((getSessionStateCurrent() == eCompleted) && (getSessionStateOld() == eAnswerReceived)) { audio_answer_ciphersuite_match = check_audio_ciphersuite_match(pA); } rtpstream_set_srtp_audio_remote(&rtpstream_callinfo, pA); if (sendMode == MODE_CLIENT) { // // TX-UAC-AUDIO SRTP context (a) -- SSRC/IPADDRESS/PORT // CryptoContextID txUACA; txUACA.ssrc = rtpstream_callinfo.taskinfo->audio_ssrc_id; txUACA.address = host; txUACA.port = audio_port; logSrtpInfo("call::process_incoming(): (a) TX-UAC-AUDIO SRTP context - ssrc:0x%08x address:%s port:%d\n", txUACA.ssrc, txUACA.address.c_str(), txUACA.port); _txUACAudio.setID(txUACA); if (audio_answer_ciphersuite_match == 0) { logSrtpInfo("call::process_incoming(): (a) TX-UAC_AUDIO SRTP context -- CLIENT -- CIPHERSUITE SWAP...\n"); _txUACAudio.swapCrypto(); } // // RX-UAC-AUDIO SRTP context (b) -- MASTER KEY/SALT PARSE + CRYPTO TAG + CRYPTOSUITE // std::string mks1; mks1 = pA.primary_audio_cryptokeyparams; if (!mks1.empty()) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- primary master key/salt: %s\n", mks1.c_str()); logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- primary crypto tag: %d\n", pA.primary_audio_cryptotag); _rxUACAudio.decodeMasterKeySalt(mks1, PRIMARY_CRYPTO); _rxUACAudio.setCryptoTag(pA.primary_audio_cryptotag, PRIMARY_CRYPTO); if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_80") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_32") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } } std::string mks2; mks2 = pA.secondary_audio_cryptokeyparams; if (!mks2.empty()) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- secondary master key/salt: %s\n", mks2.c_str()); logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- secondary crypto tag: %d\n", pA.secondary_audio_cryptotag); _rxUACAudio.decodeMasterKeySalt(mks2, SECONDARY_CRYPTO); _rxUACAudio.setCryptoTag(pA.secondary_audio_cryptotag, SECONDARY_CRYPTO); if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_80") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_32") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-AUDIO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUACAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } } } if (sendMode == MODE_SERVER) { // // TX-UAS-AUDIO SRTP context (d) -- SSRC/IPADDRESS/PORT // CryptoContextID txUASA; txUASA.ssrc = rtpstream_callinfo.taskinfo->audio_ssrc_id; txUASA.address = host; txUASA.port = audio_port; logSrtpInfo("call::process_incoming(): (d) TX-UAS-AUDIO SRTP context - ssrc:0x%08x address:%s port:%d\n", txUASA.ssrc, txUASA.address.c_str(), txUASA.port); _txUASAudio.setID(txUASA); if (audio_answer_ciphersuite_match == 0) { logSrtpInfo("call::process_incoming(): (d) TX-UAS_AUDIO SRTP context -- SERVER -- CIPHERSUITE SWAP...\n"); _txUASAudio.swapCrypto(); } // // RX-UAS-AUDIO SRTP context (c) -- MASTER KEY/SALT PARSE + CRYPTO TAG + CRYPTOSUITE // std::string mks1; mks1 = pA.primary_audio_cryptokeyparams; if (!mks1.empty()) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- primary master key/salt: %s\n", mks1.c_str()); logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- primary crypto tag: %d\n", pA.primary_audio_cryptotag); _rxUASAudio.decodeMasterKeySalt(mks1, PRIMARY_CRYPTO); _rxUASAudio.setCryptoTag(pA.primary_audio_cryptotag, PRIMARY_CRYPTO); if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_80") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "NULL_HMAC_SHA1_32") && !pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pA.primary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pA.primary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pA.primary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } } std::string mks2; mks2 = pA.secondary_audio_cryptokeyparams; if (!mks2.empty()) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- secondary master key/salt: %s\n", mks2.c_str()); logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- secondary crypto tag: %d\n", pA.secondary_audio_cryptotag); _rxUASAudio.decodeMasterKeySalt(mks2, SECONDARY_CRYPTO); _rxUASAudio.setCryptoTag(pA.secondary_audio_cryptotag, SECONDARY_CRYPTO); if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_80") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "NULL_HMAC_SHA1_32") && !pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pA.secondary_audio_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pA.secondary_unencrypted_audio_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-AUDIO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pA.secondary_audio_cryptosuite); _rxUASAudio.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASAudio.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } } } } if (pV.video_found && (pV.primary_video_cryptotag != 0)) { // // INCOMING OFFER -- PERFORM PRIMARY/SECONDARY VIDEO SWAPS IF NEEDED/APPLICABLE // if ((getSessionStateCurrent() == eOfferReceived) && ((getSessionStateOld() == eNoSession || getSessionStateOld() == eCompleted))) { // NO-OP... } // // INCOMING ANSWER -- PERFORM PRIMARY/SECONDARY VIDEO SWAPS IF NEEDED/APPLICABLE // else if ((getSessionStateCurrent() == eCompleted) && (getSessionStateOld() == eAnswerReceived)) { video_answer_ciphersuite_match = check_video_ciphersuite_match(pV); } rtpstream_set_srtp_video_remote(&rtpstream_callinfo, pV); if (sendMode == MODE_CLIENT) { // // TX-UAC-VIDEO SRTP context (a) -- SSRC/IPADDRESS/PORT // CryptoContextID txUACV; txUACV.ssrc = rtpstream_callinfo.taskinfo->video_ssrc_id; txUACV.address = host; txUACV.port = video_port; logSrtpInfo("call::process_incoming(): (a) TX-UAC-VIDEO SRTP context - ssrc:0x%08x address:%s port:%d\n", txUACV.ssrc, txUACV.address.c_str(), txUACV.port); _txUACVideo.setID(txUACV); if (video_answer_ciphersuite_match == 0) { logSrtpInfo("call::process_incoming(): (a) TX-UAC_VIDEO SRTP context -- CLIENT -- CIPHERSUITE SWAP...\n"); _txUACVideo.swapCrypto(); } // // RX-UAC-VIDEO SRTP context (b) -- MASTER KEY/SALT PARSE + CRYPTO TAG + CRYPTOSUITE // std::string mks1; mks1 = pV.primary_video_cryptokeyparams; if (!mks1.empty()) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- primary master key/salt: %s\n", mks1.c_str()); logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- primary crypto tag: %d\n", pV.primary_video_cryptotag); _rxUACVideo.decodeMasterKeySalt(mks1, PRIMARY_CRYPTO); _rxUACVideo.setCryptoTag(pV.primary_video_cryptotag, PRIMARY_CRYPTO); if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_80") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_32") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } } std::string mks2; mks2 = pV.secondary_video_cryptokeyparams; if (!mks2.empty()) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- secondary master key/salt: %s\n", mks2.c_str()); logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- secondary crypto tag: %d\n", pV.secondary_video_cryptotag); _rxUACVideo.decodeMasterKeySalt(mks2, SECONDARY_CRYPTO); _rxUACVideo.setCryptoTag(pV.secondary_video_cryptotag, SECONDARY_CRYPTO); if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_80") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_32") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (b) RX-UAC-VIDEO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUACVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUACVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } } } if (sendMode == MODE_SERVER) { // // TX-UAS-VIDEO SRTP context (d) -- SSRC/IPADDRESS/PORT // CryptoContextID txUASV; txUASV.ssrc = rtpstream_callinfo.taskinfo->video_ssrc_id; txUASV.address = host; txUASV.port = video_port; logSrtpInfo("call::process_incoming(): (d) TX-UAS-VIDEO SRTP context - ssrc:0x%08x address:%s port:%d\n", txUASV.ssrc, txUASV.address.c_str(), txUASV.port); _txUASVideo.setID(txUASV); if (video_answer_ciphersuite_match == 0) { logSrtpInfo("call::process_incoming(): (d) TX-UAS_VIDEO SRTP context -- SERVER -- CIPHERSUITE SWAP...\n"); _txUASVideo.swapCrypto(); } // // RX-UAS-VIDEO SRTP context (c) -- MASTER KEY/SALT PARSE + CRYPTO TAG + CRYPTOSUITE // std::string mks1; mks1 = pV.primary_video_cryptokeyparams; if (!mks1.empty()) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- primary master key/salt: %s\n", mks1.c_str()); logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- primary crypto tag: %d\n", pV.primary_video_cryptotag); _rxUASVideo.decodeMasterKeySalt(mks1, PRIMARY_CRYPTO); _rxUASVideo.setCryptoTag(pV.primary_video_cryptotag, PRIMARY_CRYPTO); if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(AES_CM_128, PRIMARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_80") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "NULL_HMAC_SHA1_32") && !pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, PRIMARY_CRYPTO); } else if (!strcmp(pV.primary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pV.primary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- NOENCRYPTION -- primary cryptosuite: [%s]\n", pV.primary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, PRIMARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, PRIMARY_CRYPTO); } } std::string mks2; mks2 = pV.secondary_video_cryptokeyparams; if (!mks2.empty()) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- secondary master key/salt: %s\n", mks2.c_str()); logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- secondary crypto tag: %d\n", pV.secondary_video_cryptotag); _rxUASVideo.decodeMasterKeySalt(mks2, SECONDARY_CRYPTO); _rxUASVideo.setCryptoTag(pV.secondary_video_cryptotag, SECONDARY_CRYPTO); if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(AES_CM_128, SECONDARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_80") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "NULL_HMAC_SHA1_32") && !pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- ENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_80") && pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_80, SECONDARY_CRYPTO); } else if (!strcmp(pV.secondary_video_cryptosuite, "AES_CM_128_HMAC_SHA1_32") && pV.secondary_unencrypted_video_srtp) { logSrtpInfo("call::process_incoming(): (c) RX-UAS-VIDEO SRTP context -- NOENCRYPTION -- secondary cryptosuite: [%s]\n", pV.secondary_video_cryptosuite); _rxUASVideo.selectCipherAlgorithm(NULL_CIPHER, SECONDARY_CRYPTO); /* Request JLSRTP NOT to decrypt */ _rxUASVideo.selectHashAlgorithm(HMAC_SHA1_32, SECONDARY_CRYPTO); } } } } #endif // USE_TLS } // ptr } // Content-Type /* Is it a response ? */ if ((msg[0] == 'S') && (msg[1] == 'I') && (msg[2] == 'P') && (msg[3] == '/') && (msg[4] == '2') && (msg[5] == '.') && (msg[6] == '0')) { reply_code = get_reply_code(msg); if (!reply_code) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } #ifdef PCAPPLAY } else if (hasMedia == 1 && !curmsg->ignoresdp && *(strstr(msg, "\r\n\r\n") + 4) != '\0') { /* Get media info if we find something like an SDP */ get_remote_media_addr(msg); #endif } /* It is a response: update peer_tag */ ptr = get_peer_tag(msg); if (ptr) { if(strlen(ptr) > (MAX_HEADER_LEN - 1)) { ERROR("Peer tag too long. Change MAX_HEADER_LEN and recompile sipp"); } if(peer_tag) { free(peer_tag); } peer_tag = strdup(ptr); if (!peer_tag) { ERROR("Out of memory allocating peer tag."); } } request[0] = 0; // extract the cseq method from the response extract_cseq_method(responsecseqmethod, msg); extract_transaction(txn, msg); } else if ((ptr = strchr(msg, ' '))) { if ((ptr - msg) < 64) { memcpy(request, msg, ptr - msg); request[ptr - msg] = 0; // Check if we received an ACK => call established if (strcmp(request, "ACK") == 0) { call_established = true; } #ifdef PCAPPLAY /* In case of INVITE or re-INVITE, ACK or PRACK get the media info if needed (= we got a pcap play action) */ if (((strncmp(request, "INVITE", 6) == 0) || (strncmp(request, "ACK", 3) == 0) || (strncmp(request, "PRACK", 5) == 0)) && hasMedia == 1 && !curmsg->ignoresdp) { get_remote_media_addr(msg); } #endif if (!strncmp(request, "INVITE", 6)) { invite_cseq = get_cseq_value(msg); last_recv_invite_cseq = invite_cseq; } reply_code = 0; } else { ERROR("SIP method too long in received message '%s'", msg); } } else { ERROR("Invalid sip message received '%s'", msg); } /* Try to find it in the expected non mandatory responses * until the first mandatory response in the scenario */ for (search_index = msg_index; search_index < (int)call_scenario->messages.size(); search_index++) { if (!matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) { if (call_scenario->messages[search_index]->optional) { continue; } /* The received message is different for the expected one */ break; } found = true; /* TODO : this is a little buggy: If a 100 trying from an INVITE * is delayed by the network until the BYE is sent, it may * stop BYE transmission erroneously, if the BYE also expects * a 100 trying. */ break; } /* Try to find it in the old non-mandatory receptions */ if (!found) { bool contig = true; for(search_index = msg_index - 1; search_index >= 0; search_index--) { if (call_scenario->messages[search_index]->optional == OPTIONAL_FALSE) { contig = false; } if (matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) { if (contig || call_scenario->messages[search_index]->optional == OPTIONAL_GLOBAL) { found = true; break; } else { if (int checkTxn = call_scenario->messages[search_index]->response_txn) { /* This is a reply to an old transaction. */ if (!strcmp(transactions[checkTxn - 1].txnID, txn)) { /* This reply is provisional, so it should have no effect if we recieve it out-of-order. */ if (reply_code >= 100 && reply_code <= 199) { TRACE_MSG("-----------------------------------------------\n" "Ignoring provisional %s message for transaction %s:\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); callDebug("Ignoring provisional %s message for transaction %s (hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); return true; } else if (int ackIndex = transactions[checkTxn - 1].ackIndex) { /* This is the message before an ACK, so verify that this is an invite transaction. */ assert (call_scenario->transactions[checkTxn - 1].isInvite); sendBuffer(createSendingMessage(call_scenario->messages[ackIndex]->send_scheme, ackIndex)); return true; } else { assert (!call_scenario->transactions[checkTxn - 1].isInvite); /* This is a non-provisional message for the transaction, and * we have already gotten our allowable response. Just make sure * that it is not a retransmission of the final response. */ if (transactions[checkTxn - 1].txnResp == hash(msg)) { /* We have gotten this retransmission out-of-order, let's just ignore it. */ TRACE_MSG("-----------------------------------------------\n" "Ignoring final %s message for transaction %s:\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); callDebug("Ignoring final %s message for transaction %s (hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); WARNING("Ignoring final %s message for transaction %s (hash %lu):\n\n%s", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); return true; } } } } else { /* * we received a non mandatory msg for an old transaction (this could be due to a retransmit. * If this response is for an INVITE transaction, retransmit the ACK to quench retransmits. */ if ( (reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)) ) && (call_scenario->messages[search_index+1]->M_type == MSG_TYPE_SEND) && (call_scenario->messages[search_index+1]->send_scheme->isAck()) ) { sendBuffer(createSendingMessage(call_scenario->messages[search_index+1]->send_scheme, search_index + 1)); return true; } } } } } } /* If it is still not found, process an unexpected message */ if(!found) { if (call_scenario->unexpected_jump >= 0) { bool recursive = false; if (call_scenario->retaddr >= 0) { if (M_callVariableTable->getVar(call_scenario->retaddr)->getDouble() != 0) { /* We are already in a jump! */ recursive = true; } else { M_callVariableTable->getVar(call_scenario->retaddr)->setDouble(msg_index); } } if (!recursive) { if (call_scenario->pausedaddr >= 0) { M_callVariableTable->getVar(call_scenario->pausedaddr)->setDouble(paused_until); } msg_index = call_scenario->unexpected_jump; queue_up(msg); paused_until = 0; return run(); } else { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } } } else { T_AutoMode L_case; if ((L_case = checkAutomaticResponseMode(request)) == 0) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } } else { // call aborted by automatic response mode if needed return automaticResponseMode(L_case, msg); } } } int test = (!found) ? -1 : call_scenario->messages[search_index]->test; /* test==0: No branching" * test==-1 branching without testing" * test>0 branching with testing */ /* Simulate loss of messages */ if(lost(search_index)) { TRACE_MSG("%s message lost (recv).", TRANSPORT_TO_STRING(transport)); callDebug("%s message lost (recv) (hash %lu).\n", TRANSPORT_TO_STRING(transport), hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[search_index] -> nb_lost++; return true; } /* If we are part of a transaction, mark this as the final response. */ if (int checkTxn = call_scenario->messages[search_index]->response_txn) { transactions[checkTxn - 1].txnResp = hash(msg); } /* Handle counters and RTDs for this message. */ do_bookkeeping(call_scenario->messages[search_index]); /* Increment the recv counter */ call_scenario->messages[search_index] -> nb_recv++; // Action treatment if (found) { //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg); actionResult = executeAction(msg, call_scenario->messages[search_index]); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } else if (actionResult == E_AR_CONNECT_FAILED) { terminate(CStat::E_FAILED_TCP_CONNECT); return false; } } } if (*request) { // update [cseq] with received CSeq unsigned long int rcseq = get_cseq_value(msg); if (rcseq > cseq) cseq = rcseq; } /* This is an ACK/PRACK or a response, and its index is greater than the * current active retransmission message, so we stop the retrans timer. * True also for CANCEL and BYE that we also want to answer to */ if(((reply_code) || ((!strcmp(request, "ACK")) || (!strcmp(request, "CANCEL")) || (!strcmp(request, "BYE")) || (!strcmp(request, "PRACK")))) && (search_index > last_send_index)) { /* * We should stop any retransmission timers on receipt of a provisional response only for INVITE * transactions. Non INVITE transactions continue to retransmit at T2 until a final response is * received */ if ( (0 == reply_code) || // means this is a request. (200 <= reply_code) || // final response ((0 != reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)))) ) { // prov for INVITE next_retrans = 0; } else { /* * We are here due to a provisional response for non INVITE. Update our next retransmit. */ next_retrans = clock_tick + global_t2; nb_last_delay = global_t2; } } /* This is a response with 200 so set the flag indicating that an * ACK is pending (used to prevent from release a call with CANCEL * when an ACK+BYE should be sent instead) */ if (reply_code == 200) { ack_is_pending = true; } /* store the route set only once. TODO: does not support target refreshes!! */ if (call_scenario->messages[search_index]->bShouldRecordRoutes && dialog_route_set == NULL) { realloc_ptr = (char*)realloc(next_req_url, MAX_HEADER_LEN); if (realloc_ptr) { next_req_url = realloc_ptr; } else { free(next_req_url); ERROR("Out of memory!"); return false; } /* cache the route set and the contact */ char rr[MAX_HEADER_LEN], contact[MAX_HEADER_LEN]; rr[0] = contact[0] = '\0'; /* yuck, get_header_content returns a static buffer :( */ strncat(rr, get_header_content(msg, "Record-Route:"), MAX_HEADER_LEN - 1); strncat(contact, get_header_content(msg, "Contact:"), MAX_HEADER_LEN - 1); computeRouteSetAndRemoteTargetUri(rr, contact, !reply_code); // WARNING("next_req_url is [%s]", next_req_url); } /* store the authentication info */ if ((call_scenario->messages[search_index] -> bShouldAuthenticate) && (reply_code == 401 || reply_code == 407)) { /* is a challenge */ char auth[MAX_HEADER_LEN]; memset(auth, 0, sizeof(auth)); strncpy(auth, get_header_content(msg, (char*)"Proxy-Authenticate:"), sizeof(auth) - 1); if (auth[0] == 0) { strncpy(auth, get_header_content(msg, (char*)"WWW-Authenticate:"), sizeof(auth) - 1); } if (auth[0] == 0) { ERROR("Couldn't find 'Proxy-Authenticate' or 'WWW-Authenticate' in 401 or 407!"); } realloc_ptr = (char *) realloc(dialog_authentication, strlen(auth) + 2); if (realloc_ptr) { dialog_authentication = realloc_ptr; } else { free(dialog_authentication); ERROR("Out of memory!"); return false; } sprintf(dialog_authentication, "%s", auth); /* Store the code of the challenge for building the proper header */ dialog_challenge_type = reply_code; next_nonce_count = 1; } /* If we are not advancing state, we should quite before we change this stuff. */ if (!call_scenario->messages[search_index]->advance_state) { return true; } /* Store last received message information for all messages so that we can * correctly identify retransmissions, and use its body for inclusion * in our messages. */ last_recv_index = search_index; last_recv_hash = cookie; callDebug("Set Last Recv Hash: %lu (recv index %d)\n", last_recv_hash, last_recv_index); realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, msg); /* If this was a mandatory message, or if there is an explicit next label set * we must update our state machine. */ if (!call_scenario->messages[search_index]->optional || (call_scenario->messages[search_index]->next && (test == -1 || M_callVariableTable->getVar(test)->isSet()))) { /* If we are paused, then we need to wake up so that we properly go through the state machine. */ paused_until = 0; msg_index = search_index; return next(); } else { unsigned int timeout = wake(); unsigned int candidate; if (call_scenario->messages[search_index]->next && M_callVariableTable->getVar(test)->isSet()) { WARNING("Last message generates an error and will not be used for next sends (for last_ variables):\n%s\n", msg); } /* We are just waiting for a message to be received, if any of the * potential messages have a timeout we set it as our timeout. We * start from the next message and go until any non-receives. */ for(search_index++; search_index < (int)call_scenario->messages.size(); search_index++) { if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECV) { break; } candidate = call_scenario->messages[search_index] -> timeout; if (candidate == 0) { if (defl_recv_timeout == 0) { continue; } candidate = defl_recv_timeout; } if (!timeout || (clock_tick + candidate < timeout)) { timeout = clock_tick + candidate; } } setPaused(); } return true; } double call::get_rhs(CAction *currentAction) { if (currentAction->getVarInId()) { return M_callVariableTable->getVar(currentAction->getVarInId())->getDouble(); } else { return currentAction->getDoubleValue(); } } call::T_ActionResult call::executeAction(const char* msg, message* curmsg) { CActions* actions; CAction* currentAction; int rc = 0; actions = curmsg->M_actions; // looking for action to do on this message if (actions == NULL) { return(call::E_AR_NO_ERROR); } for (int i = 0; i < actions->getActionSize(); i++) { currentAction = actions->getAction(i); if(currentAction == NULL) { continue; } if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) { char msgPart[MAX_SUB_MESSAGE_LENGTH]; /* Where to look. */ const char* haystack = NULL; if(currentAction->getLookingPlace() == CAction::E_LP_HDR) { extractSubMessage (msg, currentAction->getLookingChar(), msgPart, currentAction->getCaseIndep(), currentAction->getOccurrence(), currentAction->getHeadersOnly()); if(currentAction->getCheckIt() == true && (strlen(msgPart) == 0)) { // the sub message is not found and the checking action say it // MUST match --> Call will be marked as failed but will go on WARNING("Failed regexp match: header %s not found in message\n%s\n", currentAction->getLookingChar(), msg); return(call::E_AR_HDR_NOT_FOUND); } haystack = msgPart; } else if(currentAction->getLookingPlace() == CAction::E_LP_BODY) { haystack = strstr(msg, "\r\n\r\n"); if (!haystack) { if (currentAction->getCheckIt() == true) { WARNING("Failed regexp match: body not found in message\n%s\n", msg); return(call::E_AR_HDR_NOT_FOUND); } msgPart[0] = '\0'; haystack = msgPart; } haystack += strlen("\r\n\r\n"); } else if(currentAction->getLookingPlace() == CAction::E_LP_MSG) { haystack = msg; } else if(currentAction->getLookingPlace() == CAction::E_LP_VAR) { /* Get the input variable. */ haystack = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); if (!haystack) { if (currentAction->getCheckIt() == true) { WARNING("Failed regexp match: variable $%d not set", currentAction->getVarInId()); return(call::E_AR_HDR_NOT_FOUND); } } } else { ERROR("Invalid looking place: %d", currentAction->getLookingPlace()); } bool did_match = (currentAction->executeRegExp(haystack, M_callVariableTable) > 0); if (!did_match && currentAction->getCheckIt()) { // the message doesn't match and the checkit action say it MUST match // Allow easier regexp debugging WARNING("Failed regexp match: looking in '%s', with regexp '%s'", haystack, currentAction->getRegularExpression()); return(call::E_AR_REGEXP_DOESNT_MATCH); } else if (did_match && currentAction->getCheckItInverse()) { // The inverse of the above WARNING("Regexp matched but should not: looking in '%s', with regexp '%s'", haystack, currentAction->getRegularExpression()); return(call::E_AR_REGEXP_SHOULDNT_MATCH); } } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) { double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) { struct timeval tv; gettimeofday(&tv, NULL); M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec); M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec); } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0))); char *key = strdup(createSendingMessage(currentAction->getMessage(1))); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for insert: %s", file); } double value = inFiles[file]->lookup(key); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); free(file); free(key); } else if (currentAction->getActionType() == CAction::E_AT_INSERT) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0))); char *value = strdup(createSendingMessage(currentAction->getMessage(1))); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for insert: %s", file); } inFiles[file]->insert(value); free(file); free(value); } else if (currentAction->getActionType() == CAction::E_AT_REPLACE) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0))); char *line = strdup(createSendingMessage(currentAction->getMessage(1))); char *value = strdup(createSendingMessage(currentAction->getMessage(2))); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for replace: %s", file); } char *endptr; int lineNum = (int)strtod(line, &endptr); if (*endptr) { ERROR("Invalid line number for replace: %s", line); } inFiles[file]->replace(lineNum, value); free(file); free(line); free(value); } else if (currentAction->getActionType() == CAction::E_AT_CLOSE_CON) { if (call_socket) { call_socket->close(); call_socket = NULL; } } else if (currentAction->getActionType() == CAction::E_AT_SET_DEST) { /* Change the destination for this call. */ char *str_host = strdup(createSendingMessage(currentAction->getMessage(0))); char *str_port = strdup(createSendingMessage(currentAction->getMessage(1))); char *str_protocol = strdup(createSendingMessage(currentAction->getMessage(2))); char *endptr; int port = (int)strtod(str_port, &endptr); if (*endptr) { ERROR("Invalid port for setdest: %s", str_port); } int protocol = 0; if (!strcmp(str_protocol, "udp") || !strcmp(str_protocol, "UDP")) { protocol = T_UDP; } else if (!strcmp(str_protocol, "tcp") || !strcmp(str_protocol, "TCP")) { protocol = T_TCP; } else if (!strcmp(str_protocol, "tls") || !strcmp(str_protocol, "TLS")) { protocol = T_TLS; } else if (!strcmp(str_protocol, "sctp") || !strcmp(str_protocol, "SCTP")) { protocol = T_SCTP; } else { ERROR("Unknown transport for setdest: '%s'", str_protocol); } if (!call_socket && ((protocol == T_TCP && transport == T_TCP) || (protocol == T_SCTP && transport == T_SCTP))) { bool existing; if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { switch (protocol) { case T_SCTP: ERROR_NO("Unable to get a SCTP socket"); break; default: ERROR_NO("Unable to get a TCP socket"); } } if (!existing) { sipp_customize_socket(call_socket); } } if (!call_socket) { ERROR("Unable to get a socket"); } if (protocol != call_socket->ss_transport) { ERROR("Can not switch protocols during setdest."); } if (protocol == T_UDP) { /* Nothing to do. */ } else if (protocol == T_TLS) { ERROR("Changing destinations is not supported for TLS."); } else if (protocol == T_TCP || protocol == T_SCTP) { if (!multisocket) { ERROR("Changing destinations for TCP or SCTP requires multisocket mode."); } if (call_socket->ss_count > 1) { ERROR("Can not change destinations for a TCP/SCTP socket that has more than one user."); } } if (gai_getsockaddr(&call_peer, str_host, port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s' for setdest", str_host); } memcpy(&call_socket->ss_dest, &call_peer, sizeof(call_peer)); free(str_host); free(str_port); free(str_protocol); if (protocol == T_TCP || protocol == T_SCTP) { close(call_socket->ss_fd); call_socket->ss_fd = -1; call_socket->ss_changed_dest = true; if (call_socket->reconnect()) { if (reconnect_allowed()) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { WARNING("Unable to connect a TCP/SCTP/TLS socket"); } /* This connection failed. We must be in multisocket mode, because * otherwise we would already have a call_socket. This call can not * succeed, but does not affect any of our other calls. We do decrement * the reconnection counter however. */ if (reset_number != -1) { reset_number--; } return E_AR_CONNECT_FAILED; } else { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { ERROR_NO("Unable to connect a TCP/SCTP/TLS socket"); } } } } } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) { bool result; const char* lf; const char* end; lf = strchr(msg, '\n'); end = strchr(msg, ' '); if (!lf || !end) { result = false; } else if (lf < end) { result = false; } else { char *auth = get_header(msg, "Authorization:", true); auth = strdup(auth); // make a copy to avoid later get_header function call(clear its content) char *method = (char *)malloc(end - msg + 1); strncpy(method, msg, end - msg); method[end - msg] = '\0'; /* Generate the username to verify it against. */ char *tmp = createSendingMessage(currentAction->getMessage(0)); char *username = strdup(tmp); /* Generate the password to verify it against. */ tmp= createSendingMessage(currentAction->getMessage(1)); char *password = strdup(tmp); /* Need the body for length and auth-int calculation */ const char *body; const char *auth_body = NULL; body = strstr(msg, "\r\n\r\n"); if (body) { auth_body = body; auth_body += strlen("\r\n\r\n"); } else { auth_body = ""; } result = verifyAuthHeader(username, password, method, auth, auth_body); free(username); free(password); free(method); free(auth); } M_callVariableTable->getVar(currentAction->getVarId())->setBool(result); } else if (currentAction->getActionType() == CAction::E_AT_JUMP) { double operand = get_rhs(currentAction); if (msg_index == ((int)operand)) { ERROR("Jump statement at index %d jumps to itself and causes an infinite loop", msg_index); } msg_index = (int)operand - 1; /* -1 is allowed to go to the first label, but watch out * when using msg_index. */ if (msg_index < -1 || msg_index >= (int)call_scenario->messages.size()) { ERROR("Jump statement out of range (not 0 <= %d <= %zu)", msg_index + 1, call_scenario->messages.size()); } } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) { double operand = get_rhs(currentAction); paused_until = (int)operand; } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); if (operand == 0) { WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId()); } else { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) { bool value = currentAction->compare(M_callVariableTable); if ((currentAction->getCheckIt() && !value) || (currentAction->getCheckItInverse() && value) ) { double lhs = M_callVariableTable->getVar(currentAction->getVarInId())->getDouble(); double rhs = currentAction->getVarIn2Id() ? M_callVariableTable->getVar(currentAction->getVarIn2Id())->getDouble() : currentAction->getDoubleValue(); char *lhsName = call_scenario->allocVars->getName(currentAction->getVarInId()); const char *rhsName = ""; if (currentAction->getVarIn2Id()) { rhsName = call_scenario->allocVars->getName(currentAction->getVarIn2Id()); } const char *_inverse = currentAction->getCheckIt() ? "" : "_inverse"; call::T_ActionResult result = currentAction->getCheckIt() ? call::E_AR_TEST_DOESNT_MATCH : call::E_AR_TEST_SHOULDNT_MATCH; WARNING("test \"%s:%f %s %s:%f\" with check_it%s failed", lhsName, lhs, currentAction->comparatorToString(currentAction->getComparator()), rhsName, rhs, _inverse ); return(result); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (currentAction->getVarId() || (!currentAction->getCheckIt() && !currentAction->getCheckItInverse()) ) { M_callVariableTable->getVar(currentAction->getVarId())->setBool(value); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) { char *lhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); char *rhs = currentAction->getVarIn2Id() ? M_callVariableTable->getVar(currentAction->getVarIn2Id())->getString() : currentAction->getStringValue(); int value = strcmp(lhs, rhs); if ((currentAction->getCheckIt() && value) || (currentAction->getCheckItInverse() && !value) ) { char *lhsName = call_scenario->allocVars->getName(currentAction->getVarInId()); const char *rhsName = ""; if (currentAction->getVarIn2Id()) { rhsName = call_scenario->allocVars->getName(currentAction->getVarIn2Id()); } const char *_inverse = currentAction->getCheckIt() ? "" : "_inverse"; call::T_ActionResult result = currentAction->getCheckIt() ? call::E_AR_STRCMP_DOESNT_MATCH : call::E_AR_STRCMP_SHOULDNT_MATCH; WARNING("strcmp %s:\"%s\" and %s:\"%s\" with check_it%s returned %d", lhsName, lhs, rhsName, rhs, _inverse, value ); return(result); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (currentAction->getVarId() || (!currentAction->getCheckIt() && !currentAction->getCheckItInverse()) ) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) { CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); char *in = var->getString(); char *p = in; while (isspace(*p)) { p++; } char *q = strdup(p); var->setString(q); int l = strlen(q); for (int i = l - 1; (i >= 0) && isspace(q[i]); i--) { q[i] = '\0'; } } else if (currentAction->getActionType() == CAction::E_AT_VAR_URLDECODE) { CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); string input = var->getString(); string output = url_decode(input); char *char_output = strdup(output.c_str()); var->setString(char_output); } else if (currentAction->getActionType() == CAction::E_AT_VAR_URLENCODE) { CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); string input = var->getString(); string output = url_encode(input); char *char_output = strdup(output.c_str()); var->setString(char_output); } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) { double value; if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); } else { WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId()); } } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { double value = currentAction->getDistribution()->sample(); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) { char* x = createSendingMessage(currentAction->getMessage()); char *str = strdup(x); if (!str) { ERROR("Out of memory duplicating string for assignment!"); } M_callVariableTable->getVar(currentAction->getVarId())->setString(str); } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) { char* x = createSendingMessage(currentAction->getMessage()); LOG_MSG("%s\n", x); } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) { char* x = createSendingMessage(currentAction->getMessage()); WARNING("%s", x); } else if (currentAction->getActionType() == CAction::E_AT_LOG_ERROR) { char* x = createSendingMessage(currentAction->getMessage()); ERROR("%s", x); } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { char* x = createSendingMessage(currentAction->getMessage()); // TRACE_MSG("Trying to execute [%s]", x); 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) { int ret; ret = system(x); // second child runs if(ret == -1) { WARNING("system call error for %s", x); } } exit(EXIT_OTHER); } 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 %1ld for child %1ld", (long) ret, (long) l_pid); } } break; } } else if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) { switch (currentAction->getIntCmd()) { case CAction::E_INTCMD_STOP_ALL: quitting = 1; break; case CAction::E_INTCMD_STOP_NOW: sipp_exit(EXIT_TEST_RES_INTERNAL, 0, 0); break; case CAction::E_INTCMD_STOPCALL: default: return(call::E_AR_STOP_CALL); break; } #ifdef PCAPPLAY } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) || (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF)) { play_args_t* play_args = 0; if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) { const char *fileName = createSendingMessage(currentAction->getMessage()); currentAction->setPcapArgs(fileName); } if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF)) { play_args = &(this->play_args_a); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) { play_args = &(this->play_args_i); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { play_args = &(this->play_args_v); } else { ERROR("Can't find pcap data to play"); } // existing media thread could be using play_args, so we have to kill it before modifying parameters if (media_thread != 0) { // If a media_thread is already active, kill it before starting a new one pthread_cancel(media_thread); pthread_join(media_thread, NULL); media_thread = 0; } if (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF) { char* digits = createSendingMessage(currentAction->getMessage()); play_args->pcap = (pcap_pkts *) malloc(sizeof(pcap_pkts)); play_args->last_seq_no += parse_dtmf_play_args(digits, play_args->pcap, play_args->last_seq_no); play_args->free_pcap_when_done = 1; } else { play_args->pcap = currentAction->getPcapPkts(); play_args->free_pcap_when_done = 0; } /* port number is set in [auto_]media_port interpolation */ if (media_ip_is_ipv6) { struct sockaddr_in6* from = (struct sockaddr_in6*) &(play_args->from); from->sin6_family = AF_INET6; inet_pton(AF_INET6, media_ip, &(from->sin6_addr)); } else { struct sockaddr_in* from = (struct sockaddr_in*) &(play_args->from); from->sin_family = AF_INET; from->sin_addr.s_addr = inet_addr(media_ip); } /* Create a thread to send RTP or UDPTL packets */ pthread_attr_t attr; pthread_attr_init(&attr); #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN 16384 #endif int ret = pthread_create(&media_thread, &attr, send_wrapper, play_args); if (ret) { ERROR("Can't create thread to send RTP packets"); } pthread_attr_destroy(&attr); #endif } else if (currentAction->getActionType() == CAction::E_AT_RTP_ECHO) { rtp_echo_state = (currentAction->getDoubleValue() != 0); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PAUSE) { rtpstream_pause(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RESUME) { rtpstream_resume(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PLAY) { const char *fileName = createSendingMessage(currentAction->getMessage()); currentAction->setRTPStreamActInfo(fileName); rtpstream_play(&rtpstream_callinfo, currentAction->getRTPStreamActInfo()); // Obtain ID of parent thread used for the related RTP task call_scenario->addRtpTaskThreadID(rtpstream_callinfo.threadID); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PAUSEAPATTERN) { rtpstream_pauseapattern(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RESUMEAPATTERN) { rtpstream_resumeapattern(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PLAYAPATTERN) { const char *fileName = createSendingMessage(currentAction->getMessage()); currentAction->setRTPStreamActInfo(fileName); #ifdef USE_TLS // // TX/RX-UAC-AUDIO SRTP context (a)(b) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // if (sendMode == MODE_CLIENT) { rtpstream_actinfo_t* actinfo = currentAction->getRTPStreamActInfo(); logSrtpInfo("call::executeAction(): (a) TX-UAC-AUDIO SRTP context - CLIENT setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUACAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction(): (b) RX-UAC-AUDIO SRTP context - CLIENT setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUACAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction(): (a) TX-UAC-AUDIO SRTP context - CLIENT deriving session encryption/salting/authentication keys\n"); _txUACAudio.deriveSessionEncryptionKey(); _txUACAudio.deriveSessionSaltingKey(); _txUACAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction(): (a) TX-UAC-AUDIO SRTP context - CLIENT selecting encryption key\n"); _txUACAudio.selectEncryptionKey(); logSrtpInfo("call::executeAction(): (a) TX-UAC-AUDIO SRTP context - CLIENT resetting cipher state\n"); _txUACAudio.resetCipherState(); logSrtpInfo("call::executeAction(): (b) RX-UAC-AUDIO SRTP context - CLIENT deriving session encryption/salting/authentication keys\n"); _rxUACAudio.deriveSessionEncryptionKey(); _rxUACAudio.deriveSessionSaltingKey(); _rxUACAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction(): (b) RX-UAC-AUDIO SRTP context - CLIENT selecting decryption key\n"); _rxUACAudio.selectDecryptionKey(); logSrtpInfo("call::executeAction(): (b) RX-UAC-AUDIO SRTP context - CLIENT resetting cipher state\n"); _rxUACAudio.resetCipherState(); //logSrtpInfo("call::executeAction(): ******** (a) TX-UAC-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUACAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction(): ****************************************************\n"); //logSrtpInfo("call::executeAction(): ******** (b) RX-UAC-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUACAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction(): ****************************************************\n"); } logSrtpInfo("call::executeAction(): rtpstream_playapattern\n"); #endif // USE_TLS rtpstream_playapattern(&rtpstream_callinfo,currentAction->getRTPStreamActInfo(), _txUACAudio, _rxUACAudio); // Obtain ID of parent thread used for the related RTP task call_scenario->addRtpTaskThreadID(rtpstream_callinfo.threadID); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PAUSEVPATTERN) { rtpstream_pausevpattern(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RESUMEVPATTERN) { rtpstream_resumevpattern(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PLAYVPATTERN) { const char *fileName = createSendingMessage(currentAction->getMessage()); currentAction->setRTPStreamActInfo(fileName); #ifdef USE_TLS // // TX/RX-UAC-VIDEO SRTP context (a)(b) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // if (sendMode == MODE_CLIENT) { rtpstream_actinfo_t* actinfo = currentAction->getRTPStreamActInfo(); logSrtpInfo("call::executeAction(): (a) TX-UAC-VIDEO SRTP context - CLIENT setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUACVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction(): (b) RX-UAC-VIDEO SRTP context - CLIENT setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUACVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction(): (a) TX-UAC-VIDEO SRTP context - CLIENT deriving session encryption/salting/authentication keys\n"); _txUACVideo.deriveSessionEncryptionKey(); _txUACVideo.deriveSessionSaltingKey(); _txUACVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction(): (a) TX-UAC-VIDEO SRTP context - CLIENT selecting encryption key\n"); _txUACVideo.selectEncryptionKey(); logSrtpInfo("call::executeAction(): (a) TX-UAC-VIDEO SRTP context - CLIENT resetting cipher state\n"); _txUACVideo.resetCipherState(); logSrtpInfo("call::executeAction(): (b) RX-UAC-VIDEO SRTP context - CLIENT deriving session encryption/salting/authentication keys\n"); _rxUACVideo.deriveSessionEncryptionKey(); _rxUACVideo.deriveSessionSaltingKey(); _rxUACVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction(): (b) RX-UAC-VIDEO SRTP context - CLIENT selecting decryption key\n"); _rxUACVideo.selectDecryptionKey(); logSrtpInfo("call::executeAction(): (b) RX-UAC-VIDEO SRTP context - CLIENT resetting cipher state\n"); _rxUACVideo.resetCipherState(); //logSrtpInfo("call::executeAction(): ******** (a) TX-UAC-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUACVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction(): ****************************************************\n"); //logSrtpInfo("call::executeAction(): ******** (b) RX-UAC-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUACVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction(): ****************************************************\n"); } logSrtpInfo("call::executeAction(): rtpstream_playvpattern\n"); #endif // USE_TLS rtpstream_playvpattern(&rtpstream_callinfo,currentAction->getRTPStreamActInfo(), _txUACVideo, _rxUACVideo); // Obtain ID of parent thread used for the related RTP task call_scenario->addRtpTaskThreadID(rtpstream_callinfo.threadID); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_STARTAUDIO) { #ifdef USE_TLS if (sendMode == MODE_SERVER) { // // RX-UAS-AUDIO SRTP context (c) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUASA; rxUASA.ssrc = rtpstream_callinfo.taskinfo->audio_ssrc_id; rxUASA.address = media_ip; rxUASA.port = rtpstream_callinfo.local_audioport; logSrtpInfo("call::executeAction() [STARTAUDIO]: (c) RX-UAS-AUDIO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUASA.ssrc, rxUASA.address.c_str(), rxUASA.port); _rxUASAudio.setID(rxUASA); // // RX/TX-UAS-AUDIO SRTP context (c)(d) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // rtpecho_actinfo_t* actinfo = currentAction->getRTPEchoActInfo(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUASAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [STARTAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUASAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [STARTAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _rxUASAudio.deriveSessionEncryptionKey(); _rxUASAudio.deriveSessionSaltingKey(); _rxUASAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER selecting decryption key\n"); _rxUASAudio.selectDecryptionKey(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER resetting cipher state\n"); _rxUASAudio.resetCipherState(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _txUASAudio.deriveSessionEncryptionKey(); _txUASAudio.deriveSessionSaltingKey(); _txUASAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER selecting encryption key\n"); _txUASAudio.selectEncryptionKey(); logSrtpInfo("call::executeAction() [STARTAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER resetting cipher state\n"); _txUASAudio.resetCipherState(); //logSrtpInfo("call::executeAction() [STARTAUDIO]: ******** (c) RX-UAS-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUASAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [STARTAUDIO]: ****************************************************\n"); //logSrtpInfo("call::executeAction() [STARTAUDIO]: ******** (d) TX-UAS-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUASAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [STARTAUDIO]: ****************************************************\n"); } logSrtpInfo("call::executeAction() [STARTAUDIO]: rtpstream_rtpecho_startaudio\n"); #endif // USE_TLS rtpstream_rtpecho_startaudio(&rtpstream_callinfo, _rxUASAudio, _txUASAudio); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_UPDATEAUDIO) { #ifdef USE_TLS if (sendMode == MODE_SERVER) { // // RX-UAS-AUDIO SRTP context (c) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUASA; rxUASA.ssrc = rtpstream_callinfo.taskinfo->audio_ssrc_id; rxUASA.address = media_ip; rxUASA.port = rtpstream_callinfo.local_audioport; logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (c) RX-UAS-AUDIO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUASA.ssrc, rxUASA.address.c_str(), rxUASA.port); _rxUASAudio.setID(rxUASA); // // RX/TX-UAS-AUDIO SRTP context (c)(d) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // rtpecho_actinfo_t* actinfo = currentAction->getRTPEchoActInfo(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUASAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUASAudio.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _rxUASAudio.deriveSessionEncryptionKey(); _rxUASAudio.deriveSessionSaltingKey(); _rxUASAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER selecting decryption key\n"); _rxUASAudio.selectDecryptionKey(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (c) RX-UAS-AUDIO SRTP context - SERVER resetting cipher state\n"); _rxUASAudio.resetCipherState(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _txUASAudio.deriveSessionEncryptionKey(); _txUASAudio.deriveSessionSaltingKey(); _txUASAudio.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER selecting encryption key\n"); _txUASAudio.selectEncryptionKey(); logSrtpInfo("call::executeAction() [UPDATEAUDIO]: (d) TX-UAS-AUDIO SRTP context - SERVER resetting cipher state\n"); _txUASAudio.resetCipherState(); //logSrtpInfo("call::executeAction() [UPDATEAUDIO]: ******** (c) RX-UAS-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUASAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [UPDATEAUDIO]: ****************************************************\n"); //logSrtpInfo("call::executeAction() [UPDATEAUDIO]: ******** (d) TX-UAS-AUDIO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUASAudio.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [UPDATEAUDIO]: ****************************************************\n"); } logSrtpInfo("call::executeAction() [UPDATEAUDIO]: rtpstream_rtpecho_updateaudio\n"); #endif // USE_TLS rtpstream_rtpecho_updateaudio(&rtpstream_callinfo, _rxUASAudio, _txUASAudio); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_STOPAUDIO) { #ifdef USE_TLS logSrtpInfo("call::executeAction() [STOPAUDIO]: rtpstream_rtpecho_stopaudio\n"); #endif // USE_TLS rc = rtpstream_rtpecho_stopaudio(&rtpstream_callinfo); if (rc < 0) { #ifdef USE_TLS logSrtpInfo("call::executeAction() [STOPAUDIO]: rtpstream_rtpecho_stopaudio() rc==%d\n", rc); #endif // USE_TLS return call::E_AR_RTPECHO_ERROR; } } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_STARTVIDEO) { #ifdef USE_TLS if (sendMode == MODE_SERVER) { // // RX-UAS-VIDEO SRTP context (c) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUASV; rxUASV.ssrc = rtpstream_callinfo.taskinfo->video_ssrc_id; rxUASV.address = media_ip; rxUASV.port = rtpstream_callinfo.local_videoport; logSrtpInfo("call::executeAction() [STARTVIDEO]: (c) RX-UAS-VIDEO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUASV.ssrc, rxUASV.address.c_str(), rxUASV.port); _rxUASVideo.setID(rxUASV); // // RX/TX-UAS-VIDEO SRTP context (c)(d) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // rtpecho_actinfo_t* actinfo = currentAction->getRTPEchoActInfo(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUASVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [STARTVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUASVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [STARTVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _rxUASVideo.deriveSessionEncryptionKey(); _rxUASVideo.deriveSessionSaltingKey(); _rxUASVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER selecting decryption key\n"); _rxUASVideo.selectDecryptionKey(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER resetting cipher state\n"); _rxUASVideo.resetCipherState(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _txUASVideo.deriveSessionEncryptionKey(); _txUASVideo.deriveSessionSaltingKey(); _txUASVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER selecting encryption key\n"); _txUASVideo.selectEncryptionKey(); logSrtpInfo("call::executeAction() [STARTVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER resetting cipher state\n"); _txUASVideo.resetCipherState(); //logSrtpInfo("call::executeAction() [STARTVIDEO]: ******** (c) RX-UAS-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUASVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [STARTVIDEO]: ****************************************************\n"); //logSrtpInfo("call::executeAction() [STARTVIDEO]: ******** (d) TX-UAS-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUASVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [STARTVIDEO]: ****************************************************\n"); } logSrtpInfo("call::executeAction() [STARTVIDEO]: rtpstream_rtpecho_startvideo\n"); #endif // USE_TLS rtpstream_rtpecho_startvideo(&rtpstream_callinfo, _rxUASVideo, _txUASVideo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_UPDATEVIDEO) { #ifdef USE_TLS if (sendMode == MODE_SERVER) { // // RX-UAS-VIDEO SRTP context (c) -- SSRC/IPADDRESS/PORT // CryptoContextID rxUASV; rxUASV.ssrc = rtpstream_callinfo.taskinfo->video_ssrc_id; rxUASV.address = media_ip; rxUASV.port = rtpstream_callinfo.local_videoport; logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (c) RX-UAS-VIDEO SRTP context - ssrc:0x%08x address:%s port:%d\n", rxUASV.ssrc, rxUASV.address.c_str(), rxUASV.port); _rxUASVideo.setID(rxUASV); // // RX/TX-UAS-VIDEO SRTP context (c)(d) -- SRTP PAYLOAD SIZE + DERIVE SESSION ENCRYPTION/SALTING/AUTHENTICATION KEYS + SELECT ENCRYPTION KEY + RESET CIPHER STATE // WE ASSUME THE SAME CODEC PAYLOAD SIZE WILL BE USED IN BOTH DIRECTIONS // rtpecho_actinfo_t* actinfo = currentAction->getRTPEchoActInfo(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _rxUASVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER setting SRTP payload size to %d\n", actinfo->bytes_per_packet); _txUASVideo.setSrtpPayloadSize(actinfo->bytes_per_packet); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _rxUASVideo.deriveSessionEncryptionKey(); _rxUASVideo.deriveSessionSaltingKey(); _rxUASVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER selecting decryption key\n"); _rxUASVideo.selectDecryptionKey(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (c) RX-UAS-VIDEO SRTP context - SERVER resetting cipher state\n"); _rxUASVideo.resetCipherState(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER deriving session encryption/salting/authentication keys\n"); _txUASVideo.deriveSessionEncryptionKey(); _txUASVideo.deriveSessionSaltingKey(); _txUASVideo.deriveSessionAuthenticationKey(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER selecting encryption key\n"); _txUASVideo.selectEncryptionKey(); logSrtpInfo("call::executeAction() [UPDATEVIDEO]: (d) TX-UAS-VIDEO SRTP context - SERVER resetting cipher state\n"); _txUASVideo.resetCipherState(); //logSrtpInfo("call::executeAction() [UPDATEVIDEO]: ******** (c) RX-UAS-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _rxUASVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [UPDATEVIDEO]: ****************************************************\n"); //logSrtpInfo("call::executeAction() [UPDATEVIDEO]: ******** (d) TX-UAS-VIDEO SRTP context dump ********\n"); //logSrtpInfo("%s", _txUASVideo.dumpCryptoContext().c_str()); //logSrtpInfo("call::executeAction() [UPDATEVIDEO]: ****************************************************\n"); } logSrtpInfo("call::executeAction() [UPDATEVIDEO]: rtpstream_rtpecho_updatevideo\n"); #endif // USE_TLS rtpstream_rtpecho_updatevideo(&rtpstream_callinfo, _rxUASVideo, _txUASVideo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RTPECHO_STOPVIDEO) { #ifdef USE_TLS logSrtpInfo("call::executeAction() [STOPVIDEO]: rtpstream_rtpecho_stopvideo\n"); #endif // USE_TLS rc = rtpstream_rtpecho_stopvideo(&rtpstream_callinfo); if (rc < 0) { #ifdef USE_TLS logSrtpInfo("call::executeAction() [STOPVIDEO]: rtpstream_rtpecho_stopvideo() rc==%d\n", rc); #endif // USE_TLS return call::E_AR_RTPECHO_ERROR; } } else { ERROR("call::executeAction unknown action"); } } // end for return(call::E_AR_NO_ERROR); } void call::extractSubMessage(const char* msg, char* matchingString, char* result, bool case_indep, int occurrence, bool headers) { const char *ptr, *ptr1; int sizeOf; int i = 0; int len = strlen(matchingString); char mat1 = tolower(*matchingString); char mat2 = toupper(*matchingString); ptr = msg; while (*ptr) { if (!case_indep) { ptr = strstr(ptr, matchingString); if (ptr == NULL) break; if (headers == true && ptr != msg && *(ptr-1) != '\n') { ++ptr; continue; } } else { if (headers) { if (ptr != msg) { ptr = strchr(ptr, '\n'); if (ptr == NULL) break; ++ptr; if (*ptr == 0) break; } } else { ptr1 = strchr(ptr, mat1); ptr = strchr(ptr, mat2); if (ptr == NULL) { if (ptr1 == NULL) break; ptr = ptr1; } else { if (ptr1 != NULL && ptr1 < ptr) ptr = ptr1; } } if (strncasecmp(ptr, matchingString, len) != 0) { ++ptr; continue; } } // here with ptr pointing to a matching string if (occurrence <= 1) break; --occurrence; ++ptr; } if(ptr != NULL && *ptr != 0) { strncpy(result, ptr+len, MAX_SUB_MESSAGE_LENGTH); sizeOf = strlen(result); if(sizeOf >= MAX_SUB_MESSAGE_LENGTH) sizeOf = MAX_SUB_MESSAGE_LENGTH-1; while((i inFiles[fileName]->numLines()) { line = -1; } } if (line < 0) { return; } dest += inFiles[fileName]->getField(line, field, dest, SIPP_MAX_MSG_SIZE); } call::T_AutoMode call::checkAutomaticResponseMode(char* P_recv) { if (strcmp(P_recv, "BYE")==0) { return E_AM_UNEXP_BYE; } else if (strcmp(P_recv, "CANCEL") == 0) { return E_AM_UNEXP_CANCEL; } else if (strcmp(P_recv, "PING") == 0) { return E_AM_PING; } else if (auto_answer && ((strcmp(P_recv, "INFO") == 0) || (strcmp(P_recv, "NOTIFY") == 0) || (strcmp(P_recv, "OPTIONS") == 0) || (strcmp(P_recv, "UPDATE") == 0))) { return E_AM_AA; } else { return E_AM_DEFAULT; } } void call::setLastMsg(const char *msg) { realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return; } strcpy(last_recv_msg, msg); } bool call::automaticResponseMode(T_AutoMode P_case, const char* P_recv) { int res ; char * old_last_recv_msg = NULL; bool last_recv_msg_saved = false; switch (P_case) { case E_AM_UNEXP_BYE: // response for an unexpected BYE // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); // The BYE is unexpected, count it call_scenario->messages[msg_index] -> nb_unexp++; 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) { sendBuffer(createSendingMessage(get_default_message("200"))); } // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"))); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete this; } 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 // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); // The CANCEL is unexpected, count it call_scenario->messages[msg_index] -> nb_unexp++; 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) { sendBuffer(createSendingMessage(get_default_message("200"))); } // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"))); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete this; } 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 realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); if (default_behaviors & DEFAULT_BEHAVIOR_PINGREPLY) { WARNING("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id); sendBuffer(createSendingMessage(get_default_message("200"))); // 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 = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"))); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } CStat::globalStat(CStat::E_AUTO_ANSWERED); delete this; } 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 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 realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); free(old_last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); TRACE_CALLDEBUG("Automatic response mode for an unexpected INFO, NOTIFY, OPTIONS or UPDATE for call: %s", (id == NULL) ? "none" : id); sendBuffer(createSendingMessage(get_default_message("200"))); // restore previous last msg if (last_recv_msg_saved == true) { realloc_ptr = (char *) realloc(last_recv_msg, strlen(old_last_recv_msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, 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); return true; break; default: ERROR("Internal error for automaticResponseMode - mode %d is not implemented!", P_case); break ; } return false; } #ifdef USE_TLS int call::logSrtpInfo(const char *fmt, ...) { va_list args; if (_srtpctxdebugfile != NULL) { va_start(args, fmt); vfprintf(_srtpctxdebugfile, fmt, args); va_end(args); } return 0; } #endif // USE_TLS void call::setSessionState(SessionState state) { _sessionStateOld = _sessionStateCurrent; _sessionStateCurrent = state; } SessionState call::getSessionStateCurrent() { return _sessionStateCurrent; } SessionState call::getSessionStateOld() { return _sessionStateOld; } #ifdef PCAPPLAY void *send_wrapper(void *arg) { play_args_t *s = (play_args_t *) arg; //struct sched_param param; //int ret; //param.sched_priority = 10; //ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); //if(ret) // ERROR("Can't set RTP play thread realtime parameters"); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); send_packets(s); pthread_exit(NULL); return NULL; } #endif #ifdef GTEST #include "gtest/gtest.h" #include "gtest/gtest.h" class mockcall : public call { public: mockcall(bool is_ipv6) : listener("//testing", true), call("///testing", is_ipv6, 0, NULL) {} /* Helpers to poke at protected internals */ void parse_media_addr(std::string const& msg) { get_remote_media_addr(msg); } #ifdef PCAPPLAY bool has_media() { return hasMediaInformation; } template T get_audio_addr() { T sa; std::memcpy(&sa, &play_args_a.to, sizeof(T)); return sa; } #endif }; bool operator==(const struct sockaddr_in& a, const struct sockaddr_in &b) { return a.sin_family == b.sin_family && a.sin_port == b.sin_port && std::memcmp(&a.sin_addr, &b.sin_addr, sizeof(in_addr)) == 0; } bool operator==(const struct sockaddr_in6& a, const struct sockaddr_in6 &b) { return a.sin6_family == b.sin6_family && a.sin6_port == b.sin6_port && std::memcmp(&a.sin6_addr, &b.sin6_addr, sizeof(in_addr)) == 0; } const std::string test_sdp_v4 = "v=0\r\n" "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n" "s=-\r\n" "c=IN IP4 127.0.0.1\r\n" "t=0 0\r\n" "m=audio 12345 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n"; const std::string test_sdp_v6 = "v=0\r\n" "o=user1 53655765 2353687637 IN IP6 ::1\r\n" "s=-\r\n" "c=IN IP6 ::1\r\n" "t=0 0\r\n" "m=audio 12345 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n"; TEST(sdp, parse_valid_sdp_msg) { ASSERT_EQ(find_in_sdp("c=IN IP4 ", test_sdp_v4), "127.0.0.1"); ASSERT_EQ(find_in_sdp("c=IN IP6 ", test_sdp_v6), "::1"); ASSERT_EQ(find_in_sdp("m=audio ", test_sdp_v4), "12345"); ASSERT_EQ(find_in_sdp("m=audio ", test_sdp_v6), "12345"); } TEST(sdp, parse_invalid_sdp_msg) { ASSERT_EQ(find_in_sdp("c=IN IP4 ", test_sdp_v6), ""); ASSERT_EQ(find_in_sdp("c=IN IP6 ", test_sdp_v4), ""); ASSERT_EQ(find_in_sdp("m=video ", test_sdp_v6), ""); ASSERT_EQ(find_in_sdp("m=video ", test_sdp_v4), ""); } #ifdef PCAPPLAY TEST(sdp, good_remote_media_addr_v4) { media_ip_is_ipv6 = false; struct sockaddr_in reference; reference.sin_family = AF_INET; reference.sin_port = htons(12345); inet_pton(AF_INET, "127.0.0.1", &reference.sin_addr); mockcall call(false); call.parse_media_addr(test_sdp_v4); ASSERT_EQ(call.has_media(), true); ASSERT_EQ(reference, call.get_audio_addr()); } TEST(sdp, good_remote_media_addr_v6) { media_ip_is_ipv6 = true; struct sockaddr_in6 reference; reference.sin6_family = AF_INET6; reference.sin6_port = htons(12345); inet_pton(AF_INET6, "::1", &reference.sin6_addr); mockcall call(true); call.parse_media_addr(test_sdp_v6); ASSERT_EQ(call.has_media(), true); ASSERT_EQ(reference, call.get_audio_addr()); } #endif /* PCAP_PLAY */ #endif sipp-3.7.2/src/call_generation_task.cpp0000664000000000000000000002113414525516253015042 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class CallGenerationTask *CallGenerationTask::instance = NULL; unsigned long CallGenerationTask::calls_since_last_rate_change = 0; unsigned long CallGenerationTask::last_rate_change_time = 0; void CallGenerationTask::initialize() { assert(instance == NULL); instance = new CallGenerationTask(); } CallGenerationTask::CallGenerationTask() { setRunning(); } CallGenerationTask::~CallGenerationTask() { instance = NULL; } void CallGenerationTask::dump() { WARNING("Uniform rate call generation task: %f", rate); } unsigned int CallGenerationTask::wake() { int retval; if (paused || (users >= 0)) { // When paused or when we're doing user-based rather than // rate-based calls, return a sentinel value to indicate that // this task should wait forever before rescheduling. retval = DONT_RESCHEDULE; } else { float ms_per_call = rate_period_ms/MAX(rate, 1); /* We need to compute when the next call is going to be * opened. The current time is the time when the rate last * changed, plus the number of calls since then multiplied by * the number of milliseconds between each call. * * We then add the number of milliseconds between each call to that * figure. */ retval = (unsigned long) last_rate_change_time + (calls_since_last_rate_change * ms_per_call) + ms_per_call; /* On startup, when last_rate_change_time is 0, this calculation can be 0 (if we're opening multiple calls per ms). But 0 indicates that we should wait forever, so avoid that and return 1 instead. */ if (retval == 0 /* DONT_RESCHEDULE */) { retval = 1; } } return retval; } bool CallGenerationTask::run() { int calls_to_open = 0; if (quitting) { delete this; return false; } if (paused) { setPaused(); return true; } unsigned long long current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall); unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); if (users >= 0) { calls_to_open = users - current_calls; } else { float calls_per_ms = rate/rate_period_ms; unsigned int ms_since_last_rate_change = clock_tick - last_rate_change_time; unsigned int expected_total_calls = ms_since_last_rate_change * calls_per_ms; calls_to_open = expected_total_calls - calls_since_last_rate_change; } if (total_calls + calls_to_open > stop_after) { calls_to_open = stop_after - total_calls; } /* We base our scheduling on the number of calls made since the last rate * change, but if we reduce the number of calls we open in order to keep * within the limit, that throws this calculation off and brings CPU% up to * 100%. To avoid this, we increment calls_since_last_rate_change here. */ calls_since_last_rate_change += calls_to_open; if (open_calls_allowed && (current_calls + calls_to_open > open_calls_allowed)) { calls_to_open = open_calls_allowed - current_calls; } if (calls_to_open <= 0) { calls_to_open = 0; } unsigned int start_clock = getmilliseconds(); while(calls_to_open--) { /* Associate a user with this call, if we are in users mode. */ int userid = 0; if (users >= 0) { userid = freeUsers.back(); freeUsers.pop_back(); } // Adding a new outgoing call main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); call* call_ptr = call::add_call(userid, local_ip_is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); if(!call_ptr) { ERROR("Out of memory allocating call!"); } outbound_congestion = false; if (!multisocket) { switch(transport) { case T_UDP: call_ptr->associate_socket(main_socket); main_socket->ss_count++; break; case T_TCP: case T_SCTP: case T_TLS: call_ptr->associate_socket(tcp_multiplex); tcp_multiplex->ss_count++; break; } } // We shouldn't run for more than 1ms, so as not to tie up the scheduler if (getmilliseconds() > start_clock) { break; } } if (calls_to_open <= 0) { setPaused(); } else { // We stopped before opening all the calls we needed to so as // not to tie up the scheduler - don't pause this task, so // that it gets rescheduled ASAP and can continue. } // Quit after asked number of calls is reached if(total_calls >= stop_after) { quitting = 1; return false; } return true; } void CallGenerationTask::set_paused(bool new_paused) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ return; } if (new_paused) { instance->setPaused(); } else { instance->setRunning(); if (users >= 0) { set_users(users); } else { set_rate(rate); } } paused = new_paused; } void CallGenerationTask::set_rate(double new_rate) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ } rate = new_rate; if(rate < 0) { rate = 0; } last_rate_change_time = getmilliseconds(); calls_since_last_rate_change = 0; if(!open_calls_user_setting) { // Calculate the maximum number of open calls from the rate // and the call duration, unless the user has set a fixed value. int call_duration_min = main_scenario->duration; if (duration > call_duration_min) { call_duration_min = duration; } if (call_duration_min < 1000) { call_duration_min = 1000; } open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms); if(!open_calls_allowed) { open_calls_allowed = 1; } } } void CallGenerationTask::set_users(int new_users) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ return; } if (new_users < 0) { new_users = 0; } assert(users >= 0); while (users < new_users) { int userid; if (!retiredUsers.empty()) { userid = retiredUsers.back(); retiredUsers.pop_back(); } else { userid = users + 1; userVarMap[userid] = new VariableTable(userVariables); } freeUsers.push_front(userid); users++; } users = open_calls_allowed = new_users; last_rate_change_time = clock_tick; calls_since_last_rate_change = 0; assert(open_calls_user_setting); instance->setRunning(); } void CallGenerationTask::free_user(int userId) { if (main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) > open_calls_allowed) { retiredUsers.push_front(userId); } else { freeUsers.push_front(userId); /* Wake up the call creation thread. */ if (instance) { instance->setRunning(); } } } sipp-3.7.2/src/comp.c0000664000000000000000000000323714525516253011274 0ustar /* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * */ #define COMP_MAIN #include "comp.h" #include #include char * comp_load(void) { void *handle; char *error; comp_error[0] = 0; handle = dlopen(COMP_PLUGGIN, RTLD_LAZY); if (!handle) { strcpy(comp_error, dlerror()); return comp_error; } *(void **)(&comp_compress) = dlsym(handle, "comp_compress"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } *(void **)(&comp_uncompress) = dlsym(handle, "comp_uncompress"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } *(void **)(&comp_free) = dlsym(handle, "comp_free"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } return 0; } sipp-3.7.2/src/deadcall.cpp0000664000000000000000000000567014525516253012432 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include "sipp.hpp" #include "deadcall.hpp" #include "assert.h" /* Defined in call.cpp. */ extern timewheel paused_calls; deadcall::deadcall(const char *id, const char *reason) : listener(id, true) { this->expiration = clock_tick + deadcall_wait; this->reason = strdup(reason); setPaused(); } deadcall::~deadcall() { free(reason); } bool deadcall::process_incoming(const char* msg, const struct sockaddr_storage* /*src*/) { char buffer[MAX_HEADER_LEN]; CStat::globalStat(CStat::E_DEAD_CALL_MSGS); setRunning(); snprintf(buffer, MAX_HEADER_LEN, "Dead call %s (%s)", id, reason); WARNING("%s, received '%s'", buffer, msg); TRACE_MSG("-----------------------------------------------\n" "Dead call %s received a %s message:\n\n%s\n", id, TRANSPORT_TO_STRING(transport), msg); expiration = clock_tick + deadcall_wait; return run(); } bool deadcall::process_twinSippCom(char * msg) { CStat::globalStat(CStat::E_DEAD_CALL_MSGS); TRACE_MSG("Received twin message for dead (%s) call %s:%s\n", reason, id, msg); return true; } bool deadcall::run() { if (clock_tick > expiration) { delete this; return false; } else { setPaused(); return true; } } unsigned int deadcall::wake() { return expiration; } /* Dump call info to error log. */ void deadcall::dump() { WARNING("%s: Dead Call (%s) expiring at %lu", id, reason, expiration); } sipp-3.7.2/src/fileutil.c0000664000000000000000000000270014525516253012145 0ustar /* * 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 */ #include #include #include #include #include extern char* scenario_path; char* find_file(const char* filename) { char *fullpath; if (filename[0] == '/' || !*scenario_path) { return strdup(filename); } fullpath = malloc(MAX_PATH); snprintf(fullpath, MAX_PATH, "%s%s", scenario_path, filename); if (access(fullpath, R_OK) < 0) { free(fullpath); WARNING("SIPp now prefers looking for pcap/rtpstream files next to the scenario. " "%s couldn't be found next to the scenario, falling back to " "using the current working directory", filename); return strdup(filename); } return fullpath; } sipp-3.7.2/src/infile.cpp0000664000000000000000000002573514525516253012153 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #include "sipp.hpp" #include "screen.hpp" #include "stat.hpp" #include "infile.hpp" #include #include /* Read MAX_CHAR_BUFFER_SIZE size lines from the "fileName" and populate it in * the fileContents vector. Each line should be terminated with a '\n' */ FileContents::FileContents(const char *fileName) { ifstream *inFile = new ifstream(fileName); int virtualLines = 0; if (!inFile->good()) { ERROR("Unable to open file %s", fileName); } this->fileName = fileName; realLinesInFile = lineCounter = numLinesInFile = 0; /* Initialize printf info. */ printfFile = false; printfOffset = 0; printfMultiple = 1; std::string lineStr; std::getline(*inFile, lineStr); if (!lineStr.empty() && *lineStr.rbegin() == '\r') { lineStr.pop_back(); } const char* line = lineStr.c_str(); if (NULL != strstr(line, "RANDOM")) { usage = InputFileRandomOrder; } else if (NULL != strstr(line, "SEQUENTIAL")) { usage = InputFileSequentialOrder; } else if (NULL != strstr(line, "USER")) { usage = InputFileUser; } else { ERROR("Unknown file type (valid values are RANDOM, SEQUENTIAL, and USER) for %s:%s", fileName, line); } const char *useprintf; if ((useprintf = strstr(line, "PRINTF"))) { /* We are going to operate in printf mode, which uses the line as a format * string for printf with the line number. */ useprintf += strlen("PRINTF"); if (*useprintf != '=') { ERROR("Invalid file printf specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; virtualLines = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\r' && *endptr != '\n' && *endptr != ',') { ERROR("Invalid file printf specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } if (virtualLines == 0) { ERROR("A printf file must have at least one virtual line %s:%s", fileName, line); } printfFile = true; } if ((useprintf = strstr(line, "PRINTFOFFSET"))) { useprintf += strlen("PRINTFOFFSET"); if (*useprintf != '=') { ERROR("Invalid file PRINTFOFFSET specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; printfOffset = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\n' && *endptr != ',') { ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } } if ((useprintf = strstr(line, "PRINTFMULTIPLE"))) { useprintf += strlen("PRINTFMULTIPLE"); if (*useprintf != '=') { ERROR("Invalid PRINTFMULTIPLE specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; printfMultiple = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\n' && *endptr != ',') { ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } } while (!inFile->eof()) { lineStr.clear(); std::getline(*inFile, lineStr); if (!lineStr.empty()) { if ('#' != lineStr[0]) { fileLines.push_back(lineStr); realLinesInFile++; /* this counts number of valid data lines */ } } else { break; } } if (realLinesInFile == 0) { ERROR("Input file has zero lines: %s", fileName); } if (printfFile) { numLinesInFile = virtualLines; } else { numLinesInFile = realLinesInFile; } delete inFile; indexMap = NULL; indexField = -1; } int FileContents::getLine(int line, char *dest, int len) { if (printfFile) { line %= realLinesInFile; } return snprintf(dest, len, "%s", fileLines[line].c_str()); } int FileContents::getField(int lineNum, int field, char *dest, int len) { int curfield = field; int curline = lineNum; dest[0] = '\0'; if (lineNum >= numLinesInFile) { return 0; } if (printfFile) { curline %= realLinesInFile; } const string & line = fileLines[curline]; size_t pos(0), oldpos(0); do { oldpos = pos; size_t localpos = line.find(';', oldpos); if (localpos != string::npos) { pos = localpos + 1; } else { pos = localpos; break; } if (curfield == 0) { break; } curfield --; } while (oldpos != string::npos); if (curfield) { WARNING("Field %d not found in the file %s", field, fileName); return 0; } if (string::npos == oldpos) { return 0; } if (string::npos != pos) { // should not be decremented for fieldN pos -= (oldpos + 1); } string x = line.substr(oldpos, pos); if (x.length()) { if (printfFile) { const char *s = x.c_str(); int l = strlen(s); int copied = 0; for (int i = 0; i < l; i++) { if (s[i] == '%') { if (s[i + 1] == '%') { dest[copied++] = s[i]; } else { const char *format = s + i; i++; while (s[i] != 'd') { if (i == l) { ERROR("Invalid printf injection field (ran off end of line): %s", s); } if (!(isdigit(s[i]) || s[i] == '.' || s[i] == '-')) { ERROR("Invalid printf injection field (only decimal values allowed '%c'): %s", s[i], s); } i++; } assert(s[i] == 'd'); char *tmp = (char *)malloc(s + i + 2 - format); if (!tmp) { ERROR("Out of memory!"); } memcpy(tmp, format, s + i + 1 - format); tmp[s + i + 1 - format] = '\0'; copied += sprintf(dest + copied, tmp, printfOffset + (lineNum * printfMultiple)); free(tmp); } } else { dest[copied++] = s[i]; } } dest[copied] = '\0'; return copied; } else { return snprintf(dest, len, "%s", x.c_str()); } } else { return 0; } } int FileContents::numLines() { return numLinesInFile; } int FileContents::nextLine(int userId) { switch(usage) { case InputFileRandomOrder: return rand() % numLinesInFile; case InputFileSequentialOrder: { int ret = lineCounter; lineCounter = (lineCounter + 1) % numLinesInFile; return ret; } case InputFileUser: if (userId == 0) { return -1; } if ((userId - 1) >= numLinesInFile) { ERROR("%s has only %d lines, yet user %d was requested.", fileName, numLinesInFile, userId); } return userId - 1; default: ERROR("Internal error: unknown file usage mode!"); return -1; } } void FileContents::dump(void) { WARNING("Line choosing strategy is [%s]. m_counter [%d] numLinesInFile [%d] realLinesInFile [%d]", usage == InputFileSequentialOrder ? "SEQUENTIAL" : usage == InputFileRandomOrder ? "RANDOM" : usage == InputFileUser ? "USER" : "UNKNOWN", lineCounter, numLinesInFile, realLinesInFile); for (int i = 0; i < realLinesInFile && fileLines[i][0]; i++) { WARNING("%s:%d reads [%s]", fileName, i, fileLines[i].c_str()); } } void FileContents::index(int field) { this->indexField = field; indexMap = new str_int_map; for (int line = 0; line < numLines(); line++) { reIndex(line); } } int FileContents::lookup(char *key) { if (indexField == -1) { ERROR("Invalid Index File: %s", fileName); } if (!indexMap) { ERROR("Invalid Index File: %s", fileName); } str_int_map::iterator index_it = indexMap->find(key); if (index_it == indexMap->end()) { return -1; } return index_it->second; } void FileContents::insert(char *value) { if (printfFile) { ERROR("Can not insert or replace into a printf file: %s", fileName); } fileLines.push_back(value); realLinesInFile++; numLinesInFile++; if (indexField != -1) { reIndex(realLinesInFile - 1); } char line[1024]; getLine(realLinesInFile - 1, line, sizeof(line)); char tmp[1024]; getField(realLinesInFile - 1, 0, tmp, sizeof(tmp)); } void FileContents::replace(int line, char *value) { if (printfFile) { ERROR("Can not insert or replace into a printf file: %s", fileName); } if (line >= realLinesInFile || line < 0) { ERROR("Invalid line number (%d) for file: %s (%d lines)", line, fileName, realLinesInFile); } deIndex(line); fileLines[line] = value; reIndex(line); } void FileContents::reIndex(int line) { if (indexField == -1) { return; } assert(line >= 0); assert(line < realLinesInFile); char tmp[SIPP_MAX_MSG_SIZE]; getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); if (index_it != indexMap->end()) { indexMap->erase(index_it); } indexMap->insert(pair(str_int_map::key_type(tmp), line)); } void FileContents::deIndex(int line) { if (indexField == -1) { return; } assert(line >= 0); assert(line < realLinesInFile); char tmp[SIPP_MAX_MSG_SIZE]; getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); if (index_it != indexMap->end()) { if (index_it->second == line) { indexMap->erase(index_it); } } } sipp-3.7.2/src/jlsrtp.cpp0000664000000000000000000033276614525516253012230 0ustar /* * 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 * * Author: Jeannot Langlois (jeannot.langlois@gmail.com) -- 2016-2020 */ #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) #include "jlsrtp.hpp" #include #include #include #include #include #include #include #include #include // std::ostringstream // --------------- PRIVATE METHODS ---------------- bool JLSRTP::isBase64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } int JLSRTP::resetPseudoRandomState(std::vector iv) { unsigned int ivSize = 0; ivSize = iv.size(); assert(ivSize == JLSRTP_SALTING_KEY_LENGTH); if (ivSize == JLSRTP_SALTING_KEY_LENGTH) { // aes_ctr128_encrypt() requires 'num' and 'ecount' to be set to zero on the first call _pseudorandomstate.num = 0; memset(_pseudorandomstate.ecount, 0, sizeof(_pseudorandomstate.ecount)); // Clear BOTH high-order bytes [0..13] for 'IV' AND low-order bytes [14..15] for 'counter' memset(_pseudorandomstate.ivec, 0, AES_BLOCK_SIZE); // Copy 'IV' into high-order bytes [0..13] -- low-order bytes [14..15] remain zero memcpy(_pseudorandomstate.ivec, iv.data(), iv.size()); return 0; } else { return -1; } } int JLSRTP::pseudorandomFunction(std::vector iv, int n, std::vector &output) { int rc = 0; unsigned int num_loops = 0; std::vector block; std::vector input; unsigned int ivSize = 0; unsigned int keySize = 0; int retVal = 0; switch (_active_crypto) { case PRIMARY_CRYPTO: { ivSize = iv.size(); keySize = _primary_crypto.master_key.size(); assert(ivSize == JLSRTP_SALTING_KEY_LENGTH); assert(keySize == JLSRTP_ENCRYPTION_KEY_LENGTH); if (ivSize == JLSRTP_SALTING_KEY_LENGTH) { if (keySize == JLSRTP_ENCRYPTION_KEY_LENGTH) { input.resize(AES_BLOCK_SIZE, 0); output.clear(); // Determine how many AES_BLOCK_SIZE-byte encryption loops will be necessary to achieve at least n/8 bytes of pseudorandom ciphertext num_loops = (n % JLSRTP_PSEUDORANDOM_BITS) ? ((n / JLSRTP_PSEUDORANDOM_BITS) + 1) : (n / JLSRTP_PSEUDORANDOM_BITS); // Set encryption key rc = setAESPseudoRandomFunctionKey(_active_crypto); if (rc == 0) { // Reset IV/counter state resetPseudoRandomState(iv); for (unsigned int i = 0; i < num_loops; i++) { // Encrypt given _pseudorandomstate.ivec input using aes_key to block block.clear(); block.resize(AES_BLOCK_SIZE, 0); AES_ctr128_pseudorandom_EVPencrypt(input.data(), block.data(), AES_BLOCK_SIZE, _pseudorandomstate.ivec, _pseudorandomstate.ecount, &_pseudorandomstate.num); output.insert(output.end(), block.begin(), block.end()); } // Truncate output to n/8 bytes output.resize(n / 8); retVal = 0; } else { retVal = -3; } } else { retVal = -2; } } else { retVal = -1; } } break; case SECONDARY_CRYPTO: { ivSize = iv.size(); keySize = _secondary_crypto.master_key.size(); assert(ivSize == JLSRTP_SALTING_KEY_LENGTH); assert(keySize == JLSRTP_ENCRYPTION_KEY_LENGTH); if (ivSize == JLSRTP_SALTING_KEY_LENGTH) { if (keySize == JLSRTP_ENCRYPTION_KEY_LENGTH) { input.resize(AES_BLOCK_SIZE, 0); output.clear(); // Determine how many AES_BLOCK_SIZE-byte encryption loops will be necessary to achieve at least n/8 bytes of pseudorandom ciphertext num_loops = (n % JLSRTP_PSEUDORANDOM_BITS) ? ((n / JLSRTP_PSEUDORANDOM_BITS) + 1) : (n / JLSRTP_PSEUDORANDOM_BITS); // Set encryption key rc = setAESPseudoRandomFunctionKey(_active_crypto); if (rc == 0) { // Reset IV/counter state resetPseudoRandomState(iv); for (unsigned int i = 0; i < num_loops; i++) { // Encrypt given _pseudorandomstate.ivec input using aes_key to block block.clear(); block.resize(AES_BLOCK_SIZE, 0); AES_ctr128_pseudorandom_EVPencrypt(input.data(), block.data(), AES_BLOCK_SIZE, _pseudorandomstate.ivec, _pseudorandomstate.ecount, &_pseudorandomstate.num); output.insert(output.end(), block.begin(), block.end()); } // Truncate output to n/8 bytes output.resize(n / 8); retVal = 0; } else { retVal = -3; } } else { retVal = -2; } } else { retVal = -1; } } break; default: { retVal = -4; } break; } return retVal; } int JLSRTP::shiftVectorLeft(std::vector &shifted_vec, std::vector &original_vec, int shift_value) { shifted_vec.clear(); shifted_vec.resize(original_vec.size(), 0); for (unsigned int i = shift_value, j = 0; i < original_vec.size(); i++, j++) { shifted_vec[j] = original_vec[i]; } return 0; } int JLSRTP::shiftVectorRight(std::vector &shifted_vec, std::vector &original_vec, int shift_value) { shifted_vec.clear(); shifted_vec.resize(original_vec.size(), 0); for (unsigned int i = shift_value, j = 0; i < shifted_vec.size(); i++, j++) { shifted_vec[i] = original_vec[j]; } return 0; } int JLSRTP::xorVector(std::vector &a, std::vector &b, std::vector &result) { int retVal = -1; if (a.size() == b.size()) { result.clear(); result.resize(a.size(), 0); std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::bit_xor()); retVal = 0; } else { retVal = -1; } return retVal; } int JLSRTP::isBigEndian() { Conversion32 bint = {0x01020304}; return (bint.c[0] == 0x01); } int JLSRTP::isLittleEndian() { Conversion32 bint = {0x01020304}; return (bint.c[0] == 0x04); } int JLSRTP::convertSsrc(unsigned long ssrc, std::vector &result) { Conversion32 exchange_ssrc = {ssrc}; result.clear(); result.resize(16, 0); if (isLittleEndian()) { result[12] = exchange_ssrc.c[3]; result[13] = exchange_ssrc.c[2]; result[14] = exchange_ssrc.c[1]; result[15] = exchange_ssrc.c[0]; } else { result[12] = exchange_ssrc.c[0]; result[13] = exchange_ssrc.c[1]; result[14] = exchange_ssrc.c[2]; result[15] = exchange_ssrc.c[3]; } return 0; } int JLSRTP::convertPacketIndex(unsigned long long i, std::vector &result) { Conversion64 exchange_i = {i}; result.clear(); result.resize(16, 0); if (isLittleEndian()) { result[8] = exchange_i.c[7]; result[9] = exchange_i.c[6]; result[10] = exchange_i.c[5]; result[11] = exchange_i.c[4]; result[12] = exchange_i.c[3]; result[13] = exchange_i.c[2]; result[14] = exchange_i.c[1]; result[15] = exchange_i.c[0]; } else { result[8] = exchange_i.c[0]; result[9] = exchange_i.c[1]; result[10] = exchange_i.c[2]; result[11] = exchange_i.c[3]; result[12] = exchange_i.c[4]; result[13] = exchange_i.c[5]; result[14] = exchange_i.c[6]; result[15] = exchange_i.c[7]; } return 0; } int JLSRTP::convertROC(unsigned long ROC, std::vector &result) { Conversion32 exchange_roc = {ROC}; result.clear(); result.resize(4, 0); if (isLittleEndian()) { result[0] = exchange_roc.c[3]; result[1] = exchange_roc.c[2]; result[2] = exchange_roc.c[1]; result[3] = exchange_roc.c[0]; } else { result[0] = exchange_roc.c[0]; result[1] = exchange_roc.c[1]; result[2] = exchange_roc.c[2]; result[3] = exchange_roc.c[3]; } return 0; return 0; } unsigned long JLSRTP::determineV(unsigned short SEQ) { unsigned long v = 0; if (_s_l < 32768) { if ((SEQ - _s_l) > 32768) { v = _ROC-1; } else { v = _ROC; } } else { if ((SEQ - _s_l) < -32768) { v = _ROC+1; } else { v = _ROC; } } return v; } bool JLSRTP::updateRollOverCounter(unsigned long v) { _ROC = v; return true; } unsigned long JLSRTP::fetchRollOverCounter() { return _ROC; } bool JLSRTP::updateSL(unsigned short s) { _s_l = s; return true; } unsigned short JLSRTP::fetchSL() { return _s_l; } unsigned long long JLSRTP::determinePacketIndex(unsigned long ROC, unsigned short SEQ) { return ((JLSRTP_MAX_SEQUENCE_NUMBERS * ROC) + SEQ); } int JLSRTP::setPacketIV() { unsigned int ivSize = 0; ivSize = _packetIV.size(); assert(ivSize == JLSRTP_SALTING_KEY_LENGTH); if (ivSize == JLSRTP_SALTING_KEY_LENGTH) { // Copy 'IV' into high-order bytes [0..13] -- low-order bytes [14..15] remain zero memcpy(_cipherstate.ivec, _packetIV.data(), _packetIV.size()); return 0; } else { return -1; } } int JLSRTP::computePacketIV(unsigned long long i) { std::vector padded_salt; std::vector ssrc_vec; std::vector i_vec; std::vector shifted_ssrc; std::vector shifted_i; std::vector intermediate; unsigned long ssrc = _id.ssrc; // SSRC unsigned int saltSize = 0; _packetIV.clear(); saltSize = _session_salt_key.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { padded_salt = _session_salt_key; padded_salt.push_back(0x00); // 1-byte PAD padded_salt.push_back(0x00); // 1-byte PAD convertSsrc(ssrc, ssrc_vec); convertPacketIndex(i, i_vec); shiftVectorLeft(shifted_ssrc, ssrc_vec, 8); shiftVectorLeft(shifted_i, i_vec, 2); xorVector(padded_salt, shifted_ssrc, intermediate); xorVector(intermediate, shifted_i, _packetIV); // Truncate output IV to 14 bytes _packetIV.resize(14); return 0; } else { return -1; } } void JLSRTP::displayPacketIV() { printf("packet_iv : ["); for (unsigned int i = 0; i < _packetIV.size(); i++) { printf("%02x", _packetIV[i]); } printf("]\n"); } int JLSRTP::encryptVector(std::vector &invdata, std::vector &ciphertext_output) { int retVal = 0; assert(!invdata.empty()); if (!invdata.empty()) { switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.cipher_algorithm) { case AES_CM_128: { ciphertext_output.resize(invdata.size(), 0); resetCipherBlockOffset(); resetCipherOutputBlock(); resetCipherBlockCounter(); AES_ctr128_session_EVPencrypt(invdata.data(), ciphertext_output.data(), invdata.size(), _cipherstate.ivec, _cipherstate.ecount, &_cipherstate.num); retVal = 0; } break; case NULL_CIPHER: { ciphertext_output = invdata; retVal = 0; } break; default: { retVal = -3; } break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.cipher_algorithm) { case AES_CM_128: { ciphertext_output.resize(invdata.size(), 0); resetCipherBlockOffset(); resetCipherOutputBlock(); resetCipherBlockCounter(); AES_ctr128_session_EVPencrypt(invdata.data(), ciphertext_output.data(), invdata.size(), _cipherstate.ivec, _cipherstate.ecount, &_cipherstate.num); retVal = 0; } break; case NULL_CIPHER: { ciphertext_output = invdata; retVal = 0; } break; default: { retVal = -3; } break; } } break; default: { retVal = -4; } break; } } else { retVal = -1; } return retVal; } int JLSRTP::decryptVector(std::vector &ciphertext_input, std::vector &outvdata) { int retVal = 0; assert(!ciphertext_input.empty()); if (!ciphertext_input.empty()) { switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.cipher_algorithm) { case AES_CM_128: { outvdata.resize(ciphertext_input.size(), 0); resetCipherBlockOffset(); resetCipherOutputBlock(); resetCipherBlockCounter(); AES_ctr128_session_EVPencrypt(ciphertext_input.data(), outvdata.data(), ciphertext_input.size(), _cipherstate.ivec, _cipherstate.ecount, &_cipherstate.num); retVal = 0; } break; case NULL_CIPHER: { outvdata = ciphertext_input; retVal = 0; } break; default: { retVal = -3; } break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.cipher_algorithm) { case AES_CM_128: { outvdata.resize(ciphertext_input.size(), 0); resetCipherBlockOffset(); resetCipherOutputBlock(); resetCipherBlockCounter(); AES_ctr128_session_EVPencrypt(ciphertext_input.data(), outvdata.data(), ciphertext_input.size(), _cipherstate.ivec, _cipherstate.ecount, &_cipherstate.num); retVal = 0; } break; case NULL_CIPHER: { outvdata = ciphertext_input; retVal = 0; } break; default: { retVal = -3; } break; } } break; default: { retVal = -4; } break; } } else { retVal = -1; } return retVal; } int JLSRTP::issueAuthenticationTag(std::vector &data, std::vector &hash) { unsigned char* digest = NULL; int retVal = -1; std::vector auth_portion; std::vector rocVec; int rc = -1; assert(!_session_auth_key.empty()); if (!_session_auth_key.empty()) { rc = convertROC(_ROC, rocVec); if (rc == 0) { auth_portion.clear(); auth_portion.insert(auth_portion.end(), data.begin(), data.end()); auth_portion.insert(auth_portion.end(), rocVec.begin(), rocVec.end()); hash.clear(); digest = HMAC(EVP_sha1(), _session_auth_key.data(), _session_auth_key.size(), /*data.data()*/ auth_portion.data(), /*data.size()*/ auth_portion.size(), NULL, NULL); if (digest != NULL) { hash.assign(digest, digest+JLSRTP_SHA1_HASH_LENGTH); switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.hmac_algorithm) { case HMAC_SHA1_80: hash.resize(JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80); // Truncate to 10 bytes (80 bits / 8 bits/byte = 10 bytes) retVal = 0; break; case HMAC_SHA1_32: hash.resize(JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32); // Truncate to 4 bytes (32 bits / 8 bits/byte = 4 bytes) retVal = 0; break; default: // Unrecognized input value -- NO-OP... retVal = -3; break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.hmac_algorithm) { case HMAC_SHA1_80: hash.resize(JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80); // Truncate to 10 bytes (80 bits / 8 bits/byte = 10 bytes) retVal = 0; break; case HMAC_SHA1_32: hash.resize(JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32); // Truncate to 4 bytes (32 bits / 8 bits/byte = 4 bytes) retVal = 0; break; default: // Unrecognized input value -- NO-OP... retVal = -3; break; } } break; default: { retVal = -5; } break; } } else { retVal = -2; } } else { retVal = -4; } } else { retVal = -1; } return retVal; } int JLSRTP::extractAuthenticationTag(std::vector srtp_packet, std::vector &hash) { int retVal = -1; std::vector::iterator it = srtp_packet.begin(); int authtag_pos = 0; assert(!_session_auth_key.empty()); if (!_session_auth_key.empty()) { switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.hmac_algorithm) { case HMAC_SHA1_80: if (srtp_packet.size() >= JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80) { authtag_pos = srtp_packet.size() - 10; std::advance(it, authtag_pos); hash.assign(it, srtp_packet.end()); // Fetch trailing 10 bytes (80 bits / 8 bits/byte = 10 bytes) retVal = 0; } else { retVal = -2; } break; case HMAC_SHA1_32: if (srtp_packet.size() >= JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32) { authtag_pos = srtp_packet.size() - 4; std::advance(it, authtag_pos); hash.assign(it, srtp_packet.end()); // Fetch trailing 4 bytes (32 bits / 8 bits/byte = 4 bytes) retVal = 0; } else { retVal = -2; } break; default: // Unrecognized input value -- NO-OP... retVal = -3; break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.hmac_algorithm) { case HMAC_SHA1_80: if (srtp_packet.size() >= JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80) { authtag_pos = srtp_packet.size() - 10; std::advance(it, authtag_pos); hash.assign(it, srtp_packet.end()); // Fetch trailing 10 bytes (80 bits / 8 bits/byte = 10 bytes) retVal = 0; } else { retVal = -2; } break; case HMAC_SHA1_32: if (srtp_packet.size() >= JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32) { authtag_pos = srtp_packet.size() - 4; std::advance(it, authtag_pos); hash.assign(it, srtp_packet.end()); // Fetch trailing 4 bytes (32 bits / 8 bits/byte = 4 bytes) retVal = 0; } else { retVal = -2; } break; default: // Unrecognized input value -- NO-OP... retVal = -3; break; } } break; default: { retVal = -4; } break; } } else { retVal = -1; } return retVal; } int JLSRTP::extractSRTPHeader(std::vector srtp_packet, std::vector &header) { int retVal = -1; std::vector::iterator it = srtp_packet.begin(); if (_srtp_header_size > 0) { if (srtp_packet.size() >= _srtp_header_size) { header.clear(); std::advance(it, _srtp_header_size); header.assign(srtp_packet.begin(), it); // Fetch leading 12 bytes retVal = 0; } else { retVal = -2; } } else { retVal = -1; } return retVal; } int JLSRTP::extractSRTPPayload(std::vector srtp_packet, std::vector &payload) { int retVal = -1; std::vector::iterator it_payload_begin = srtp_packet.begin(); std::vector::iterator it_payload_end = srtp_packet.begin(); unsigned int header_payload_size = 0; header_payload_size = _srtp_header_size + _srtp_payload_size; if (_srtp_header_size > 0) { if (_srtp_payload_size > 0) { if (srtp_packet.size() >= header_payload_size) { payload.clear(); std::advance(it_payload_begin, _srtp_header_size); std::advance(it_payload_end, header_payload_size); payload.assign(it_payload_begin, it_payload_end); // Fetch payload bytes retVal = 0; } else { retVal = -3; } } else { retVal = -2; } } else { retVal = -1; } return retVal; } std::string JLSRTP::base64Encode(std::vector const& s) { int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; unsigned char const* bytes_to_encode = &s.front(); unsigned int in_len = s.size(); std::string ret; while (in_len--) { char_array_3[i++] = *(bytes_to_encode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) { ret += base64Chars[char_array_4[i]]; } i = 0; } } if (i) { for(j = i; j < 3; j++) { char_array_3[j] = '\0'; } char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) { ret += base64Chars[char_array_4[j]]; } while((i++ < 3)) { ret += '='; } } return ret; } std::vector JLSRTP::base64Decode(std::string const& encoded_string) { int i = 0; int j = 0; unsigned char char_array_4[4]; unsigned char char_array_3[3]; int in_ = 0; unsigned int in_len = encoded_string.size(); std::vector ret; while (in_len-- && ( encoded_string[in_] != '=') && isBase64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) { char_array_4[i] = base64Chars.find(char_array_4[i]); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) { ret.push_back(char_array_3[i]); } i = 0; } } if (i) { for (j = i; j <4; j++) { char_array_4[j] = 0; } for (j = 0; j <4; j++) { char_array_4[j] = base64Chars.find(char_array_4[j]); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) { ret.push_back(char_array_3[j]); } } return ret; } int JLSRTP::resetCipherBlockOffset() { _cipherstate.num = 0; return 0; } int JLSRTP::resetCipherOutputBlock() { memset(_cipherstate.ecount, 0, sizeof(_cipherstate.ecount)); return 0; } int JLSRTP::resetCipherBlockCounter() { // Clear low-order bytes [14..15] for 'counter' memset(_cipherstate.ivec+14, 0, 2); return 0; } int JLSRTP::setAESPseudoRandomFunctionKey(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { ActiveCrypto active_crypto = INVALID_CRYPTO; int rc = 0; int retVal = 0; if (_pseudorandomstate.cipher != NULL) { if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { rc = EVP_EncryptInit_ex(_pseudorandomstate.cipher, NULL, NULL, _primary_crypto.master_key.data(), NULL); if (rc == 1) { retVal = 0; } else { retVal = -2; } } break; case SECONDARY_CRYPTO: { rc = EVP_EncryptInit_ex(_pseudorandomstate.cipher, NULL, NULL, _secondary_crypto.master_key.data(), NULL); if (rc == 1) { retVal = 0; } else { retVal = -3; } } break; default: { retVal = -4; } break; } } else { retVal = -1; } return retVal; } int JLSRTP::setAESSessionEncryptionKey() { int rc = 0; int retVal = 0; if (_cipherstate.cipher != NULL) { rc = EVP_EncryptInit_ex(_cipherstate.cipher, NULL, NULL, _session_enc_key.data(), NULL); if (rc == 1) { retVal = 0; } else { retVal = -2; } } else { retVal = -1; } return retVal; } void JLSRTP::AES_ctr128_increment(unsigned char* counter) { unsigned char* cur_pos = NULL; for (cur_pos = counter + 15; cur_pos >= counter; cur_pos--) { (*cur_pos)++; if (*cur_pos != 0) { break; } } } int JLSRTP::AES_ctr128_pseudorandom_EVPencrypt(const unsigned char* in, unsigned char* out, const unsigned long length, unsigned char counter[AES_BLOCK_SIZE], unsigned char ecount_buf[AES_BLOCK_SIZE], unsigned int* num) { int nb; unsigned int n; unsigned long l=length; int rc = 0; int retVal = 0; switch (_active_crypto) { case PRIMARY_CRYPTO: { n = *num; while (l--) { if (n == 0) { // IMPORTANT: Key MUST be set every single time EVP_EncryptUpdate() is to be called... rc = EVP_EncryptInit_ex(_pseudorandomstate.cipher, NULL, NULL, _primary_crypto.master_key.data(), NULL); if (rc == 1) { rc = EVP_EncryptUpdate(_pseudorandomstate.cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE); if (rc == 1) { retVal = 0; AES_ctr128_increment(counter); } else { retVal = -2; break; } } else { retVal = -1; break; } } *(out++) = *(in++) ^ ecount_buf[n]; n = (n+1) % AES_BLOCK_SIZE; } *num=n; } break; case SECONDARY_CRYPTO: { n = *num; while (l--) { if (n == 0) { // IMPORTANT: Key MUST be set every single time EVP_EncryptUpdate() is to be called... rc = EVP_EncryptInit_ex(_pseudorandomstate.cipher, NULL, NULL, _secondary_crypto.master_key.data(), NULL); if (rc == 1) { rc = EVP_EncryptUpdate(_pseudorandomstate.cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE); if (rc == 1) { retVal = 0; AES_ctr128_increment(counter); } else { retVal = -2; break; } } else { retVal = -1; break; } } *(out++) = *(in++) ^ ecount_buf[n]; n = (n+1) % AES_BLOCK_SIZE; } *num=n; } break; default: { retVal = -3; } break; } return retVal; } int JLSRTP::AES_ctr128_session_EVPencrypt(const unsigned char* in, unsigned char* out, const unsigned long length, unsigned char counter[AES_BLOCK_SIZE], unsigned char ecount_buf[AES_BLOCK_SIZE], unsigned int* num) { int nb; unsigned int n; unsigned long l=length; int rc = 0; int retVal = 0; n = *num; while (l--) { if (n == 0) { // IMPORTANT: Key MUST be set every single time EVP_EncryptUpdate() is to be called... rc = EVP_EncryptInit_ex(_cipherstate.cipher, NULL, NULL, _session_enc_key.data(), NULL); if (rc == 1) { rc = EVP_EncryptUpdate(_cipherstate.cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE); if (rc == 1) { retVal = 0; AES_ctr128_increment(counter); } else { retVal = -2; break; } } else { retVal = -1; break; } } *(out++) = *(in++) ^ ecount_buf[n]; n = (n+1) % AES_BLOCK_SIZE; } *num=n; return retVal; } // --------------- PUBLIC METHODS ---------------- void JLSRTP::resetCryptoContext(unsigned int ssrc, std::string ipAddress, unsigned short port) { _id.ssrc = ssrc; _id.address = ipAddress; _id.port = port; _ROC = 0; _s_l = 0; _primary_crypto.cipher_algorithm = AES_CM_128; _primary_crypto.hmac_algorithm = HMAC_SHA1_80; _primary_crypto.MKI = 0; _primary_crypto.MKI_length = 0; _primary_crypto.active_MKI = 0; _primary_crypto.master_key.resize(JLSRTP_ENCRYPTION_KEY_LENGTH, 0); _primary_crypto.master_key_counter = 0; _primary_crypto.n_e = _primary_crypto.master_key.size(); _primary_crypto.n_a = JLSRTP_AUTHENTICATION_KEY_LENGTH; _primary_crypto.master_salt.resize(JLSRTP_SALTING_KEY_LENGTH, 0); _primary_crypto.master_key_derivation_rate = 0; _primary_crypto.master_mki_value = 0; _primary_crypto.n_s = _primary_crypto.master_salt.size(); _primary_crypto.tag = 0; _secondary_crypto.cipher_algorithm = AES_CM_128; _secondary_crypto.hmac_algorithm = HMAC_SHA1_80; _secondary_crypto.MKI = 0; _secondary_crypto.MKI_length = 0; _secondary_crypto.active_MKI = 0; _secondary_crypto.master_key.resize(JLSRTP_ENCRYPTION_KEY_LENGTH, 0); _secondary_crypto.master_key_counter = 0; _secondary_crypto.n_e = _secondary_crypto.master_key.size(); _secondary_crypto.n_a = JLSRTP_AUTHENTICATION_KEY_LENGTH; _secondary_crypto.master_salt.resize(JLSRTP_SALTING_KEY_LENGTH, 0); _secondary_crypto.master_key_derivation_rate = 0; _secondary_crypto.master_mki_value = 0; _secondary_crypto.n_s = _secondary_crypto.master_salt.size(); _secondary_crypto.tag = 0; _session_enc_key.resize(JLSRTP_ENCRYPTION_KEY_LENGTH, 0); _session_salt_key.resize(JLSRTP_SALTING_KEY_LENGTH, 0); _session_auth_key.resize(JLSRTP_AUTHENTICATION_KEY_LENGTH, 0); _packetIV.resize(JLSRTP_SALTING_KEY_LENGTH, 0); memset(_pseudorandomstate.ivec, 0, sizeof(_pseudorandomstate.ivec)); _pseudorandomstate.num = 0; memset(_pseudorandomstate.ecount, 0, sizeof(_pseudorandomstate.ecount)); memset(_cipherstate.ivec, 0, sizeof(_cipherstate.ivec)); _cipherstate.num = 0; memset(_cipherstate.ecount, 0, sizeof(_cipherstate.ecount)); _srtp_header_size = JLSRTP_SRTP_DEFAULT_HEADER_SIZE; _srtp_payload_size = 0; _active_crypto = PRIMARY_CRYPTO; } int JLSRTP::resetCipherState() { unsigned int ivSize = 0; ivSize = _packetIV.size(); assert(ivSize == JLSRTP_SALTING_KEY_LENGTH); if (ivSize == JLSRTP_SALTING_KEY_LENGTH) { // aes_ctr128_encrypt() requires 'num' and 'ecount' to be set to zero on the first call resetCipherBlockOffset(); resetCipherOutputBlock(); // Clear BOTH high-order bytes [0..13] for 'IV' AND low-order bytes [14..15] for 'counter' memset(_cipherstate.ivec, 0, AES_BLOCK_SIZE); // Copy 'IV' into high-order bytes [0..13] -- low-order bytes [14..15] remain zero memcpy(_cipherstate.ivec, _packetIV.data(), _packetIV.size()); return 0; } else { return -1; } } int JLSRTP::deriveSessionEncryptionKey() { std::vector input_vector; // Input vector (built from applicable keyid_XXXs) std::vector keyid_encryption; unsigned int saltSize = 0; int retVal = -1; switch (_active_crypto) { case PRIMARY_CRYPTO: { saltSize = _primary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_encryption.clear(); keyid_encryption.resize(7, 0); keyid_encryption.push_back(JLSRTP_KEY_ENCRYPTION_LABEL); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); xorVector(keyid_encryption, _primary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 128, _session_enc_key); } else { retVal = -1; } } break; case SECONDARY_CRYPTO: { saltSize = _secondary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_encryption.clear(); keyid_encryption.resize(7, 0); keyid_encryption.push_back(JLSRTP_KEY_ENCRYPTION_LABEL); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); keyid_encryption.push_back(0x00); xorVector(keyid_encryption, _secondary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 128, _session_enc_key); } else { retVal = -1; } } break; default: { retVal = -4; } break; } return retVal; } int JLSRTP::deriveSessionSaltingKey() { std::vector input_vector; // Input vector (built from applicable keyid_XXXs) std::vector keyid_salting; unsigned int saltSize = 0; int retVal = -1; switch (_active_crypto) { case PRIMARY_CRYPTO: { saltSize = _primary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_salting.clear(); keyid_salting.resize(7, 0); keyid_salting.push_back(JLSRTP_KEY_SALTING_LABEL); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); xorVector(keyid_salting, _primary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 112, _session_salt_key); } else { retVal = -1; } } break; case SECONDARY_CRYPTO: { saltSize = _secondary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_salting.clear(); keyid_salting.resize(7, 0); keyid_salting.push_back(JLSRTP_KEY_SALTING_LABEL); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); keyid_salting.push_back(0x00); xorVector(keyid_salting, _secondary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 112, _session_salt_key); } else { retVal = -1; } } break; default: { retVal = -4; } break; } return retVal; } int JLSRTP::deriveSessionAuthenticationKey() { std::vector input_vector; // Input vector (built from applicable keyid_XXXs) std::vector keyid_authentication; unsigned int saltSize = 0; int retVal = -1; switch (_active_crypto) { case PRIMARY_CRYPTO: { saltSize = _primary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_authentication.clear(); keyid_authentication.resize(7, 0); keyid_authentication.push_back(JLSRTP_KEY_AUTHENTICATION_LABEL); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); xorVector(keyid_authentication, _primary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 160, _session_auth_key); } else { retVal = -1; } } break; case SECONDARY_CRYPTO: { saltSize = _secondary_crypto.master_salt.size(); assert(saltSize == JLSRTP_SALTING_KEY_LENGTH); if (saltSize == JLSRTP_SALTING_KEY_LENGTH) { input_vector.clear(); keyid_authentication.clear(); keyid_authentication.resize(7, 0); keyid_authentication.push_back(JLSRTP_KEY_AUTHENTICATION_LABEL); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); keyid_authentication.push_back(0x00); xorVector(keyid_authentication, _secondary_crypto.master_salt, input_vector); retVal = pseudorandomFunction(input_vector, 160, _session_auth_key); } else { retVal = -1; } } break; default: { retVal = -4; } break; } return retVal; } void JLSRTP::displaySessionEncryptionKey() { //printf("session_encryption_key[] size: %d\n", _session_enc_key.size()); printf("_session_enc_key : ["); for (unsigned int i = 0; i < _session_enc_key.size(); i++) { printf("%02x", _session_enc_key[i]); } printf("]\n"); } void JLSRTP::displaySessionSaltingKey() { //printf("session_salting_key[] size: %d\n", _session_salt_key.size()); printf("_session_salt_key : ["); for (unsigned int i = 0; i < _session_salt_key.size(); i++) { printf("%02x", _session_salt_key[i]); } printf("]\n"); } void JLSRTP::displaySessionAuthenticationKey() { //printf("session_authentication_key[] size: %d\n", _session_auth_key.size()); printf("_session_auth_key : ["); for (unsigned int i = 0; i < _session_auth_key.size(); i++) { printf("%02x", _session_auth_key[i]); } printf("]\n"); } int JLSRTP::selectEncryptionKey() { int rc = 0; assert(!_session_enc_key.empty()); if (!_session_enc_key.empty()) { rc = setAESSessionEncryptionKey(); if (rc == 0) { return 0; } return -2; } else { return -1; } } int JLSRTP::selectDecryptionKey() { int rc = 0; assert(!_session_enc_key.empty()); if (!_session_enc_key.empty()) { rc = setAESSessionEncryptionKey(); if (rc == 0) { return 0; } return -2; } else { return -1; } } CipherType JLSRTP::getCipherAlgorithm(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { CipherType retVal = INVALID_CIPHER; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { retVal = _primary_crypto.cipher_algorithm; } break; case SECONDARY_CRYPTO: { retVal = _secondary_crypto.cipher_algorithm; } break; default: { retVal = INVALID_CIPHER; } break; } return retVal; } int JLSRTP::selectCipherAlgorithm(CipherType cipherType, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { switch (cipherType) { case AES_CM_128: { _primary_crypto.cipher_algorithm = AES_CM_128; retVal = 0; } break; case NULL_CIPHER: { _primary_crypto.cipher_algorithm = NULL_CIPHER; retVal = 0; } break; default: { retVal = -1; } break; } } break; case SECONDARY_CRYPTO: { switch (cipherType) { case AES_CM_128: { _secondary_crypto.cipher_algorithm = AES_CM_128; retVal = 0; } break; case NULL_CIPHER: { _secondary_crypto.cipher_algorithm = NULL_CIPHER; retVal = 0; } break; default: { retVal = -1; } break; } } break; default: { retVal = -2; } break; } return retVal; } HashType JLSRTP::getHashAlgorithm(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { HashType retVal = INVALID_HASH; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { retVal = _primary_crypto.hmac_algorithm; } break; case SECONDARY_CRYPTO: { retVal = _secondary_crypto.hmac_algorithm; } break; default: { retVal = INVALID_HASH; } break; } return retVal; } int JLSRTP::selectHashAlgorithm(HashType hashType, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { switch (hashType) { case HMAC_SHA1_80: { _primary_crypto.hmac_algorithm = HMAC_SHA1_80; retVal = 0; } break; case HMAC_SHA1_32: { _primary_crypto.hmac_algorithm = HMAC_SHA1_32; retVal = 0; } break; default: { retVal = -1; } break; } } break; case SECONDARY_CRYPTO: { switch (hashType) { case HMAC_SHA1_80: { _secondary_crypto.hmac_algorithm = HMAC_SHA1_80; retVal = 0; } break; case HMAC_SHA1_32: { _secondary_crypto.hmac_algorithm = HMAC_SHA1_32; retVal = 0; } break; default: { retVal = -1; } break; } } break; default: { retVal = -2; } break; } return retVal; } int JLSRTP::getAuthenticationTagSize() { int retVal = -1; assert(!_session_auth_key.empty()); if (!_session_auth_key.empty()) { switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.hmac_algorithm) { case HMAC_SHA1_80: retVal = JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80; break; case HMAC_SHA1_32: retVal = JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32; break; default: // Unrecognized input value -- NO-OP... retVal = -2; break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.hmac_algorithm) { case HMAC_SHA1_80: retVal = JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_80; break; case HMAC_SHA1_32: retVal = JLSRTP_AUTHENTICATION_TAG_SIZE_SHA1_32; break; default: // Unrecognized input value -- NO-OP... retVal = -2; break; } } break; default: { retVal = -3; } break; } } else { retVal = -1; } return retVal; } void JLSRTP::displayAuthenticationTag(std::vector &authtag) { printf("authentication tag : ["); for (unsigned int i = 0; i < authtag.size(); i++) { printf("%02x", authtag[i]); } printf("]\n"); } unsigned int JLSRTP::getSSRC() { return _id.ssrc; } std::string JLSRTP::getIPAddress() { return _id.address; } unsigned short JLSRTP::getPort() { return _id.port; } void JLSRTP::setSSRC(unsigned int ssrc) { _id.ssrc = ssrc; } void JLSRTP::setIPAddress(std::string ipAddress) { _id.address = ipAddress; } void JLSRTP::setPort(unsigned short port) { _id.port = port; } void JLSRTP::setID(CryptoContextID id) { _id.ssrc = id.ssrc; _id.address = id.address; _id.port = id.port; } unsigned int JLSRTP::getSrtpHeaderSize() { return _srtp_header_size; } void JLSRTP::setSrtpHeaderSize(unsigned int size) { _srtp_header_size = size; } unsigned int JLSRTP::getSrtpPayloadSize() { return _srtp_payload_size; } void JLSRTP::setSrtpPayloadSize(unsigned int size) { _srtp_payload_size = size; } int JLSRTP::processOutgoingPacket(unsigned short SEQ_s, std::vector &rtp_header, std::vector &rtp_payload, std::vector &srtp_packet) { int rc = 0; bool check = false; unsigned long v_s = 0; unsigned long long i_s = 0LL; /* TEST PACKET INDEX */ std::vector srtp_payload; /* ENCRYPTED PAYLOAD */ std::vector auth_tag; std::vector auth_portion; int retVal = -1; // 1. Determine crypto context to use // NO-OP (IMPLICIT) // 2. Determine packet index (i) using RoC + _s_l + SEQ (section 3.3.1) //std::cout << "[processOutgoingPacket] SEQ_s: " << SEQ_s << " current ROC_s: " << _ROC << " current s_l_s: " << _s_l << " "; v_s = determineV(SEQ_s); //std::cout << "v_s: " << v_s << " "; i_s = determinePacketIndex(v_s, SEQ_s); //std::cout << "i_s: " << i_s << std::endl; // 3. Determine master key / master salt using packet index (i) OR MKI (section 8.1) // NO-OP -- MASTER KEY / MASTER SALT ASSUMED TO BE UNIQUE WITHIN CONTEXT // 4. Determine session key / session salt (section 4.3) using master key + master salt + key_derivation_rate + session key-lengths + packet index (i) // NO-OP -- SESSION KEY / SESSION SALT ALREADY DETERMINED AT THIS POINT // 5. Encrypt PAYLOAD to produce encrypted portion (section 4.1) using encryption algorithm + session encryption key + session salting key + packet index (i) rc = computePacketIV(i_s); if (rc == 0) { rc = setPacketIV(); if (rc == 0) { rc = encryptVector(rtp_payload, srtp_payload); if (rc == 0) { //printf("[processOutgoingPacket] CIPHERTEXT: ["); //for (unsigned int i = 0; i < srtp_payload.size(); i++) { // printf("%02x", srtp_payload[i]); //} //printf("]\n"); auth_portion.insert(auth_portion.end(), rtp_header.begin(), rtp_header.end()); auth_portion.insert(auth_portion.end(), srtp_payload.begin(), srtp_payload.end()); // 6. If MKI is 1 then append MKI to packet // NO-OP -- MKI NOT USED // 7A. Compute authentication tag from authenticated portion of the packet (section 4.2) using RoC + authentication algorithm + session authentication key rc = issueAuthenticationTag(auth_portion, auth_tag); if (rc == 0) { // 7B. Append authentication tag to the packet to produce encrypted+authenticated portion srtp_packet.clear(); srtp_packet.insert(srtp_packet.end(), auth_portion.begin(), auth_portion.end()); srtp_packet.insert(srtp_packet.end(), auth_tag.begin(), auth_tag.end()); // 8. If necessary update RoC (section 3.3.1) using packet index (i) check = updateRollOverCounter(v_s); if (check) { check = updateSL(SEQ_s); if (check) { retVal = 0; } else { retVal = -6; } } else { retVal = -5; } } else { retVal = -2; } } else { retVal = -1; // ENCRYPTION FAILURE } } else { retVal = -4; } } else { retVal = -3; } return retVal; } int JLSRTP::processIncomingPacket(unsigned short SEQ_r, std::vector &srtp_packet, std::vector &rtp_header, std::vector &rtp_payload) { int rc = 0; bool check = false; unsigned long v_r = 0; unsigned long long i_r = 0LL; /* TEST PACKET INDEX */ std::vector auth_tag_generated; std::vector auth_portion; std::vector auth_tag_received; std::vector srtp_payload; /* ENCRYPTED PAYLOAD */ int retVal = -1; rtp_header.clear(); rtp_payload.clear(); // 1. Determine crypto context to use // NO-OP (IMPLICIT) // 2. Determine packet index (i) using RoC + _s_l (section 3.3.1) //std::cout << "[processIncomingPacket] SEQ_r: " << SEQ_r << " current ROC_r: " << _ROC << " current s_l_r: " << _s_l << " "; v_r = determineV(SEQ_r); //std::cout << "v_r: " << v_r << " "; i_r = determinePacketIndex(v_r, SEQ_r); //std::cout << "i_r: " << i_r << " " << std::endl; // 3. Determine master key / master salt -- if MKI is 1 then use MKI in packet otherwise use packet index (i) (section 8.1) // NO-OP -- MASTER KEY / MASTER SALT ASSUMED TO BE UNIQUE WITHIN CONTEXT // 4. Determine session key / session salt (section 4.3) using master key + master salt + key_derivation_rate + session key-lengths + packet index (i) // NO-OP -- SESSION KEY / SESSION SALT ALREADY DETERMINED AT THIS POINT // 5A. Check if packet has been replayed (section 3.3.2) using replay list + packet index (i) -- discard packet if replayed // NO-OP -- REPLAY LIST NOT USED // 5B. Verify authentication tag using RoC + authentication algorithm + session authentication key -- if authentication failure (section 4.2) discard packet rc = extractAuthenticationTag(srtp_packet, auth_tag_received); if (rc == 0) { rc = extractSRTPHeader(srtp_packet, rtp_header); if (rc == 0) { rc = extractSRTPPayload(srtp_packet, srtp_payload); if (rc == 0) { auth_portion.insert(auth_portion.end(), rtp_header.begin(), rtp_header.end()); auth_portion.insert(auth_portion.end(), srtp_payload.begin(), srtp_payload.end()); rc = issueAuthenticationTag(auth_portion, auth_tag_generated); if (rc == 0) { if (auth_tag_received == auth_tag_generated) { // 6. Decrypt PAYLOAD (section 4.1) using decryption algorithm + session encryption key + session salting key //printf("[processIncomingPacket] CIPHERTEXT: ["); //for (unsigned int i = 0; i < srtp_payload.size(); i++) { // printf("%02x", srtp_payload[i]); //} //printf("]\n"); rc = computePacketIV(i_r); if (rc == 0) { rc = setPacketIV(); if (rc == 0) { rc = decryptVector(srtp_payload, rtp_payload); if (rc == 0) { // 7A. Update RoC / _s_l (section 3.3.1) using estimated packet index (i) check = updateRollOverCounter(v_r); if (check) { check = updateSL(SEQ_r); if (check) { // 7B. Update replay list if applicable (section 3.3.2) // NO-OP -- REPLAY LIST NOT USED // 8. Remove MKI + authentication tag fields from packet if present // NO-OP -- AUTOMATICALLY DONE BY PREVIOUS RTP HEADER+PAYLOAD EXTRACTION retVal = 0; } else { retVal = -10; } } else { retVal = -9; } } else { retVal = -2; // DECRYPTION FAILURE } } else { retVal = -8; } } else { retVal = -7; } } else { retVal = -1; // AUTHENTICATION FAILURE } } else { retVal = -6; } } else { retVal = -5; } } else { retVal = -4; } } else { retVal = -3; } return retVal; } int JLSRTP::setCryptoTag(unsigned int tag, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { _primary_crypto.tag = tag; retVal = 0; } break; case SECONDARY_CRYPTO: { _secondary_crypto.tag = tag; retVal = 0; } break; default: { retVal = -1; } break; } return retVal; } unsigned int JLSRTP::getCryptoTag(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { return _primary_crypto.tag; } break; case SECONDARY_CRYPTO: { return _secondary_crypto.tag; } break; default: { retVal = -1; } break; } return retVal; } std::string JLSRTP::getCryptoSuite() { std::string cryptosuite; switch (_active_crypto) { case PRIMARY_CRYPTO: { switch (_primary_crypto.cipher_algorithm) { case AES_CM_128: { switch (_primary_crypto.hmac_algorithm) { case HMAC_SHA1_80: { cryptosuite = "AES_CM_128_HMAC_SHA1_80"; } break; case HMAC_SHA1_32: { cryptosuite = "AES_CM_128_HMAC_SHA1_32"; } break; default: { cryptosuite = ""; } break; } } break; case NULL_CIPHER: { switch (_primary_crypto.hmac_algorithm) { case HMAC_SHA1_80: { cryptosuite = "NULL_HMAC_SHA1_80"; } break; case HMAC_SHA1_32: { cryptosuite = "NULL_HMAC_SHA1_32"; } break; default: { cryptosuite = ""; } break; } } break; default: { cryptosuite = ""; } break; } } break; case SECONDARY_CRYPTO: { switch (_secondary_crypto.cipher_algorithm) { case AES_CM_128: { switch (_secondary_crypto.hmac_algorithm) { case HMAC_SHA1_80: { cryptosuite = "AES_CM_128_HMAC_SHA1_80"; } break; case HMAC_SHA1_32: { cryptosuite = "AES_CM_128_HMAC_SHA1_32"; } break; default: { cryptosuite = ""; } break; } } break; case NULL_CIPHER: { switch (_secondary_crypto.hmac_algorithm) { case HMAC_SHA1_80: { cryptosuite = "NULL_HMAC_SHA1_80"; } break; case HMAC_SHA1_32: { cryptosuite = "NULL_HMAC_SHA1_32"; } break; default: { cryptosuite = ""; } break; } } break; default: { cryptosuite = ""; } break; } } break; default: { cryptosuite = ""; } break; } return cryptosuite; } int JLSRTP::encodeMasterKeySalt(std::string &mks, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; std::vector concat; mks.clear(); ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { concat.insert(concat.end(), _primary_crypto.master_key.begin(), _primary_crypto.master_key.end()); concat.insert(concat.end(), _primary_crypto.master_salt.begin(), _primary_crypto.master_salt.end()); //std::cout << "encodeMasterKeySalt(): concat:["; //for (unsigned int i = 0; i < concat.size(); i++) //{ // printf("%02X", concat[i]); //} //std::cout << "]" << std::endl; mks = base64Encode(concat); //std::cout << "encodeMasterKeySalt(): [" << mks << "]" << std::endl; retVal = 0; } break; case SECONDARY_CRYPTO: { concat.insert(concat.end(), _secondary_crypto.master_key.begin(), _secondary_crypto.master_key.end()); concat.insert(concat.end(), _secondary_crypto.master_salt.begin(), _secondary_crypto.master_salt.end()); //std::cout << "encodeMasterKeySalt(): concat:["; //for (unsigned int i = 0; i < concat.size(); i++) //{ // printf("%02X", concat[i]); //} //std::cout << "]" << std::endl; mks = base64Encode(concat); //std::cout << "encodeMasterKeySalt(): [" << mks << "]" << std::endl; retVal = 0; } break; default: { retVal = -1; } } return retVal; } int JLSRTP::decodeMasterKeySalt(std::string &mks, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; std::vector concat; int split_pos = 0; std::vector::iterator it_begin; std::vector::iterator it_middle; std::vector::iterator it_end; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { concat = base64Decode(mks); //std::cout << "decodeMasterKeySalt(): concat:["; //for (unsigned int i = 0; i < concat.size(); i++) //{ // printf("%02X", concat[i]); //} //std::cout << "]" << std::endl; split_pos = _primary_crypto.n_e; it_begin = concat.begin(); it_middle = concat.begin(); it_end = concat.end(); std::advance(it_middle, split_pos); _primary_crypto.master_key.assign(it_begin, it_middle); _primary_crypto.master_salt.assign(it_middle, it_end); //std::cout << "decodeMasterKeySalt(): _masterKey:["; //for (unsigned int i = 0; i < _primary_crypto.master_key.size(); i++) //{ // printf("%02X", _primary_crypto.master_key[i]); //} //std::cout << "] _masterSalt:["; //for (unsigned int i = 0; i < _primary_crypto.master_salt.size(); i++) //{ // printf("%02X", _primary_crypto.master_salt[i]); //} //std::cout << "]" << std::endl; retVal = 0; } break; case SECONDARY_CRYPTO: { concat = base64Decode(mks); //std::cout << "decodeMasterKeySalt(): concat:["; //for (unsigned int i = 0; i < concat.size(); i++) //{ // printf("%02X", concat[i]); //} //std::cout << "]" << std::endl; split_pos = _secondary_crypto.n_e; it_begin = concat.begin(); it_middle = concat.begin(); it_end = concat.end(); std::advance(it_middle, split_pos); _secondary_crypto.master_key.assign(it_begin, it_middle); _secondary_crypto.master_salt.assign(it_middle, it_end); //std::cout << "decodeMasterKeySalt(): _masterKey:["; //for (unsigned int i = 0; i < _secondary_crypto.master_key.size(); i++) //{ // printf("%02X", _secondary_crypto.master_key[i]); //} //std::cout << "] _masterSalt:["; //for (unsigned int i = 0; i < _secondary_crypto.master_salt.size(); i++) //{ // printf("%02X", _secondary_crypto.master_salt[i]); //} //std::cout << "]" << std::endl; retVal = 0; } break; default: { retVal = -1; } break; } return retVal; } void JLSRTP::displayCryptoContext() { std::cout << "_id : " << "(" << _id.ssrc << ", " << _id.address << ", " << _id.port << ")" << std::endl; std::cout << "_ROC : " << _ROC << std::endl; std::cout << "_s_l : " << _s_l << std::endl; std::cout << "_primary_crypto.cipher_algorithm : " << _primary_crypto.cipher_algorithm << std::endl; std::cout << "_primary_crypto.hmac_algorithm : " << _primary_crypto.hmac_algorithm << std::endl; std::cout << "_primary_crypto.MKI : " << _primary_crypto.MKI << std::endl; std::cout << "_primary_crypto.MKI_length : " << _primary_crypto.MKI_length << std::endl; std::cout << "_primary_crypto.active_MKI : " << _primary_crypto.active_MKI << std::endl; std::cout << "_primary_crypto.master_key : "; std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << std::setfill('0') << std::setw(JLSRTP_ENCRYPTION_KEY_LENGTH); for (unsigned int i = 0; i < _primary_crypto.master_key.size(); i++) { printf("%02x", _primary_crypto.master_key[i]); // std::cout << std::hex << _primary_crypto.master_key[i]; } std::cout.unsetf(std::ios::hex); std::cout << std::endl; std::cout << "_primary_crypto.master_key_counter : " << _primary_crypto.master_key_counter << std::endl; std::cout << "_primary_crypto.n_e : " << _primary_crypto.n_e << std::endl; std::cout << "_primary_crypto.n_a : " << _primary_crypto.n_a << std::endl; std::cout << "_primary_crypto.master_salt : "; std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << std::setfill('0') << std::setw(JLSRTP_SALTING_KEY_LENGTH); for (unsigned int i = 0; i < _primary_crypto.master_salt.size(); i++) { printf("%02x", _primary_crypto.master_salt[i]); // std::cout << std::hex << _primary_crypto.master_salt[i]; } std::cout.unsetf(std::ios::hex); std::cout << std::endl; std::cout << "_primary_crypto.master_key_derivation_rate : " << _primary_crypto.master_key_derivation_rate << std::endl; std::cout << "_primary_crypto.master_mki_value : " << _primary_crypto.master_mki_value << std::endl; std::cout << "_primary_crypto.n_s : " << _primary_crypto.n_s << std::endl; std::cout << "_primary_crypto.tag : " << _primary_crypto.tag << std::endl; std::cout << "_secondary_crypto.cipher_algorithm : " << _secondary_crypto.cipher_algorithm << std::endl; std::cout << "_secondary_crypto.hmac_algorithm : " << _secondary_crypto.hmac_algorithm << std::endl; std::cout << "_secondary_crypto.MKI : " << _secondary_crypto.MKI << std::endl; std::cout << "_secondary_crypto.MKI_length : " << _secondary_crypto.MKI_length << std::endl; std::cout << "_secondary_crypto.active_MKI : " << _secondary_crypto.active_MKI << std::endl; std::cout << "_secondary_crypto.master_key : "; std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << std::setfill('0') << std::setw(JLSRTP_ENCRYPTION_KEY_LENGTH); for (unsigned int i = 0; i < _secondary_crypto.master_key.size(); i++) { printf("%02x", _secondary_crypto.master_key[i]); // std::cout << std::hex << _secondary_crypto.master_key[i]; } std::cout.unsetf(std::ios::hex); std::cout << std::endl; std::cout << "_secondary_crypto.master_key_counter : " << _secondary_crypto.master_key_counter << std::endl; std::cout << "_secondary_crypto.n_e : " << _secondary_crypto.n_e << std::endl; std::cout << "_secondary_crypto.n_a : " << _secondary_crypto.n_a << std::endl; std::cout << "_secondary_crypto.master_salt : "; std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << std::setfill('0') << std::setw(JLSRTP_SALTING_KEY_LENGTH); for (unsigned int i = 0; i < _secondary_crypto.master_salt.size(); i++) { printf("%02x", _secondary_crypto.master_salt[i]); // std::cout << std::hex << _secondary_crypto.master_salt[i]; } std::cout.unsetf(std::ios::hex); std::cout << std::endl; std::cout << "_secondary_crypto.master_key_derivation_rate : " << _secondary_crypto.master_key_derivation_rate << std::endl; std::cout << "_secondary_crypto.master_mki_value : " << _secondary_crypto.master_mki_value << std::endl; std::cout << "_secondary_crypto.n_s : " << _secondary_crypto.n_s << std::endl; std::cout << "_secondary_crypto.tag : " << _secondary_crypto.tag << std::endl; printf("_session_enc_key : ["); for (unsigned int i = 0; i < _session_enc_key.size(); i++) { printf("%02x", _session_enc_key[i]); } printf("]\n"); printf("_session_salt_key : ["); for (unsigned int i = 0; i < _session_salt_key.size(); i++) { printf("%02x", _session_salt_key[i]); } printf("]\n"); printf("_session_auth_key : ["); for (unsigned int i = 0; i < _session_auth_key.size(); i++) { printf("%02x", _session_auth_key[i]); } printf("]\n"); printf("_packet_iv : ["); for (unsigned int i = 0; i < _packetIV.size(); i++) { printf("%02x", _packetIV[i]); } printf("]\n"); std::cout << "_pseudorandomstate.ivec : ["; for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { printf("%02x", _pseudorandomstate.ivec[i]); } std::cout << "]" << std::endl; std::cout << "_pseudorandomstate.num : " << _pseudorandomstate.num << std::endl; std::cout << "_pseudorandomstate.ecount : ["; for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { printf("%02x", _pseudorandomstate.ecount[i]); } std::cout << "]" << std::endl; std::cout << "_cipherstate.ivec : ["; for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { printf("%02x", _cipherstate.ivec[i]); } std::cout << "]" << std::endl; std::cout << "_cipherstate.num : " << _cipherstate.num << std::endl; std::cout << "_cipherstate.ecount : ["; for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { printf("%02x", _cipherstate.ecount[i]); } std::cout << "]" << std::endl; std::cout << "_srtp_header_size : " << _srtp_header_size << std::endl; std::cout << "_srtp_payload_size : " << _srtp_payload_size << std::endl; std::cout << "_active_crypto : " << _active_crypto << std::endl; } std::string JLSRTP::dumpCryptoContext() { std::ostringstream oss; oss.str(""); oss << "_id : " << "(" << _id.ssrc << ", " << _id.address << ", " << _id.port << ")" << std::endl; oss << "_ROC : " << _ROC << std::endl; oss << "_s_l : " << _s_l << std::endl; oss << "_primary_crypto.cipher_algorithm : " << _primary_crypto.cipher_algorithm << std::endl; oss << "_primary_crypto.hmac_algorithm : " << _primary_crypto.hmac_algorithm << std::endl; oss << "_primary_crypto.MKI : " << _primary_crypto.MKI << std::endl; oss << "_primary_crypto.MKI_length : " << _primary_crypto.MKI_length << std::endl; oss << "_primary_crypto.active_MKI : " << _primary_crypto.active_MKI << std::endl; oss << "_primary_crypto.master_key : "; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _primary_crypto.master_key.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_primary_crypto.master_key[i]); } oss.unsetf(std::ios::hex); oss << std::endl; oss << "_primary_crypto.master_key_counter : " << _primary_crypto.master_key_counter << std::endl; oss << "_primary_crypto.n_e : " << _primary_crypto.n_e << std::endl; oss << "_primary_crypto.n_a : " << _primary_crypto.n_a << std::endl; oss << "_primary_crypto.master_salt : "; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _primary_crypto.master_salt.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_primary_crypto.master_salt[i]); } oss.unsetf(std::ios::hex); oss << std::endl; oss << "_primary_crypto.master_key_derivation_rate : " << _primary_crypto.master_key_derivation_rate << std::endl; oss << "_primary_crypto.master_mki_value : " << _primary_crypto.master_mki_value << std::endl; oss << "_primary_crypto.n_s : " << _primary_crypto.n_s << std::endl; oss << "_primary_crypto.tag : " << _primary_crypto.tag << std::endl; oss << "_secondary_crypto.cipher_algorithm : " << _secondary_crypto.cipher_algorithm << std::endl; oss << "_secondary_crypto.hmac_algorithm : " << _secondary_crypto.hmac_algorithm << std::endl; oss << "_secondary_crypto.MKI : " << _secondary_crypto.MKI << std::endl; oss << "_secondary_crypto.MKI_length : " << _secondary_crypto.MKI_length << std::endl; oss << "_secondary_crypto.active_MKI : " << _secondary_crypto.active_MKI << std::endl; oss << "_secondary_crypto.master_key : "; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _secondary_crypto.master_key.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_secondary_crypto.master_key[i]); } oss.unsetf(std::ios::hex); oss << std::endl; oss << "_secondary_crypto.master_key_counter : " << _secondary_crypto.master_key_counter << std::endl; oss << "_secondary_crypto.n_e : " << _secondary_crypto.n_e << std::endl; oss << "_secondary_crypto.n_a : " << _secondary_crypto.n_a << std::endl; oss << "_secondary_crypto.master_salt : "; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _secondary_crypto.master_salt.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_secondary_crypto.master_salt[i]); } oss.unsetf(std::ios::hex); oss << std::endl; oss << "_secondary_crypto.master_key_derivation_rate : " << _secondary_crypto.master_key_derivation_rate << std::endl; oss << "_secondary_crypto.master_mki_value : " << _secondary_crypto.master_mki_value << std::endl; oss << "_secondary_crypto.n_s : " << _secondary_crypto.n_s << std::endl; oss << "_secondary_crypto.tag : " << _secondary_crypto.tag << std::endl; oss << "_session_enc_key : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _session_enc_key.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_session_enc_key[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_session_salt_key : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _session_salt_key.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_session_salt_key[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_session_auth_key : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _session_auth_key.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_session_auth_key[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_packet_iv : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < _packetIV.size(); i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_packetIV[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_pseudorandomstate.ivec : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_pseudorandomstate.ivec[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_pseudorandomstate.num : " << _pseudorandomstate.num << std::endl; oss << "_pseudorandomstate.ecount : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_pseudorandomstate.ecount[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_cipherstate.ivec : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_cipherstate.ivec[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_cipherstate.num : " << _cipherstate.num << std::endl; oss << "_cipherstate.ecount : ["; oss.setf(std::ios::hex, std::ios::basefield); for (unsigned int i = 0; i < AES_BLOCK_SIZE; i++) { oss << std::setw(2) << std::setfill('0'); oss << static_cast(_cipherstate.ecount[i]); } oss.unsetf(std::ios::hex); oss << "]" << std::endl; oss << "_srtp_header_size : " << _srtp_header_size << std::endl; oss << "_srtp_payload_size : " << _srtp_payload_size << std::endl; oss << "_active_crypto : " << _active_crypto << std::endl; return oss.str(); } int JLSRTP::generateMasterKey(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { if (RAND_bytes(_primary_crypto.master_key.data(), _primary_crypto.master_key.size()) == 1) { retVal = 0; } else { retVal = -1; } /* _primary_crypto.master_key.clear(); _primary_crypto.master_key.push_back(0xE1); _primary_crypto.master_key.push_back(0xF9); _primary_crypto.master_key.push_back(0x7A); _primary_crypto.master_key.push_back(0x0D); _primary_crypto.master_key.push_back(0x3E); _primary_crypto.master_key.push_back(0x01); _primary_crypto.master_key.push_back(0x8B); _primary_crypto.master_key.push_back(0xE0); _primary_crypto.master_key.push_back(0xD6); _primary_crypto.master_key.push_back(0x4F); _primary_crypto.master_key.push_back(0xA3); _primary_crypto.master_key.push_back(0x2C); _primary_crypto.master_key.push_back(0x06); _primary_crypto.master_key.push_back(0xDE); _primary_crypto.master_key.push_back(0x41); _primary_crypto.master_key.push_back(0x39); retVal = 0; */ } break; case SECONDARY_CRYPTO: { if (RAND_bytes(_secondary_crypto.master_key.data(), _secondary_crypto.master_key.size()) == 1) { retVal = 0; } else { retVal = -1; } /* _secondary_crypto.master_key.clear(); _secondary_crypto.master_key.push_back(0xE1); _secondary_crypto.master_key.push_back(0xF9); _secondary_crypto.master_key.push_back(0x7A); _secondary_crypto.master_key.push_back(0x0D); _secondary_crypto.master_key.push_back(0x3E); _secondary_crypto.master_key.push_back(0x01); _secondary_crypto.master_key.push_back(0x8B); _secondary_crypto.master_key.push_back(0xE0); _secondary_crypto.master_key.push_back(0xD6); _secondary_crypto.master_key.push_back(0x4F); _secondary_crypto.master_key.push_back(0xA3); _secondary_crypto.master_key.push_back(0x2C); _secondary_crypto.master_key.push_back(0x06); _secondary_crypto.master_key.push_back(0xDE); _secondary_crypto.master_key.push_back(0x41); _secondary_crypto.master_key.push_back(0x39); retVal = 0; */ } break; default: { retVal = -1; } break; } return retVal; } int JLSRTP::generateMasterSalt(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { if (RAND_bytes(_primary_crypto.master_salt.data(), _primary_crypto.master_salt.size()) == 1) { retVal = 0; } else { retVal = -1; } /* _primary_crypto.master_salt.clear(); _primary_crypto.master_salt.push_back(0x0E); _primary_crypto.master_salt.push_back(0xC6); _primary_crypto.master_salt.push_back(0x75); _primary_crypto.master_salt.push_back(0xAD); _primary_crypto.master_salt.push_back(0x49); _primary_crypto.master_salt.push_back(0x8A); _primary_crypto.master_salt.push_back(0xFE); _primary_crypto.master_salt.push_back(0xEB); _primary_crypto.master_salt.push_back(0xB6); _primary_crypto.master_salt.push_back(0x96); _primary_crypto.master_salt.push_back(0x0B); _primary_crypto.master_salt.push_back(0x3A); _primary_crypto.master_salt.push_back(0xAB); _primary_crypto.master_salt.push_back(0xE6); retVal = 0; */ } break; case SECONDARY_CRYPTO: { if (RAND_bytes(_secondary_crypto.master_salt.data(), _secondary_crypto.master_salt.size()) == 1) { retVal = 0; } else { retVal = -1; } /* _secondary_crypto.master_salt.clear(); _secondary_crypto.master_salt.push_back(0x0E); _secondary_crypto.master_salt.push_back(0xC6); _secondary_crypto.master_salt.push_back(0x75); _secondary_crypto.master_salt.push_back(0xAD); _secondary_crypto.master_salt.push_back(0x49); _secondary_crypto.master_salt.push_back(0x8A); _secondary_crypto.master_salt.push_back(0xFE); _secondary_crypto.master_salt.push_back(0xEB); _secondary_crypto.master_salt.push_back(0xB6); _secondary_crypto.master_salt.push_back(0x96); _secondary_crypto.master_salt.push_back(0x0B); _secondary_crypto.master_salt.push_back(0x3A); _secondary_crypto.master_salt.push_back(0xAB); _secondary_crypto.master_salt.push_back(0xE6); retVal = 0; */ } break; default: { retVal = -1; } break; } return retVal; } std::vector JLSRTP::getMasterKey(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { std::vector retVal; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { retVal = _primary_crypto.master_key; } break; case SECONDARY_CRYPTO: { retVal = _secondary_crypto.master_key; } break; default: { retVal.clear(); } break; } return retVal; } std::vector JLSRTP::getMasterSalt(ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { std::vector retVal; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { retVal = _primary_crypto.master_salt; } break; case SECONDARY_CRYPTO: { retVal = _secondary_crypto.master_salt; } break; default: { retVal.clear(); } break; } return retVal; } int JLSRTP::setMasterKey(std::vector &key, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { _primary_crypto.master_key = key; retVal = 0; } break; case SECONDARY_CRYPTO: { _secondary_crypto.master_key = key; retVal = 0; } break; default: { retVal = -1; } break; } return retVal; } int JLSRTP::setMasterSalt(std::vector &salt, ActiveCrypto crypto_attrib /*= ACTIVE_CRYPTO*/) { int retVal = -1; ActiveCrypto active_crypto = INVALID_CRYPTO; if (crypto_attrib == ACTIVE_CRYPTO) { active_crypto = _active_crypto; } else { active_crypto = crypto_attrib; } switch (active_crypto) { case PRIMARY_CRYPTO: { _primary_crypto.master_salt = salt; retVal = 0; } break; case SECONDARY_CRYPTO: { _secondary_crypto.master_salt = salt; retVal = 0; } break; default: { retVal = -1; } break; } return retVal; } int JLSRTP::swapCrypto() { int retVal = 0; CipherType cipher_algorithm = INVALID_CIPHER; HashType hmac_algorithm = INVALID_HASH; bool MKI = false; unsigned int MKI_length = 0; unsigned long active_MKI = 0; std::vector master_key; unsigned long master_key_counter = 0; unsigned short n_e = 0; unsigned short n_a = 0; std::vector master_salt; unsigned long master_key_derivation_rate = 0; unsigned long master_mki_value = 0; unsigned short n_s = 0; unsigned int tag = 0; cipher_algorithm = _primary_crypto.cipher_algorithm; hmac_algorithm = _primary_crypto.hmac_algorithm; MKI = _primary_crypto.MKI; MKI_length = _primary_crypto.MKI_length; active_MKI = _primary_crypto.active_MKI; master_key = _primary_crypto.master_key; master_key_counter = _primary_crypto.master_key_counter; n_e = _primary_crypto.n_e; n_a = _primary_crypto.n_a; master_salt = _primary_crypto.master_salt; master_key_derivation_rate = _primary_crypto.master_key_derivation_rate; master_mki_value = _primary_crypto.master_mki_value; n_s = _primary_crypto.n_s; tag = _primary_crypto.tag; _primary_crypto.cipher_algorithm = _secondary_crypto.cipher_algorithm; _primary_crypto.hmac_algorithm = _secondary_crypto.hmac_algorithm; _primary_crypto.MKI = _secondary_crypto.MKI; _primary_crypto.MKI_length = _secondary_crypto.MKI_length; _primary_crypto.active_MKI = _secondary_crypto.active_MKI; _primary_crypto.master_key = _secondary_crypto.master_key; _primary_crypto.master_key_counter = _secondary_crypto.master_key_counter; _primary_crypto.n_e = _secondary_crypto.n_e; _primary_crypto.n_a = _secondary_crypto.n_a; _primary_crypto.master_salt = _secondary_crypto.master_salt; _primary_crypto.master_key_derivation_rate = _secondary_crypto.master_key_derivation_rate; _primary_crypto.master_mki_value = _secondary_crypto.master_mki_value; _primary_crypto.n_s = _secondary_crypto.n_s; _primary_crypto.tag = _secondary_crypto.tag; _secondary_crypto.cipher_algorithm = cipher_algorithm; _secondary_crypto.hmac_algorithm = hmac_algorithm; _secondary_crypto.MKI = MKI; _secondary_crypto.MKI_length = MKI_length; _secondary_crypto.active_MKI = active_MKI; _secondary_crypto.master_key = master_key; _secondary_crypto.master_key_counter = master_key_counter; _secondary_crypto.n_e = n_e; _secondary_crypto.n_a = n_a; _secondary_crypto.master_salt = master_salt; _secondary_crypto.master_key_derivation_rate = master_key_derivation_rate; _secondary_crypto.master_mki_value = master_mki_value; _secondary_crypto.n_s = n_s; _secondary_crypto.tag = tag; return retVal; } int JLSRTP::selectActiveCrypto(ActiveCrypto activeCrypto) { int retVal = -1; switch (activeCrypto) { case PRIMARY_CRYPTO: { _active_crypto = activeCrypto; retVal = 0; } break; case SECONDARY_CRYPTO: { _active_crypto = activeCrypto; retVal = 0; } break; default: { _active_crypto = INVALID_CRYPTO; retVal = -1; } break; } return retVal; } ActiveCrypto JLSRTP::getActiveCrypto() { return _active_crypto; } JLSRTP& JLSRTP::operator=(const JLSRTP& that) { _id.ssrc = that._id.ssrc; _id.address = that._id.address; _id.port = that._id.port; _ROC = that._ROC; _s_l = that._s_l; _primary_crypto.cipher_algorithm = that._primary_crypto.cipher_algorithm; _primary_crypto.hmac_algorithm = that._primary_crypto.hmac_algorithm; _primary_crypto.MKI = that._primary_crypto.MKI; _primary_crypto.MKI_length = that._primary_crypto.MKI_length; _primary_crypto.active_MKI = that._primary_crypto.active_MKI; _primary_crypto.master_key = that._primary_crypto.master_key; _primary_crypto.master_key_counter = that._primary_crypto.master_key_counter; _primary_crypto.n_e = that._primary_crypto.n_e; _primary_crypto.n_a = that._primary_crypto.n_a; _primary_crypto.master_salt = that._primary_crypto.master_salt; _primary_crypto.master_key_derivation_rate = that._primary_crypto.master_key_derivation_rate; _primary_crypto.master_mki_value = that._primary_crypto.master_mki_value; _primary_crypto.n_s = that._primary_crypto.n_s; _primary_crypto.tag = that._primary_crypto.tag; _secondary_crypto.cipher_algorithm = that._secondary_crypto.cipher_algorithm; _secondary_crypto.hmac_algorithm = that._secondary_crypto.hmac_algorithm; _secondary_crypto.MKI = that._secondary_crypto.MKI; _secondary_crypto.MKI_length = that._secondary_crypto.MKI_length; _secondary_crypto.active_MKI = that._secondary_crypto.active_MKI; _secondary_crypto.master_key = that._secondary_crypto.master_key; _secondary_crypto.master_key_counter = that._secondary_crypto.master_key_counter; _secondary_crypto.n_e = that._secondary_crypto.n_e; _secondary_crypto.n_a = that._secondary_crypto.n_a; _secondary_crypto.master_salt = that._secondary_crypto.master_salt; _secondary_crypto.master_key_derivation_rate = that._secondary_crypto.master_key_derivation_rate; _secondary_crypto.master_mki_value = that._secondary_crypto.master_mki_value; _secondary_crypto.n_s = that._secondary_crypto.n_s; _secondary_crypto.tag = that._secondary_crypto.tag; _session_enc_key = that._session_enc_key; _session_salt_key = that._session_salt_key; _session_auth_key = that._session_auth_key; _packetIV = that._packetIV; memcpy(_pseudorandomstate.ivec, that._pseudorandomstate.ivec, sizeof(_pseudorandomstate.ivec)); _pseudorandomstate.num = that._pseudorandomstate.num; memcpy(_pseudorandomstate.ecount, that._pseudorandomstate.ecount, sizeof(_pseudorandomstate.ecount)); memcpy(_cipherstate.ivec, that._cipherstate.ivec, sizeof(_cipherstate.ivec)); _cipherstate.num = that._cipherstate.num; memcpy(_cipherstate.ecount, that._cipherstate.ecount, sizeof(_cipherstate.ecount)); _srtp_header_size = that._srtp_header_size; _srtp_payload_size = that._srtp_payload_size; _active_crypto = that._active_crypto; return *this; } bool JLSRTP::operator==(const JLSRTP& that) { if ( (_id.ssrc == that._id.ssrc) && (_id.address == that._id.address) && (_id.port == that._id.port) && (_ROC == that._ROC) && (_s_l == that._s_l) && (_primary_crypto.cipher_algorithm == that._primary_crypto.cipher_algorithm) && (_primary_crypto.hmac_algorithm == that._primary_crypto.hmac_algorithm) && (_primary_crypto.MKI == that._primary_crypto.MKI) && (_primary_crypto.MKI_length == that._primary_crypto.MKI_length) && (_primary_crypto.active_MKI == that._primary_crypto.active_MKI) && (_primary_crypto.master_key == that._primary_crypto.master_key) && (_primary_crypto.master_key_counter == that._primary_crypto.master_key_counter) && (_primary_crypto.n_e == that._primary_crypto.n_e) && (_primary_crypto.n_a == that._primary_crypto.n_a) && (_primary_crypto.master_salt == that._primary_crypto.master_salt) && (_primary_crypto.master_key_derivation_rate == that._primary_crypto.master_key_derivation_rate) && (_primary_crypto.master_mki_value == that._primary_crypto.master_mki_value) && (_primary_crypto.n_s == that._primary_crypto.n_s) && (_primary_crypto.tag == that._primary_crypto.tag) && (_secondary_crypto.cipher_algorithm == that._secondary_crypto.cipher_algorithm) && (_secondary_crypto.hmac_algorithm == that._secondary_crypto.hmac_algorithm) && (_secondary_crypto.MKI == that._secondary_crypto.MKI) && (_secondary_crypto.MKI_length == that._secondary_crypto.MKI_length) && (_secondary_crypto.active_MKI == that._secondary_crypto.active_MKI) && (_secondary_crypto.master_key == that._secondary_crypto.master_key) && (_secondary_crypto.master_key_counter == that._secondary_crypto.master_key_counter) && (_secondary_crypto.n_e == that._secondary_crypto.n_e) && (_secondary_crypto.n_a == that._secondary_crypto.n_a) && (_secondary_crypto.master_salt == that._secondary_crypto.master_salt) && (_secondary_crypto.master_key_derivation_rate == that._secondary_crypto.master_key_derivation_rate) && (_secondary_crypto.master_mki_value == that._secondary_crypto.master_mki_value) && (_secondary_crypto.n_s == that._secondary_crypto.n_s) && (_secondary_crypto.tag == that._secondary_crypto.tag) && (_session_enc_key == that._session_enc_key) && (_session_salt_key == that._session_salt_key) && (_session_auth_key == that._session_auth_key) && (_packetIV == that._packetIV) && (memcmp(_pseudorandomstate.ivec, that._pseudorandomstate.ivec, sizeof(_pseudorandomstate.ivec)) == 0) && (_pseudorandomstate.num == that._pseudorandomstate.num) && (memcmp(_pseudorandomstate.ecount, that._pseudorandomstate.ecount, sizeof(_pseudorandomstate.ecount)) == 0) && (memcmp(_cipherstate.ivec, that._cipherstate.ivec, sizeof(_cipherstate.ivec)) == 0) && (_cipherstate.num == that._cipherstate.num) && (memcmp(_cipherstate.ecount, that._cipherstate.ecount, sizeof(_cipherstate.ecount)) == 0) && (_srtp_header_size == that._srtp_header_size) && (_srtp_payload_size == that._srtp_payload_size) && (_active_crypto == that._active_crypto) ) { return true; } else { return false; } } bool JLSRTP::operator!=(const JLSRTP& that) { if (*this == that) { return false; } else { return true; } } JLSRTP::JLSRTP() { resetCryptoContext(0xCA110000, "127.0.0.1", 0); _pseudorandomstate.cipher = EVP_CIPHER_CTX_new(); if (_pseudorandomstate.cipher != NULL) { EVP_EncryptInit_ex(_pseudorandomstate.cipher, EVP_aes_128_ecb(), NULL, NULL /* primary/secondary master key set later */, NULL); } _cipherstate.cipher = EVP_CIPHER_CTX_new(); if (_cipherstate.cipher != NULL) { EVP_EncryptInit_ex(_cipherstate.cipher, EVP_aes_128_ecb(), NULL, NULL /* _session_enc_key set later */, NULL); } } JLSRTP::JLSRTP(unsigned int ssrc, std::string ipAddress, unsigned short port) { resetCryptoContext(ssrc, ipAddress, port); _pseudorandomstate.cipher = EVP_CIPHER_CTX_new(); if (_pseudorandomstate.cipher != NULL) { EVP_EncryptInit_ex(_pseudorandomstate.cipher, EVP_aes_128_ecb(), NULL, NULL /* primary/secondary master key set later */, NULL); } _cipherstate.cipher = EVP_CIPHER_CTX_new(); if (_cipherstate.cipher != NULL) { EVP_EncryptInit_ex(_cipherstate.cipher, EVP_aes_128_ecb(), NULL, NULL /* _session_enc_key set later */, NULL); } } JLSRTP::~JLSRTP() { EVP_CIPHER_CTX_free(_cipherstate.cipher); EVP_CIPHER_CTX_free(_pseudorandomstate.cipher); RAND_cleanup(); } #else // !USE_OPENSSL && !USE_WOLFSSL #include "jlsrtp.hpp" JLSRTP::JLSRTP() { } JLSRTP::~JLSRTP() { } #endif // USE_OPENSSL || USE_WOLFSSL sipp-3.7.2/src/listener.cpp0000664000000000000000000000367014525516253012524 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #include #include #include #include #include #include #include "sipp.hpp" listener_map listeners; listener::listener(const char *id, bool listening) { this->id = strdup(id); this->listening = false; if (listening) { startListening(); } } void listener::startListening() { assert(!listening); listeners.insert(pair(listener_map::key_type(id),this)); listening = true; } void listener::stopListening() { assert(listening); listener_map::iterator listener_it; listener_it = listeners.find(listener_map::key_type(id)); listeners.erase(listener_it); listening = false; } char *listener::getId() { return id; } listener::~listener() { if (listening) { stopListening(); } free(id); id = NULL; } listener *get_listener(const char *id) { listener_map::iterator listener_it = listeners.find(listener_map::key_type(id)); if (listener_it == listeners.end()) { return NULL; } return listener_it->second; } sipp-3.7.2/src/logger.cpp0000664000000000000000000004114414525516253012154 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include #include #include #include #include #include #include #include "logger.hpp" unsigned long total_errors = 0; void log_off(struct logfile_info* lfi) { if (lfi->fptr) { fflush(lfi->fptr); fclose(lfi->fptr); lfi->fptr = NULL; lfi->overwrite = false; } } void print_count_file(FILE* f, int header) { char temp_str[256]; if (!main_scenario || (!header && !main_scenario->stats)) { return; } if (header) { fprintf(f, "CurrentTime%sElapsedTime%s", stat_delimiter, stat_delimiter); } else { struct timeval currentTime, startTime; GET_TIME(¤tTime); main_scenario->stats->getStartTime(&startTime); unsigned long globalElapsedTime = CStat::computeDiffTimeInMs(¤tTime, &startTime); fprintf(f, "%s%s", CStat::formatTime(¤tTime), stat_delimiter); fprintf(f, "%s%s", CStat::msToHHMMSSus(globalElapsedTime), stat_delimiter); } for (unsigned int index = 0; index < main_scenario->messages.size(); index++) { message* curmsg = main_scenario->messages[index]; if (curmsg->hide) { continue; } if (SendingMessage* src = curmsg->send_scheme) { if (header) { if (src->isResponse()) { sprintf(temp_str, "%u_%d_", index, src->getCode()); } else { sprintf(temp_str, "%u_%s_", index, src->getMethod()); } fprintf(f, "%sSent%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); if (curmsg->retrans_delay) { fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); } if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_sent, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_sent_retrans, stat_delimiter); if (curmsg->retrans_delay) { fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); } if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->recv_response) { if (header) { sprintf(temp_str, "%u_%d_", index, curmsg->recv_response); fprintf(f, "%sRecv%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->recv_request) { if (header) { sprintf(temp_str, "%u_%s_", index, curmsg->recv_request); fprintf(f, "%sRecv%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->pause_distribution || curmsg->pause_variable) { if (header) { sprintf(temp_str, "%u_Pause_", index); fprintf(f, "%sSessions%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); } else { fprintf(f, "%d%s", curmsg->sessions, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); } } else if (curmsg->M_type == MSG_TYPE_NOP) { /* No output. */ } else if (curmsg->M_type == MSG_TYPE_RECVCMD) { if (header) { sprintf(temp_str, "%u_RecvCmd", index); fprintf(f, "%s%s", temp_str, stat_delimiter); fprintf(f, "%s_Timeout%s", temp_str, stat_delimiter); } else { fprintf(f, "%lu%s", curmsg->M_nbCmdRecv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); } } else if (curmsg->M_type == MSG_TYPE_SENDCMD) { if (header) { sprintf(temp_str, "%u_SendCmd", index); fprintf(f, "%s%s", temp_str, stat_delimiter); } else { fprintf(f, "%lu%s", curmsg->M_nbCmdSent, stat_delimiter); } } else { ERROR("Unknown count file message type:"); } } fprintf(f, "\n"); fflush(f); } void print_error_codes_file(FILE* f) { if (!main_scenario || !main_scenario->stats) { return; } // Print time and elapsed time to file struct timeval currentTime, startTime; GET_TIME(¤tTime); main_scenario->stats->getStartTime(&startTime); unsigned long globalElapsedTime = CStat::computeDiffTimeInMs(¤tTime, &startTime); fprintf(f, "%s%s", CStat::formatTime(¤tTime), stat_delimiter); fprintf(f, "%s%s", CStat::msToHHMMSSus(globalElapsedTime), stat_delimiter); // Print comma-separated list of all error codes seen since the last time // this function was called for (; main_scenario->stats->error_codes.size() != 0;) { fprintf( f, "%d,", main_scenario->stats ->error_codes[main_scenario->stats->error_codes.size() - 1]); main_scenario->stats->error_codes.pop_back(); } fprintf(f, "\n"); fflush(f); } /* Function to dump all available screens in a file */ void print_screens(void) { int oldScreen = currentScreenToDisplay; int oldRepartition = currentRepartitionToDisplay; currentScreenToDisplay = DISPLAY_SCENARIO_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_STAT_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_REPARTITION_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; for (currentRepartitionToDisplay = 2; currentRepartitionToDisplay <= display_scenario->stats->nRtds(); currentRepartitionToDisplay++) { sp->print_to_file(screen_lfi.fptr); } currentScreenToDisplay = oldScreen; currentRepartitionToDisplay = oldRepartition; } static void rotatef(struct logfile_info* lfi) { char L_rotate_file_name[MAX_PATH]; if (!lfi->fixedname) { sprintf(lfi->file_name, "%s_%ld_%s.log", scenario_file, (long)getpid(), lfi->name); } if (ringbuffer_files > 0) { if (!lfi->ftimes) { lfi->ftimes = (struct logfile_id*)calloc(ringbuffer_files, sizeof(struct logfile_id)); } /* We need to rotate away an existing file. */ if (lfi->nfiles == ringbuffer_files) { if ((lfi->ftimes)[0].n) { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.%d.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[0].start, (lfi->ftimes)[0].n); } else { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[0].start); } unlink(L_rotate_file_name); lfi->nfiles--; memmove(lfi->ftimes, &((lfi->ftimes)[1]), sizeof(struct logfile_id) * (lfi->nfiles)); } if (lfi->starttime) { (lfi->ftimes)[lfi->nfiles].start = lfi->starttime; (lfi->ftimes)[lfi->nfiles].n = 0; /* If we have the same time, then we need to append an identifier. */ if (lfi->nfiles && ((lfi->ftimes)[lfi->nfiles].start == (lfi->ftimes)[lfi->nfiles - 1].start)) { (lfi->ftimes)[lfi->nfiles].n = (lfi->ftimes)[lfi->nfiles - 1].n + 1; } if ((lfi->ftimes)[lfi->nfiles].n) { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.%d.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[lfi->nfiles].start, (lfi->ftimes)[lfi->nfiles].n); } else { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[lfi->nfiles].start); } lfi->nfiles++; fflush(lfi->fptr); fclose(lfi->fptr); lfi->fptr = NULL; rename(lfi->file_name, L_rotate_file_name); } } time(&lfi->starttime); if (lfi->overwrite) { lfi->fptr = fopen(lfi->file_name, "w"); } else { lfi->fptr = fopen(lfi->file_name, "a"); lfi->overwrite = true; } if (lfi->check && !lfi->fptr) { /* We can not use the error functions from this function, as we may be * rotating the error log itself! */ ERROR("Unable to create '%s'", lfi->file_name); } } void rotate_screenf() { rotatef(&screen_lfi); } void rotate_calldebugf() { rotatef(&calldebug_lfi); } void rotate_messagef() { rotatef(&message_lfi); } void rotate_shortmessagef() { rotatef(&shortmessage_lfi); } void rotate_logfile() { rotatef(&log_lfi); } void rotate_errorf() { 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) { ret = vfprintf(lfi->fptr, fmt, ap); fflush(lfi->fptr); lfi->count += ret; if (max_log_size && lfi->count > max_log_size) { fclose(lfi->fptr); lfi->fptr = NULL; } if (ringbuffer_size && lfi->count > ringbuffer_size) { rotatef(lfi); lfi->count = 0; } } return ret; } int TRACE_MSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&message_lfi, fmt, ap); va_end(ap); return ret; } int TRACE_SHORTMSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&shortmessage_lfi, fmt, ap); va_end(ap); return ret; } int LOG_MSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&log_lfi, fmt, ap); va_end(ap); return ret; } int TRACE_CALLDEBUG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&calldebug_lfi, fmt, ap); va_end(ap); return ret; } void print_errors() { if (total_errors == 0) { return; } fprintf(stderr, "%s\n", screen_last_error); if (total_errors > 1) { if (screen_logfile[0] != '\0') { fprintf(stderr, "There were more errors, see '%s' file\n", screen_logfile); } else { fprintf(stderr, "There were more errors, enable -trace_err to log them.\n"); } } fflush(stderr); } static void _advance(char*& c, const int snprintfResult) { if (snprintfResult > 0) { c += snprintfResult; } } static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, va_list ap) { static unsigned long long count = 0; struct timeval currentTime; CStat::globalStat(fatal ? CStat::E_FATAL_ERRORS : CStat::E_WARNING); GET_TIME (¤tTime); const std::size_t bufSize = sizeof(screen_last_error) / sizeof(screen_last_error[0]); const char* const bufEnd = &screen_last_error[bufSize]; char* c = screen_last_error; _advance(c, snprintf(c, bufEnd - c, "%s: ", CStat::formatTime(¤tTime))); if (c < bufEnd) { _advance(c, vsnprintf(c, bufEnd - c, fmt, ap)); } if (use_errno && c < bufEnd) { _advance(c, snprintf(c, bufEnd - c, ", errno = %d (%s)", error, strerror(error))); } total_errors++; if (!error_lfi.fptr && print_all_responses) { rotate_errorf(); if (error_lfi.fptr) { fprintf(error_lfi.fptr, "The following events occurred:\n"); fflush(error_lfi.fptr); } else { if (c < bufEnd) { _advance(c, snprintf(c, bufEnd - c, "Unable to create '%s': %s.\n", screen_logfile, strerror(errno))); } sipp_exit(EXIT_FATAL_ERROR, 0, 0); } } if (error_lfi.fptr) { count += fprintf(error_lfi.fptr, "%s", screen_last_error); fflush(error_lfi.fptr); if (ringbuffer_size && count > ringbuffer_size) { rotate_errorf(); count = 0; } if (max_log_size && count > max_log_size) { print_all_responses = 0; if (error_lfi.fptr) { fflush(error_lfi.fptr); fclose(error_lfi.fptr); error_lfi.fptr = NULL; error_lfi.overwrite = false; } } } else if (fatal) { fprintf(stderr, "%s\n", screen_last_error); fflush(stderr); } if (fatal) { if (error == EADDRINUSE) { sipp_exit(EXIT_BIND_ERROR, 0, 0); } else { sipp_exit(EXIT_FATAL_ERROR, 0, 0); } } } extern "C" { void ERROR(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(true, false, 0, fmt, ap); va_end(ap); exit(1); } void ERROR_NO(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(true, true, errno, fmt, ap); va_end(ap); exit(1); } void WARNING(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(false, false, 0, fmt, ap); va_end(ap); } void WARNING_NO(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(false, true, errno, fmt, ap); va_end(ap); } } sipp-3.7.2/src/md5.c0000664000000000000000000003131414525516253011020 0ustar /* * Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * L. Peter Deutsch * ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* * Independent implementation of MD5 (RFC 1321). * * This code implements the MD5 Algorithm defined in RFC 1321, whose * text is available at * http://www.ietf.org/rfc/rfc1321.txt * The code is derived from the text of the RFC, including the test suite * (section A.5) but excluding the rest of Appendix A. It does not include * any code or documentation that is identified in the RFC as being * copyrighted. * * The original and principal author of md5.c is L. Peter Deutsch * . Other authors are noted in the change history * that follows (in reverse chronological order): * * 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order * either statically or dynamically; added missing #include * in library. * 2002-03-11 lpd Corrected argument list for main(), and added int return * type, in test program and T value program. * 2002-02-21 lpd Added missing #include in test program. * 2000-07-03 lpd Patched to eliminate warnings about "constant is * unsigned in ANSI C, signed in traditional"; made test program * self-checking. * 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. * 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). * 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + F(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + G(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti) \ t = a + H(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + I(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } sipp-3.7.2/src/message.cpp0000664000000000000000000005375614525516253012335 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken */ #include "sipp.hpp" #include "message.hpp" struct KeywordMap { const char *keyword; MessageCompType type; }; typedef std::map kw_map; kw_map keyword_map; /* These keywords take no parameters. */ struct KeywordMap SimpleKeywords[] = { {"remote_ip", E_Message_Remote_IP }, {"remote_host", E_Message_Remote_Host }, {"remote_port", E_Message_Remote_Port }, {"transport", E_Message_Transport }, {"local_ip", E_Message_Local_IP }, {"local_ip_type", E_Message_Local_IP_Type }, {"local_port", E_Message_Local_Port }, {"server_ip", E_Message_Server_IP }, {"media_ip", E_Message_Media_IP }, {"rtpstream_audio_port", E_Message_RTPStream_Audio_Port }, {"rtpstream_video_port", E_Message_RTPStream_Video_Port }, #ifdef USE_TLS {"cryptotag1audio", E_Message_CryptoTag1Audio }, {"cryptotag2audio", E_Message_CryptoTag2Audio }, {"cryptosuiteaescm128sha1801audio", E_Message_CryptoSuiteAesCm128Sha1801Audio }, {"cryptosuiteaescm128sha1802audio", E_Message_CryptoSuiteAesCm128Sha1802Audio }, {"cryptosuiteaescm128sha1321audio", E_Message_CryptoSuiteAesCm128Sha1321Audio }, {"cryptosuiteaescm128sha1322audio", E_Message_CryptoSuiteAesCm128Sha1322Audio }, {"cryptokeyparams1audio", E_Message_CryptoKeyParams1Audio }, {"cryptokeyparams2audio", E_Message_CryptoKeyParams2Audio }, {"cryptotag1video", E_Message_CryptoTag1Video }, {"cryptotag2video", E_Message_CryptoTag2Video }, {"cryptosuiteaescm128sha1801video", E_Message_CryptoSuiteAesCm128Sha1801Video }, {"cryptosuiteaescm128sha1802video", E_Message_CryptoSuiteAesCm128Sha1802Video }, {"cryptosuiteaescm128sha1321video", E_Message_CryptoSuiteAesCm128Sha1321Video }, {"cryptosuiteaescm128sha1322video", E_Message_CryptoSuiteAesCm128Sha1322Video }, {"cryptokeyparams1video", E_Message_CryptoKeyParams1Video }, {"cryptokeyparams2video", E_Message_CryptoKeyParams2Video }, {"cryptosuitenullsha1801audio" , E_Message_CryptoSuiteNullSha1801Audio }, {"cryptosuitenullsha1802audio" , E_Message_CryptoSuiteNullSha1802Audio }, {"cryptosuitenullsha1321audio" , E_Message_CryptoSuiteNullSha1321Audio }, {"cryptosuitenullsha1322audio" , E_Message_CryptoSuiteNullSha1322Audio }, {"cryptosuitenullsha1801video" , E_Message_CryptoSuiteNullSha1801Video }, {"cryptosuitenullsha1802video" , E_Message_CryptoSuiteNullSha1802Video }, {"cryptosuitenullsha1321video" , E_Message_CryptoSuiteNullSha1321Video }, {"cryptosuitenullsha1322video" , E_Message_CryptoSuiteNullSha1322Video }, {"ueaescm128sha1801audio" , E_Message_UEAesCm128Sha1801Audio }, {"ueaescm128sha1802audio" , E_Message_UEAesCm128Sha1802Audio }, {"ueaescm128sha1321audio" , E_Message_UEAesCm128Sha1321Audio }, {"ueaescm128sha1322audio" , E_Message_UEAesCm128Sha1322Audio }, {"ueaescm128sha1801video" , E_Message_UEAesCm128Sha1801Video }, {"ueaescm128sha1802video" , E_Message_UEAesCm128Sha1802Video }, {"ueaescm128sha1321video" , E_Message_UEAesCm128Sha1321Video }, {"ueaescm128sha1322video" , E_Message_UEAesCm128Sha1322Video }, #endif // USE_TLS {"media_port", E_Message_Media_Port }, {"media_ip_type", E_Message_Media_IP_Type }, {"call_number", E_Message_Call_Number }, {"dynamic_id", E_Message_DynamicId }, // wrapping global counter {"call_id", E_Message_Call_ID }, {"cseq", E_Message_CSEQ }, {"pid", E_Message_PID }, {"service", E_Message_Service }, {"branch", E_Message_Branch }, {"msg_index", E_Message_Index }, {"next_url", E_Message_Next_Url }, {"len", E_Message_Len }, {"peer_tag_param", E_Message_Peer_Tag_Param }, {"last_Request_URI", E_Message_Last_Request_URI }, {"last_cseq_number", E_Message_Last_CSeq_Number }, {"last_message", E_Message_Last_Message }, {"routes", E_Message_Routes }, {"tdmmap", E_Message_TDM_Map }, {"clock_tick", E_Message_ClockTick }, {"users", E_Message_Users }, {"userid", E_Message_UserID }, {"timestamp", E_Message_Timestamp }, {"date", E_Message_Date }, {"sipp_version", E_Message_SippVersion }, }; #define KEYWORD_SIZE 256 static char* quoted_strchr(const char* s, int c) { const char* p; for (p = s; *p && *p != c; p++) { if (*p == '"') { p++; p += strcspn(p, "\""); } } return *p == c ? const_cast(p) : NULL; } SendingMessage::SendingMessage(scenario* msg_scenario, const char* const_src, bool skip_sanity) { char * src = strdup(const_src); char * osrc = src; char * literal; int literalLen; char * dest; char * key; char current_line[MAX_HEADER_LEN]; char * line_mark = NULL; char * tsrc; int num_cr = get_cr_number(src); this->msg_scenario = msg_scenario; dest = literal = (char *)malloc(strlen(src) + num_cr + 1); current_line[0] = '\0'; *dest = 0; while(*src) { if (current_line[0] == '\0') { line_mark = strchr(src, '\n'); if (line_mark) { int header_len = line_mark - src; if (header_len > MAX_HEADER_LEN-1) header_len = MAX_HEADER_LEN-1; memcpy(current_line, src, header_len); current_line[header_len] = '\0'; } } /* This hex encoding could be done in XML parsing, allowing us to skip * these conditionals and branches. */ if ((*src == '\\') && (*(src+1) == 'x')) { /* Allows any hex coded char like '\x5B' ([) */ src += 2; if (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); } *dest++ = val & 0xff; } src++; } else if (*src == '\n') { *dest++ = '\r'; *dest++ = *src++; current_line[0] = '\0'; } else if (*src != '[') { *dest++ = *src++; } else { /* We have found a keyword, store the literal that we have been generating. */ literalLen = dest - literal; if (literalLen) { *dest = '\0'; literal = (char *)realloc(literal, literalLen + 1); if (!literal) { ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen; // length without the terminator messageComponents.push_back(newcomp); } else { free(literal); } dest = literal = (char *)malloc(strlen(src) + num_cr + 1); *dest = '\0'; /* Now lets determine which keyword we have. */ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } char keyword [KEYWORD_SIZE+1]; src++; tsrc = quoted_strchr(src, '['); key = quoted_strchr(src, ']'); if ((tsrc) && (tsrc KEYWORD_SIZE) || (!(key - src))) { ERROR("Syntax error or invalid [keyword] in scenario while parsing '%s'", current_line); } memcpy(keyword, src, key - src); keyword[key - src] = 0; src = key + 1; // allow +/-n for numeric variables newcomp->offset = 0; if ((strncmp(keyword, "authentication", strlen("authentication")) && strncmp(keyword, "tdmmap", strlen("tdmmap"))) && ((key = strchr(keyword,'+')) || (key = strchr(keyword,'-')))) { if (isdigit(*(key+1))) { newcomp->offset = atoi(key); *key = 0; } } char *spc = NULL; char ospc; if ((spc = strchr(keyword, ' '))) { ospc = *spc; *spc = '\0'; } kw_map::iterator it = keyword_map.find(keyword); if (spc) { *spc = ospc; } if (it != keyword_map.end()) { newcomp->type = E_Message_Custom; newcomp->comp_param.fxn = it->second; messageComponents.push_back(newcomp); continue; } bool simple_keyword = false; for (unsigned int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) { if (!strcmp(keyword, SimpleKeywords[i].keyword)) { newcomp->type = SimpleKeywords[i].type; simple_keyword = true; break; } } if (simple_keyword) { messageComponents.push_back(newcomp); continue; } if(!strncmp(keyword, "field", strlen("field"))) { newcomp->type = E_Message_Injection; /* Parse out the interesting things like file and number. */ newcomp->comp_param.field_param.field = atoi(keyword + strlen("field")); char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "file=", fileName); if (fileName[0] == '\0') { if (!default_file) { ERROR("No injection file was specified!"); } newcomp->comp_param.field_param.filename = strdup(default_file); } else { newcomp->comp_param.field_param.filename = strdup(fileName); } if (inFiles.find(newcomp->comp_param.field_param.filename) == inFiles.end()) { ERROR("Invalid injection file: %s", fileName); } char line[KEYWORD_SIZE]; getKeywordParam(keyword, "line=", line); if (line[0]) { /* Turn this into a new message component. */ newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true); } } else if(!strncmp(keyword, "file", strlen("file"))) { newcomp->type = E_Message_File; /* Parse out the interesting things like file and number. */ char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "name=", fileName); if (fileName[0] == '\0') { ERROR("No name specified for 'file' keyword!"); } /* Turn this into a new message component. */ newcomp->comp_param.filename = new SendingMessage(msg_scenario, fileName, true); } else if(*keyword == '$') { newcomp->type = E_Message_Variable; if (!msg_scenario) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(keyword + 1, "Variable keyword"); } else if(!strncmp(keyword, "fill", strlen("fill"))) { newcomp->type = E_Message_Fill; char filltext[KEYWORD_SIZE]; char varName[KEYWORD_SIZE]; getKeywordParam(keyword, "text=", filltext); if (filltext[0] == '\0') { strcpy(filltext, "X"); } getKeywordParam(keyword, "variable=", varName); newcomp->literal = strdup(filltext); newcomp->literalLen = strlen(newcomp->literal); if (!msg_scenario) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(varName, "Fill Variable"); } else if(!strncmp(keyword, "last_", strlen("last_"))) { newcomp->type = E_Message_Last_Header; newcomp->literal = strdup(keyword + strlen("last_")); newcomp->literalLen = strlen(newcomp->literal); } else if(!strncmp(keyword, "authentication", strlen("authentication"))) { parseAuthenticationKeyword(msg_scenario, newcomp, keyword); } else { // scan for the generic parameters - must be last test int i = 0; while (generic[i]) { char *msg1 = *generic[i]; char *msg2 = *(generic[i] + 1); if(!strcmp(keyword, msg1)) { newcomp->type = E_Message_Literal; newcomp->literal = strdup(msg2); newcomp->literalLen = strlen(newcomp->literal); break; } ++i; } if (!generic[i]) { ERROR("Unsupported keyword '%s' in xml scenario file", keyword); } } messageComponents.push_back(newcomp); } } if (literal[0]) { *dest++ = '\0'; literalLen = dest - literal; literal = (char *)realloc(literal, literalLen); if (!literal) { ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen-1; messageComponents.push_back(newcomp); } else { free(literal); } if (skip_sanity) { cancel = response = ack = false; method = NULL; free(osrc); return; } if (numComponents() < 1) { ERROR("Can not create a message that is empty!"); } if (getComponent(0)->type != E_Message_Literal) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } char *p = method = strdup(getComponent(0)->literal); char *q; while (isspace(*p)) { p++; } if (!(q = strchr(method, ' '))) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } *q++ = '\0'; while (isspace(*q)) { q++; } if (!strcmp(method, "SIP/2.0")) { char *endptr; code = strtol(q, &endptr, 10); if (*endptr && !isspace(*endptr)) { ERROR("Invalid reply code: %s", q); } if (code < 100 || code >= 700) { ERROR("Response codes must be in the range of 100-700"); } response = true; ack = false; cancel = false; free(method); method = NULL; } else { if (p != method) { memmove(method, p, strlen(p) + 1); } method = (char *)realloc(method, strlen(method) + 1); if (!method) { ERROR("Out of memory"); } ack = (!strcmp(method, "ACK")); cancel = (!strcmp(method, "CANCEL")); response = false; }; free(osrc); } SendingMessage::~SendingMessage() { for (int i = 0; i < numComponents(); i++) { freeMessageComponent(messageComponents[i]); } free(method); } bool SendingMessage::isAck() { return ack; } bool SendingMessage::isCancel() { return cancel; } bool SendingMessage::isResponse() { return response; } char *SendingMessage::getMethod() { return method; } int SendingMessage::getCode() { return code; } void SendingMessage::getQuotedParam(char * dest, char * src, int * len) { *len=0; /* Allows any hex coded string like '0x5B07F6' */ while (char c = *src++) { switch(c) { case '"': (*len)++; *dest = '\0'; return; case '\\': c = *src++; (*len)++; if (c == 0) { *dest = '\0'; return; } /* Fall-Through. */ default: *dest++ = c; (*len)++; } } *dest = '\0'; } void SendingMessage::getHexStringParam(char * dest, char * src, int * len) { *len=0; /* Allows any hex coded string like '0x5B07F6' */ while (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); src++; } *dest++ = val & 0xff; (*len)++; } } void SendingMessage::getKeywordParam(char * src, const char * param, char * output) { char *key, *tmp; int len; len = 0; key = NULL; if ((tmp = strstr(src, param))) { tmp += strlen(param); key = tmp; if ((*key == '0') && (*(key+1) == 'x')) { key += 2; getHexStringParam(output, key, &len); } else if (*key == '\"') { key++; getQuotedParam(output, key, &len); } else { while (*key) { if (((key - src) > KEYWORD_SIZE) || (!(key - src))) { ERROR("Syntax error parsing '%s' parameter", param); } else if (*key == ']' || *key < 33 || *key > 126) { break; } key++; } strncpy(output, tmp, key-tmp); output[key-tmp] = '\0'; } } else { output[0] = '\0'; } } void SendingMessage::parseAuthenticationKeyword(scenario *msg_scenario, struct MessageComponent *dst, char *keyword) { char my_auth_user[KEYWORD_SIZE + 1]; char my_auth_pass[KEYWORD_SIZE + 1]; char my_aka[KEYWORD_SIZE + 1]; dst->type = E_Message_Authentication; memset(my_auth_user,0,KEYWORD_SIZE); memset(my_auth_pass,0,KEYWORD_SIZE); /* Look for optional username and password parameters */ getKeywordParam(keyword, "username=", my_auth_user); getKeywordParam(keyword, "password=", my_auth_pass); if(*my_auth_user == '\0') { strncpy(my_auth_user, auth_username ? auth_username : service, sizeof(my_auth_user) - 1); } if(*my_auth_pass == '\0') { strncpy(my_auth_pass, auth_password, sizeof(my_auth_pass) - 1); } dst->comp_param.auth_param.auth_user = new SendingMessage(msg_scenario, my_auth_user, true /* skip sanity */); dst->comp_param.auth_param.auth_pass = new SendingMessage(msg_scenario, my_auth_pass, true); /* add aka_OP, aka_AMF, aka_K */ getKeywordParam(keyword, "aka_K=", my_aka); if (my_aka[0]==0) { memcpy(my_aka,my_auth_pass,16); my_aka[16]=0; } dst->comp_param.auth_param.aka_K = new SendingMessage(msg_scenario, my_aka, true); getKeywordParam(keyword, "aka_OP=", my_aka); dst->comp_param.auth_param.aka_OP = new SendingMessage(msg_scenario, my_aka, true); getKeywordParam(keyword, "aka_AMF=", my_aka); dst->comp_param.auth_param.aka_AMF = new SendingMessage(msg_scenario, my_aka, true); } void SendingMessage::freeMessageComponent(struct MessageComponent *comp) { free(comp->literal); if (comp->type == E_Message_Authentication) { if (comp->comp_param.auth_param.auth_user) { delete comp->comp_param.auth_param.auth_user; } if (comp->comp_param.auth_param.auth_pass) { delete comp->comp_param.auth_param.auth_pass; } if (comp->comp_param.auth_param.aka_K) { delete comp->comp_param.auth_param.aka_K; } if (comp->comp_param.auth_param.aka_AMF) { delete comp->comp_param.auth_param.aka_AMF; } if (comp->comp_param.auth_param.aka_OP) { delete comp->comp_param.auth_param.aka_OP; } } else if (comp->type == E_Message_Injection) { free(comp->comp_param.field_param.filename); } free(comp); } int SendingMessage::numComponents() { return messageComponents.size(); } struct MessageComponent *SendingMessage::getComponent(int i) { return messageComponents[i]; } /* This is very simplistic and does not yet allow any arguments, but it is a start. */ int registerKeyword(char *keyword, customKeyword fxn) { if (keyword_map.find(keyword) != keyword_map.end()) { ERROR("Can not register keyword '%s', already registered!", keyword); } keyword_map[keyword] = fxn; return 0; } sipp-3.7.2/src/milenage.c0000664000000000000000000001721014525516253012113 0ustar /*------------------------------------------------------------------- * Example algorithms f1, f1*, f2, f3, f4, f5, f5* *------------------------------------------------------------------- * * A sample implementation of the example 3GPP authentication and * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is * a byte-oriented implementation of the functions, and of the block * cipher kernel function Rijndael. * * This has been coded for clarity, not necessarily for efficiency. * * The functions f2, f3, f4 and f5 share the same inputs and have * been coded together as a single function. f1, f1* and f5* are * all coded separately. * *-----------------------------------------------------------------*/ #include "milenage.h" #include "rijndael.h" #include /*--------------------------- prototypes --------------------------*/ /*------------------------------------------------------------------- * Algorithm f1 *------------------------------------------------------------------- * * Computes network authentication code MAC-A from key K, random * challenge RAND, sequence number SQN and authentication management * field AMF. * *-----------------------------------------------------------------*/ void f1(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_a[8], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t in1[16]; uint8_t out1[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); for (i=0; i<6; i++) { in1[i] = sqn[i]; in1[i+8] = sqn[i]; } for (i=0; i<2; i++) { in1[i+6] = amf[i]; in1[i+14] = amf[i]; } /* XOR op_c and in1, rotate by r1=64, and XOR * * on the constant c1 (which is all zeroes) */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; /* XOR on the value temp computed before */ for (i=0; i<16; i++) rijndaelInput[i] ^= temp[i]; RijndaelEncrypt( rijndaelInput, out1 ); for (i=0; i<16; i++) out1[i] ^= op_c[i]; for (i=0; i<8; i++) mac_a[i] = out1[i]; return; } /* end of function f1 */ /*------------------------------------------------------------------- * Algorithms f2-f5 *------------------------------------------------------------------- * * Takes key K and random challenge RAND, and returns response RES, * confidentiality key CK, integrity key IK and anonymity key AK. * *-----------------------------------------------------------------*/ void f2345(uint8_t k[16], uint8_t rand[16], uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t out[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); /* To obtain output block OUT2: XOR OPc and TEMP, * * rotate by r2=0, and XOR on the constant c2 (which * * is all zeroes except that the last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[i] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 1; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<8; i++) res[i] = out[i+8]; for (i=0; i<6; i++) ak[i] = out[i]; /* To obtain output block OUT3: XOR OPc and TEMP, * * rotate by r3=32, and XOR on the constant c3 (which * * is all zeroes except that the next to last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+12) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 2; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<16; i++) ck[i] = out[i]; /* To obtain output block OUT4: XOR OPc and TEMP, * * rotate by r4=64, and XOR on the constant c4 (which * * is all zeroes except that the 2nd from last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 4; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<16; i++) ik[i] = out[i]; return; } /* end of function f2345 */ /*------------------------------------------------------------------- * Algorithm f1* *------------------------------------------------------------------- * * Computes resynch authentication code MAC-S from key K, random * challenge RAND, sequence number SQN and authentication management * field AMF. * *-----------------------------------------------------------------*/ void f1star(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_s[8], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t in1[16]; uint8_t out1[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); for (i=0; i<6; i++) { in1[i] = sqn[i]; in1[i+8] = sqn[i]; } for (i=0; i<2; i++) { in1[i+6] = amf[i]; in1[i+14] = amf[i]; } /* XOR op_c and in1, rotate by r1=64, and XOR * * on the constant c1 (which is all zeroes) */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; /* XOR on the value temp computed before */ for (i=0; i<16; i++) rijndaelInput[i] ^= temp[i]; RijndaelEncrypt( rijndaelInput, out1 ); for (i=0; i<16; i++) out1[i] ^= op_c[i]; for (i=0; i<8; i++) mac_s[i] = out1[i+8]; return; } /* end of function f1star */ /*------------------------------------------------------------------- * Algorithm f5* *------------------------------------------------------------------- * * Takes key K and random challenge RAND, and returns resynch * anonymity key AK. * *-----------------------------------------------------------------*/ void f5star(uint8_t k[16], uint8_t rand[16], uint8_t ak[6], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t out[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); /* To obtain output block OUT5: XOR OPc and TEMP, * * rotate by r5=96, and XOR on the constant c5 (which * * is all zeroes except that the 3rd from last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+4) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 8; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<6; i++) ak[i] = out[i]; return; } /* end of function f5star */ /*------------------------------------------------------------------- * Function to compute OPc from OP and K. Assumes key schedule has already been performed. *-----------------------------------------------------------------*/ void ComputeOPc(uint8_t op_c[16], uint8_t op[16]) { uint8_t i; RijndaelEncrypt( op, op_c ); for (i=0; i<16; i++) op_c[i] ^= op[i]; return; } /* end of function ComputeOPc */ sipp-3.7.2/src/prepare_pcap.c0000664000000000000000000004617114525516253013003 0ustar /* * 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 * * Author : Guillaume TEISSIER from FTR&D 02/02/2006 */ #include "config.h" #include #include #include #include #include #include #include #include "defines.h" #include "endianshim.h" #include "prepare_pcap.h" #ifndef HAVE_UDP_UH_PREFIX #define uh_ulen len #define uh_sum check #define uh_sport source #define uh_dport dest #endif /* Helpful RFCs for DTMF generation. * https://tools.ietf.org/html/rfc4733 * https://tools.ietf.org/html/rfc3550 */ /* We only need the fields, which are necessary to determine the type of the next header. * we could also define our own structures for UDP and IPv4. We currently use the structures * made available by the platform, as we had no problems to get them on all supported platforms. */ typedef struct _ether_type_hdr { uint16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */ } ether_type_hdr; int check(uint16_t *buffer, int len) { int sum; int i; sum = 0; for (i=0; i<(len&~1); i+= 2) sum += *buffer++; if (len & 1) { sum += htons((*(const uint8_t*)buffer) << 8); } return sum; } uint16_t checksum_carry(int s) { int s_c = (s >> 16) + (s & 0xffff); return (~(s_c + (s_c >> 16)) & 0xffff); } char errbuf[PCAP_ERRBUF_SIZE]; /* get octet offset to EtherType block in 802.11 frame */ size_t get_802_11_ethertype_offset(int link, const uint8_t* pktdata) { size_t offset = 0; uint8_t frame_type = 0; /* 2 bits */ uint8_t frame_sub_type = 0; /* 4 bits */ uint16_t frame_ctl_fld; /* Frame Control Field */ /* get RadioTap header length */ if (link == DLT_IEEE802_11_RADIO) { uint16_t rdtap_hdr_len = 0; /* http://www.radiotap.org */ /* rdtap_version[1], pad[1], rdtap_hdr_len[2], rdtap_flds[4] */ memcpy(&rdtap_hdr_len, pktdata + 2, sizeof(rdtap_hdr_len)); /* http://radiotap.org */ /* all data fields in the radiotap header are to be specified * in little-endian order */ rdtap_hdr_len = le16toh(rdtap_hdr_len); offset += rdtap_hdr_len; } memcpy(&frame_ctl_fld, pktdata + offset, sizeof(frame_ctl_fld)); /* extract frame type and subtype from Frame Control Field */ frame_type = frame_sub_type = frame_ctl_fld>>8; frame_type = frame_type>>2 & 0x03; frame_sub_type >>= 4; if (frame_type < 0x02) { /* Control or Management frame, so ignore it and try to get * EtherType from next one */ offset = 0; } else if (frame_type == 0x02) { /* only Data frames carry the relevant payload and EtherType */ if (frame_sub_type < 0x04 || (frame_sub_type > 0x07 && frame_sub_type < 0x0c)) { /* MAC header of a Data frame is at least 24 and at most 36 * octets long */ size_t mac_hdr_len = 24; uint8_t llc_hdr[8] = { 0x00 }; while (mac_hdr_len <= 36) { /* attempt to get Logical-Link Control header */ /* dsap[1],ssap[1],ctrl_fld[1],org_code[3],ethertype[2] */ memcpy(llc_hdr, pktdata + offset + mac_hdr_len, sizeof(llc_hdr)); /* check if Logical-Link Control header */ if (llc_hdr[0] == 0xaa && llc_hdr[1] == 0xaa && llc_hdr[2] == 0x03) { /* get EtherType and convert to host byte-order. * (reduce by sizeof(eth_type)) */ offset += mac_hdr_len + (sizeof(llc_hdr) - sizeof(uint16_t)); break; } mac_hdr_len++; } } else { /* could be Null Data frame, so ignore it and try to get * EtherType from next one */ offset = 0; } } else { ERROR("Unsupported frame type %d", frame_type); } return offset; } /* get octet offset to EtherType block */ size_t get_ethertype_offset(int link, const uint8_t* pktdata) { int is_le_encoded = 0; /* little endian */ uint16_t eth_type = 0; size_t offset = 0; /* http://www.tcpdump.org/linktypes.html */ if (link == DLT_EN10MB) { /* srcmac[6], dstmac[6], ethertype[2] */ offset = 12; } else if (link == DLT_LINUX_SLL) { /* http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html */ /* pkttype[2], arphrd_type[2], lladdrlen[2], lladdr[8], ethertype[2] */ offset = 14; } else if (link == DLT_IEEE802_11 || link == DLT_IEEE802_11_RADIO) { offset = get_802_11_ethertype_offset(link, pktdata); /* multi-octet fields in 802.11 frame are to be specified in * little-endian order */ is_le_encoded = 1; } else { ERROR("Unsupported link-type %d", link); } if (offset) { /* get EtherType and convert to host byte order */ memcpy(ð_type, pktdata + offset, sizeof(eth_type)); eth_type = (is_le_encoded) ? le16toh(eth_type) : ntohs(eth_type); if (eth_type != 0x0800 && eth_type != 0x86dd) { /* check if Ethernet 802.1Q VLAN */ if (eth_type == 0x8100) { /* vlan_tag[4] */ offset += 4; } else { ERROR("Unsupported ethernet type %d", eth_type); } } } return offset; } /* prepare a pcap file */ int prepare_pkts(const char* file, pcap_pkts* pkts) { pcap_t* pcap; #ifdef HAVE_PCAP_NEXT_EX struct pcap_pkthdr* pkthdr = NULL; #else struct pcap_pkthdr pkthdr_storage; struct pcap_pkthdr* pkthdr = &pkthdr_storage; #endif const uint8_t* pktdata = NULL; int n_pkts = 0; u_long max_length = 0; size_t ether_type_offset = 0; uint16_t base = 0xffff; u_long pktlen; pcap_pkt* pkt_index; ether_type_hdr* ethhdr; struct ip* iphdr; struct ip6_hdr* ip6hdr; struct udphdr* udphdr; pkts->pkts = NULL; pcap = pcap_open_offline(file, errbuf); if (!pcap) ERROR("Can't open PCAP file '%s': %s", file, errbuf); #ifdef HAVE_PCAP_NEXT_EX while (pcap_next_ex(pcap, &pkthdr, &pktdata) == 1) { #else while ((pktdata = pcap_next(pcap, pkthdr)) != NULL) { #endif if (pkthdr->len != pkthdr->caplen) { ERROR("You got truncated packets. Please create a new dump with -s0"); } /* Determine offset from packet to ether type only once. */ if (!ether_type_offset) { int datalink = pcap_datalink(pcap); ether_type_offset = get_ethertype_offset(datalink, pktdata); } ethhdr = (ether_type_hdr *)(pktdata + ether_type_offset); if (ntohs(ethhdr->ether_type) != 0x0800 /* IPv4 */ && ntohs(ethhdr->ether_type) != 0x86dd) { /* IPv6 */ fprintf(stderr, "Ignoring non IP{4,6} packet, got ether_type %hu!\n", ntohs(ethhdr->ether_type)); continue; } iphdr = (struct ip*)((char*)ethhdr + sizeof(*ethhdr)); if (iphdr && iphdr->ip_v == 6) { /* ipv6 */ ip6hdr = (struct ip6_hdr*)(void*)iphdr; if (ip6hdr->ip6_nxt != IPPROTO_UDP) { fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n"); continue; } udphdr = (struct udphdr*)((char*)ip6hdr + sizeof(*ip6hdr)); } else { /* ipv4 */ if (iphdr->ip_p != IPPROTO_UDP) { fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n"); continue; } udphdr = (struct udphdr*)((char*)iphdr + (iphdr->ip_hl << 2)); } pktlen = ntohs(udphdr->uh_ulen); if (pktlen > PCAP_MAXPACKET) { ERROR("Packet %d with size 0x%lx is too big! " "Recompile with bigger PCAP_MAXPACKET in prepare_pcap.h", n_pkts, pktlen); } /* BUG: inefficient */ pkts->pkts = (pcap_pkt *)realloc(pkts->pkts, sizeof(*(pkts->pkts)) * (n_pkts + 1)); if (!pkts->pkts) ERROR("Can't re-allocate memory for pcap pkt"); pkt_index = pkts->pkts + n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts = pkthdr->ts; pkt_index->data = (unsigned char *) malloc(pktlen); /* BUG: inefficient */ if (!pkt_index->data) ERROR("Can't allocate memory for pcap pkt data"); memcpy(pkt_index->data, udphdr, pktlen); udphdr->uh_sum = 0; /* compute a partial udp checksum */ /* not including port that will be changed */ /* when sending RTP */ pkt_index->partial_check = check((uint16_t*)&udphdr->uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); if (max_length < pktlen) max_length = pktlen; if (base > ntohs(udphdr->uh_dport)) base = ntohs(udphdr->uh_dport); n_pkts++; } pkts->max = pkts->pkts + n_pkts; pkts->max_length = max_length; pkts->base = base; pcap_close(pcap); return 0; } struct rtphdr { /* Bit-fields are always assigned to the first available bit, possibly * constrained by other factors, such as alignment. That means that they * start at the low order bit for little-endian, and the high order bit * for big-endian. This is the "right" way to do things. It is very * unusual for a compiler to do this differently. */ #if BYTE_ORDER == LITTLE_ENDIAN uint8_t csicnt:4; uint8_t extension:1; uint8_t padding:1; uint8_t version:2; uint8_t payload_type:7; uint8_t marker:1; #elif BYTE_ORDER == BIG_ENDIAN uint8_t version:2; uint8_t padding:1; uint8_t extension:1; uint8_t csicnt:4; uint8_t marker:1; uint8_t payload_type:7; #else #error "Please fix endian macros" #endif uint16_t seqno; uint32_t timestamp; uint32_t ssrcid; }; struct rtpevent { uint8_t event_id; #if BYTE_ORDER == LITTLE_ENDIAN uint8_t volume:6; uint8_t reserved:1; uint8_t end_of_event:1; #elif BYTE_ORDER == BIG_ENDIAN uint8_t end_of_event:1; uint8_t reserved:1; uint8_t volume:6; #else #error "Please fix endian macros" #endif uint16_t duration; }; struct dtmfpacket { struct udphdr udp; struct rtphdr rtp; struct rtpevent dtmf; }; struct rtpnoop { #if BYTE_ORDER == LITTLE_ENDIAN uint32_t reserved:31; uint32_t request_rtcp:1; #elif BYTE_ORDER == BIG_ENDIAN uint32_t request_rtcp:1; uint32_t reserved:31; #else #error "Please fix endian macros" #endif }; struct nooppacket { struct udphdr udp; struct rtphdr rtp; struct rtpnoop noop; }; static u_long dtmf_ssrcid = 0x01020304; /* bug, should be random/unique */ static void fill_default_udphdr(struct udphdr* udp, u_long pktlen) { udp->uh_ulen = htons(pktlen); udp->uh_sum = 0; udp->uh_sport = 0; udp->uh_dport = 0; } static void fill_default_rtphdr(struct rtphdr* rtp, int marker, int seqno, int ts) { rtp->version = 2; rtp->padding = 0; rtp->extension = 0; rtp->csicnt = 0; rtp->marker = marker; rtp->payload_type = 0x60; /* 96 as in the SDP */ rtp->seqno = htons(seqno); rtp->timestamp = htonl(ts); rtp->ssrcid = htonl(dtmf_ssrcid); } static void fill_default_dtmf(struct dtmfpacket* dtmfpacket, int marker, int seqno, int ts, char digit, int eoe, int duration) { const u_long pktlen = sizeof(*dtmfpacket); fill_default_udphdr(&dtmfpacket->udp, pktlen); fill_default_rtphdr(&dtmfpacket->rtp, marker, seqno, ts); dtmfpacket->dtmf.event_id = digit; dtmfpacket->dtmf.end_of_event = eoe; dtmfpacket->dtmf.volume = 10; dtmfpacket->dtmf.duration = htons(duration * 8); } static void fill_default_noop(struct nooppacket* nooppacket, int seqno, int ts) { const u_long pktlen = sizeof(*nooppacket); fill_default_udphdr(&nooppacket->udp, pktlen); fill_default_rtphdr(&nooppacket->rtp, 0, seqno, ts); nooppacket->rtp.payload_type = 0x61; /* 97 for noop */ nooppacket->noop.request_rtcp = 0; nooppacket->noop.reserved = 0; } static void prepare_dtmf_digit_start( pcap_pkts* pkts, int* n_pkts, uint16_t start_seq_no, int n_digits, unsigned char uc_digit, int tone_len, unsigned long ts_offset, unsigned timestamp_start) { const u_long pktlen = sizeof(struct dtmfpacket); unsigned long cur_tone_len = 0; int marked = 0; while (cur_tone_len < tone_len) { unsigned long ts = ts_offset + (n_digits + 1) * tone_len * 2 + cur_tone_len; pcap_pkt* pkt_index; struct dtmfpacket* dtmfpacket; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for dtmf pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); /* BUG: inefficient */ if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } dtmfpacket = (struct dtmfpacket*)pkt_index->data; fill_default_dtmf(dtmfpacket, !marked, *n_pkts + start_seq_no, n_digits * tone_len * 2 + timestamp_start, uc_digit, 0, cur_tone_len); marked = 1; /* set marker once per event */ pkt_index->partial_check = check(&dtmfpacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; cur_tone_len += 20; } } static void prepare_dtmf_digit_end( pcap_pkts* pkts, int* n_pkts, uint16_t start_seq_no, int n_digits, unsigned char uc_digit, int tone_len, unsigned long ts_offset, unsigned timestamp_start) { const u_long pktlen = sizeof(struct dtmfpacket); int i; for (i = 0; i < 3; i++) { unsigned long ts = ts_offset + (n_digits + 1) * tone_len * 2 + tone_len + i + 1; pcap_pkt* pkt_index; struct dtmfpacket* dtmfpacket; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for dtmf pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } dtmfpacket = (struct dtmfpacket*)pkt_index->data; fill_default_dtmf(dtmfpacket, 0, *n_pkts + start_seq_no, n_digits * tone_len * 2 + timestamp_start, uc_digit, 1, tone_len); pkt_index->partial_check = check(&dtmfpacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; } } static void prepare_noop( pcap_pkts* pkts, int* n_pkts, uint16_t* start_seq_no, unsigned long *ts_offset, unsigned *timestamp_start) { const u_long pktlen = sizeof(struct nooppacket); /* not dtmfpacket */ int i; for (i = 0; i < 20; i++) { /* 400ms of nothingness */ unsigned long ts = *ts_offset; pcap_pkt* pkt_index; struct nooppacket* nooppacket; *ts_offset += 20; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for noop pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } nooppacket = (struct nooppacket*)pkt_index->data; fill_default_noop(nooppacket, *n_pkts + *start_seq_no, *timestamp_start + ts); pkt_index->partial_check = check(&nooppacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; (*start_seq_no)++; } *timestamp_start += *ts_offset; } /* prepare a dtmf pcap */ int prepare_dtmf(const char* digits, pcap_pkts* pkts, uint16_t start_seq_no) { unsigned long tone_len = 200; const u_long pktlen = sizeof(struct dtmfpacket); int n_pkts = 0; int n_digits = 0; int needs_filler = 0; /* warm up the stream */ const char* digit; unsigned long ts_offset = 0; /* packet timestamp */ unsigned timestamp_start = 24000; /* RTP timestamp, should be random */ /* If we see the DTMF as part of the entire audio stream, we'd need * to reuse the SSRC, but it's legal to start a new stream (new * SSRC) like we do. Note that the new SSRC that will cause some * devices to not pick up on the first event as quickly: we can work * around that by adding a few empty RTP packets with this SSRC * first. */ dtmf_ssrcid++; /* Because we need to warm up the stream (puncture NAT, make phone * accept the SSRC(?)), we add a few filler packets first. */ needs_filler = 1; pkts->pkts = NULL; char* comma = strchr(digits, ','); if (comma) { tone_len = atol(comma + 1); if (tone_len < 50 || tone_len > 2000) { tone_len = 200; } *comma = '\0'; } for (digit = digits; *digit; digit++) { unsigned char uc_digit; if (*digit >= '0' && *digit <= '9') { uc_digit = *digit - '0'; } else if (*digit == '*') { uc_digit = 10; } else if (*digit == '#') { uc_digit = 11; } else if (*digit == 'A') { uc_digit = 12; } else if (*digit == 'B') { uc_digit = 13; } else if (*digit == 'C') { uc_digit = 14; } else if (*digit == 'D') { uc_digit = 15; } else { continue; } if (needs_filler) { prepare_noop(pkts, &n_pkts, &start_seq_no, &ts_offset, ×tamp_start); needs_filler = 0; } prepare_dtmf_digit_start(pkts, &n_pkts, start_seq_no, n_digits, uc_digit, tone_len, ts_offset, timestamp_start); prepare_dtmf_digit_end(pkts, &n_pkts, start_seq_no, n_digits, uc_digit, tone_len, ts_offset, timestamp_start); n_digits++; } pkts->max = pkts->pkts + n_pkts; pkts->max_length = pktlen; pkts->base = 0; return n_pkts; } sipp-3.7.2/src/ratetask.cpp0000664000000000000000000000424714525516253012516 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class ratetask *ratetask::instance = NULL; void ratetask::initialize() { assert(instance == NULL); if (rate_increase) { instance = new ratetask(); } } void ratetask::dump() { WARNING("Increasing call rate task."); } bool ratetask::run() { if (quitting >= 10) { delete this; return false; } /* Statistics Logs. */ if ((getmilliseconds() - last_rate_increase_time) >= rate_increase_freq) { if (rate_increase) { rate += rate_increase; if (rate_max && (rate > rate_max)) { rate = rate_max; if (rate_quit) { quitting += 10; } } CallGenerationTask::set_rate(rate); last_rate_increase_time = clock_tick; } } setPaused(); return true; } unsigned int ratetask::wake() { return last_rate_increase_time + rate_increase_freq; } sipp-3.7.2/src/reporttask.cpp0000664000000000000000000000553414525516253013076 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class stattask *stattask::instance = NULL; class screentask *screentask::instance = NULL; void stattask::initialize() { assert(instance == NULL); if (dumpInFile || useCountf || useErrorCodesf) { instance = new stattask(); } } void screentask::initialize() { assert(instance == NULL); if (report_freq) { instance = new screentask(); } } void stattask::dump() { WARNING("Statistics reporting task."); } void screentask::dump() { WARNING("Screen update task."); } void screentask::report(bool last) { print_statistics(last); display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS); last_report_time = getmilliseconds(); scheduling_loops = 0; } bool screentask::run() { if (quitting > 11) { delete this; return false; } if (getmilliseconds() - last_report_time >= report_freq) { report(false); } setPaused(); return true; } unsigned int screentask::wake() { return last_report_time + report_freq; } void stattask::report() { if(dumpInFile) { main_scenario->stats->dumpData(); } if (useCountf) { print_count_file(countf, 0); } if (useErrorCodesf) { print_error_codes_file(codesf); } main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS); last_dump_time = clock_tick; } bool stattask::run() { /* Statistics Logs. */ if((getmilliseconds() - last_dump_time) >= report_freq_dumpLog) { report(); } setPaused(); return true; } unsigned int stattask::wake() { return last_dump_time + report_freq_dumpLog; } sipp-3.7.2/src/rijndael.c0000664000000000000000000007171514525516253012134 0ustar /*------------------------------------------------------------------- * Rijndael Implementation *------------------------------------------------------------------- * * A sample 32-bit orientated implementation of Rijndael, the * suggested kernel for the example 3GPP authentication and key * agreement functions. * * This implementation draws on the description in section 5.2 of * the AES proposal and also on the implementation by * Dr B. R. Gladman 9th October 2000. * It uses a number of large (4k) lookup tables to implement the * algorithm in an efficient manner. * * Note: in this implementation the State is stored in four 32-bit * words, one per column of the State, with the top byte of the * column being the _least_ significant byte of the word. * *-----------------------------------------------------------------*/ #include "rijndael.h" #include #define LITTLE_ENDIAN /* For INTEL architecture */ /* Circular byte rotates of 32 bit values */ #define rot1(x) ((x << 8) | (x >> 24)) #define rot2(x) ((x << 16) | (x >> 16)) #define rot3(x) ((x << 24) | (x >> 8)) /* Extract a byte from a 32-bit uint32_t */ #define byte0(x) ((uint8_t)(x)) #define byte1(x) ((uint8_t)(x >> 8)) #define byte2(x) ((uint8_t)(x >> 16)) #define byte3(x) ((uint8_t)(x >> 24)) /* Put or get a 32 bit uint32_t (v) in machine order from a byte * * address in (x) */ #ifdef LITTLE_ENDIAN #define u32_in(x) (*(uint32_t*)(x)) #define u32_out(x,y) (*(uint32_t*)(x) = y) #else /* Invert byte order in a 32 bit variable */ __inline uint32_t byte_swap(const uint32_t x) { return rot1(x) & 0x00ff00ff | rot3(x) & 0xff00ff00; } __inline uint32_t u32_in(const uint8_t x[]) { return byte_swap(*(uint32_t*)x); }; __inline void u32_out(uint8_t x[], const uint32_t v) { *(uint32_t*)x = byte_swap(v); }; #endif /*--------------- The lookup tables ----------------------------*/ static uint32_t rnd_con[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 }; static uint32_t ft_tab[4][256] = { { 0xA56363C6,0x847C7CF8,0x997777EE,0x8D7B7BF6,0x0DF2F2FF,0xBD6B6BD6,0xB16F6FDE,0x54C5C591, 0x50303060,0x03010102,0xA96767CE,0x7D2B2B56,0x19FEFEE7,0x62D7D7B5,0xE6ABAB4D,0x9A7676EC, 0x45CACA8F,0x9D82821F,0x40C9C989,0x877D7DFA,0x15FAFAEF,0xEB5959B2,0xC947478E,0x0BF0F0FB, 0xECADAD41,0x67D4D4B3,0xFDA2A25F,0xEAAFAF45,0xBF9C9C23,0xF7A4A453,0x967272E4,0x5BC0C09B, 0xC2B7B775,0x1CFDFDE1,0xAE93933D,0x6A26264C,0x5A36366C,0x413F3F7E,0x02F7F7F5,0x4FCCCC83, 0x5C343468,0xF4A5A551,0x34E5E5D1,0x08F1F1F9,0x937171E2,0x73D8D8AB,0x53313162,0x3F15152A, 0x0C040408,0x52C7C795,0x65232346,0x5EC3C39D,0x28181830,0xA1969637,0x0F05050A,0xB59A9A2F, 0x0907070E,0x36121224,0x9B80801B,0x3DE2E2DF,0x26EBEBCD,0x6927274E,0xCDB2B27F,0x9F7575EA, 0x1B090912,0x9E83831D,0x742C2C58,0x2E1A1A34,0x2D1B1B36,0xB26E6EDC,0xEE5A5AB4,0xFBA0A05B, 0xF65252A4,0x4D3B3B76,0x61D6D6B7,0xCEB3B37D,0x7B292952,0x3EE3E3DD,0x712F2F5E,0x97848413, 0xF55353A6,0x68D1D1B9,0000000000,0x2CEDEDC1,0x60202040,0x1FFCFCE3,0xC8B1B179,0xED5B5BB6, 0xBE6A6AD4,0x46CBCB8D,0xD9BEBE67,0x4B393972,0xDE4A4A94,0xD44C4C98,0xE85858B0,0x4ACFCF85, 0x6BD0D0BB,0x2AEFEFC5,0xE5AAAA4F,0x16FBFBED,0xC5434386,0xD74D4D9A,0x55333366,0x94858511, 0xCF45458A,0x10F9F9E9,0x06020204,0x817F7FFE,0xF05050A0,0x443C3C78,0xBA9F9F25,0xE3A8A84B, 0xF35151A2,0xFEA3A35D,0xC0404080,0x8A8F8F05,0xAD92923F,0xBC9D9D21,0x48383870,0x04F5F5F1, 0xDFBCBC63,0xC1B6B677,0x75DADAAF,0x63212142,0x30101020,0x1AFFFFE5,0x0EF3F3FD,0x6DD2D2BF, 0x4CCDCD81,0x140C0C18,0x35131326,0x2FECECC3,0xE15F5FBE,0xA2979735,0xCC444488,0x3917172E, 0x57C4C493,0xF2A7A755,0x827E7EFC,0x473D3D7A,0xAC6464C8,0xE75D5DBA,0x2B191932,0x957373E6, 0xA06060C0,0x98818119,0xD14F4F9E,0x7FDCDCA3,0x66222244,0x7E2A2A54,0xAB90903B,0x8388880B, 0xCA46468C,0x29EEEEC7,0xD3B8B86B,0x3C141428,0x79DEDEA7,0xE25E5EBC,0x1D0B0B16,0x76DBDBAD, 0x3BE0E0DB,0x56323264,0x4E3A3A74,0x1E0A0A14,0xDB494992,0x0A06060C,0x6C242448,0xE45C5CB8, 0x5DC2C29F,0x6ED3D3BD,0xEFACAC43,0xA66262C4,0xA8919139,0xA4959531,0x37E4E4D3,0x8B7979F2, 0x32E7E7D5,0x43C8C88B,0x5937376E,0xB76D6DDA,0x8C8D8D01,0x64D5D5B1,0xD24E4E9C,0xE0A9A949, 0xB46C6CD8,0xFA5656AC,0x07F4F4F3,0x25EAEACF,0xAF6565CA,0x8E7A7AF4,0xE9AEAE47,0x18080810, 0xD5BABA6F,0x887878F0,0x6F25254A,0x722E2E5C,0x241C1C38,0xF1A6A657,0xC7B4B473,0x51C6C697, 0x23E8E8CB,0x7CDDDDA1,0x9C7474E8,0x211F1F3E,0xDD4B4B96,0xDCBDBD61,0x868B8B0D,0x858A8A0F, 0x907070E0,0x423E3E7C,0xC4B5B571,0xAA6666CC,0xD8484890,0x05030306,0x01F6F6F7,0x120E0E1C, 0xA36161C2,0x5F35356A,0xF95757AE,0xD0B9B969,0x91868617,0x58C1C199,0x271D1D3A,0xB99E9E27, 0x38E1E1D9,0x13F8F8EB,0xB398982B,0x33111122,0xBB6969D2,0x70D9D9A9,0x898E8E07,0xA7949433, 0xB69B9B2D,0x221E1E3C,0x92878715,0x20E9E9C9,0x49CECE87,0xFF5555AA,0x78282850,0x7ADFDFA5, 0x8F8C8C03,0xF8A1A159,0x80898909,0x170D0D1A,0xDABFBF65,0x31E6E6D7,0xC6424284,0xB86868D0, 0xC3414182,0xB0999929,0x772D2D5A,0x110F0F1E,0xCBB0B07B,0xFC5454A8,0xD6BBBB6D,0x3A16162C }, { 0x6363C6A5,0x7C7CF884,0x7777EE99,0x7B7BF68D,0xF2F2FF0D,0x6B6BD6BD,0x6F6FDEB1,0xC5C59154, 0x30306050,0x01010203,0x6767CEA9,0x2B2B567D,0xFEFEE719,0xD7D7B562,0xABAB4DE6,0x7676EC9A, 0xCACA8F45,0x82821F9D,0xC9C98940,0x7D7DFA87,0xFAFAEF15,0x5959B2EB,0x47478EC9,0xF0F0FB0B, 0xADAD41EC,0xD4D4B367,0xA2A25FFD,0xAFAF45EA,0x9C9C23BF,0xA4A453F7,0x7272E496,0xC0C09B5B, 0xB7B775C2,0xFDFDE11C,0x93933DAE,0x26264C6A,0x36366C5A,0x3F3F7E41,0xF7F7F502,0xCCCC834F, 0x3434685C,0xA5A551F4,0xE5E5D134,0xF1F1F908,0x7171E293,0xD8D8AB73,0x31316253,0x15152A3F, 0x0404080C,0xC7C79552,0x23234665,0xC3C39D5E,0x18183028,0x969637A1,0x05050A0F,0x9A9A2FB5, 0x07070E09,0x12122436,0x80801B9B,0xE2E2DF3D,0xEBEBCD26,0x27274E69,0xB2B27FCD,0x7575EA9F, 0x0909121B,0x83831D9E,0x2C2C5874,0x1A1A342E,0x1B1B362D,0x6E6EDCB2,0x5A5AB4EE,0xA0A05BFB, 0x5252A4F6,0x3B3B764D,0xD6D6B761,0xB3B37DCE,0x2929527B,0xE3E3DD3E,0x2F2F5E71,0x84841397, 0x5353A6F5,0xD1D1B968,0000000000,0xEDEDC12C,0x20204060,0xFCFCE31F,0xB1B179C8,0x5B5BB6ED, 0x6A6AD4BE,0xCBCB8D46,0xBEBE67D9,0x3939724B,0x4A4A94DE,0x4C4C98D4,0x5858B0E8,0xCFCF854A, 0xD0D0BB6B,0xEFEFC52A,0xAAAA4FE5,0xFBFBED16,0x434386C5,0x4D4D9AD7,0x33336655,0x85851194, 0x45458ACF,0xF9F9E910,0x02020406,0x7F7FFE81,0x5050A0F0,0x3C3C7844,0x9F9F25BA,0xA8A84BE3, 0x5151A2F3,0xA3A35DFE,0x404080C0,0x8F8F058A,0x92923FAD,0x9D9D21BC,0x38387048,0xF5F5F104, 0xBCBC63DF,0xB6B677C1,0xDADAAF75,0x21214263,0x10102030,0xFFFFE51A,0xF3F3FD0E,0xD2D2BF6D, 0xCDCD814C,0x0C0C1814,0x13132635,0xECECC32F,0x5F5FBEE1,0x979735A2,0x444488CC,0x17172E39, 0xC4C49357,0xA7A755F2,0x7E7EFC82,0x3D3D7A47,0x6464C8AC,0x5D5DBAE7,0x1919322B,0x7373E695, 0x6060C0A0,0x81811998,0x4F4F9ED1,0xDCDCA37F,0x22224466,0x2A2A547E,0x90903BAB,0x88880B83, 0x46468CCA,0xEEEEC729,0xB8B86BD3,0x1414283C,0xDEDEA779,0x5E5EBCE2,0x0B0B161D,0xDBDBAD76, 0xE0E0DB3B,0x32326456,0x3A3A744E,0x0A0A141E,0x494992DB,0x06060C0A,0x2424486C,0x5C5CB8E4, 0xC2C29F5D,0xD3D3BD6E,0xACAC43EF,0x6262C4A6,0x919139A8,0x959531A4,0xE4E4D337,0x7979F28B, 0xE7E7D532,0xC8C88B43,0x37376E59,0x6D6DDAB7,0x8D8D018C,0xD5D5B164,0x4E4E9CD2,0xA9A949E0, 0x6C6CD8B4,0x5656ACFA,0xF4F4F307,0xEAEACF25,0x6565CAAF,0x7A7AF48E,0xAEAE47E9,0x08081018, 0xBABA6FD5,0x7878F088,0x25254A6F,0x2E2E5C72,0x1C1C3824,0xA6A657F1,0xB4B473C7,0xC6C69751, 0xE8E8CB23,0xDDDDA17C,0x7474E89C,0x1F1F3E21,0x4B4B96DD,0xBDBD61DC,0x8B8B0D86,0x8A8A0F85, 0x7070E090,0x3E3E7C42,0xB5B571C4,0x6666CCAA,0x484890D8,0x03030605,0xF6F6F701,0x0E0E1C12, 0x6161C2A3,0x35356A5F,0x5757AEF9,0xB9B969D0,0x86861791,0xC1C19958,0x1D1D3A27,0x9E9E27B9, 0xE1E1D938,0xF8F8EB13,0x98982BB3,0x11112233,0x6969D2BB,0xD9D9A970,0x8E8E0789,0x949433A7, 0x9B9B2DB6,0x1E1E3C22,0x87871592,0xE9E9C920,0xCECE8749,0x5555AAFF,0x28285078,0xDFDFA57A, 0x8C8C038F,0xA1A159F8,0x89890980,0x0D0D1A17,0xBFBF65DA,0xE6E6D731,0x424284C6,0x6868D0B8, 0x414182C3,0x999929B0,0x2D2D5A77,0x0F0F1E11,0xB0B07BCB,0x5454A8FC,0xBBBB6DD6,0x16162C3A }, { 0x63C6A563,0x7CF8847C,0x77EE9977,0x7BF68D7B,0xF2FF0DF2,0x6BD6BD6B,0x6FDEB16F,0xC59154C5, 0x30605030,0x01020301,0x67CEA967,0x2B567D2B,0xFEE719FE,0xD7B562D7,0xAB4DE6AB,0x76EC9A76, 0xCA8F45CA,0x821F9D82,0xC98940C9,0x7DFA877D,0xFAEF15FA,0x59B2EB59,0x478EC947,0xF0FB0BF0, 0xAD41ECAD,0xD4B367D4,0xA25FFDA2,0xAF45EAAF,0x9C23BF9C,0xA453F7A4,0x72E49672,0xC09B5BC0, 0xB775C2B7,0xFDE11CFD,0x933DAE93,0x264C6A26,0x366C5A36,0x3F7E413F,0xF7F502F7,0xCC834FCC, 0x34685C34,0xA551F4A5,0xE5D134E5,0xF1F908F1,0x71E29371,0xD8AB73D8,0x31625331,0x152A3F15, 0x04080C04,0xC79552C7,0x23466523,0xC39D5EC3,0x18302818,0x9637A196,0x050A0F05,0x9A2FB59A, 0x070E0907,0x12243612,0x801B9B80,0xE2DF3DE2,0xEBCD26EB,0x274E6927,0xB27FCDB2,0x75EA9F75, 0x09121B09,0x831D9E83,0x2C58742C,0x1A342E1A,0x1B362D1B,0x6EDCB26E,0x5AB4EE5A,0xA05BFBA0, 0x52A4F652,0x3B764D3B,0xD6B761D6,0xB37DCEB3,0x29527B29,0xE3DD3EE3,0x2F5E712F,0x84139784, 0x53A6F553,0xD1B968D1,0000000000,0xEDC12CED,0x20406020,0xFCE31FFC,0xB179C8B1,0x5BB6ED5B, 0x6AD4BE6A,0xCB8D46CB,0xBE67D9BE,0x39724B39,0x4A94DE4A,0x4C98D44C,0x58B0E858,0xCF854ACF, 0xD0BB6BD0,0xEFC52AEF,0xAA4FE5AA,0xFBED16FB,0x4386C543,0x4D9AD74D,0x33665533,0x85119485, 0x458ACF45,0xF9E910F9,0x02040602,0x7FFE817F,0x50A0F050,0x3C78443C,0x9F25BA9F,0xA84BE3A8, 0x51A2F351,0xA35DFEA3,0x4080C040,0x8F058A8F,0x923FAD92,0x9D21BC9D,0x38704838,0xF5F104F5, 0xBC63DFBC,0xB677C1B6,0xDAAF75DA,0x21426321,0x10203010,0xFFE51AFF,0xF3FD0EF3,0xD2BF6DD2, 0xCD814CCD,0x0C18140C,0x13263513,0xECC32FEC,0x5FBEE15F,0x9735A297,0x4488CC44,0x172E3917, 0xC49357C4,0xA755F2A7,0x7EFC827E,0x3D7A473D,0x64C8AC64,0x5DBAE75D,0x19322B19,0x73E69573, 0x60C0A060,0x81199881,0x4F9ED14F,0xDCA37FDC,0x22446622,0x2A547E2A,0x903BAB90,0x880B8388, 0x468CCA46,0xEEC729EE,0xB86BD3B8,0x14283C14,0xDEA779DE,0x5EBCE25E,0x0B161D0B,0xDBAD76DB, 0xE0DB3BE0,0x32645632,0x3A744E3A,0x0A141E0A,0x4992DB49,0x060C0A06,0x24486C24,0x5CB8E45C, 0xC29F5DC2,0xD3BD6ED3,0xAC43EFAC,0x62C4A662,0x9139A891,0x9531A495,0xE4D337E4,0x79F28B79, 0xE7D532E7,0xC88B43C8,0x376E5937,0x6DDAB76D,0x8D018C8D,0xD5B164D5,0x4E9CD24E,0xA949E0A9, 0x6CD8B46C,0x56ACFA56,0xF4F307F4,0xEACF25EA,0x65CAAF65,0x7AF48E7A,0xAE47E9AE,0x08101808, 0xBA6FD5BA,0x78F08878,0x254A6F25,0x2E5C722E,0x1C38241C,0xA657F1A6,0xB473C7B4,0xC69751C6, 0xE8CB23E8,0xDDA17CDD,0x74E89C74,0x1F3E211F,0x4B96DD4B,0xBD61DCBD,0x8B0D868B,0x8A0F858A, 0x70E09070,0x3E7C423E,0xB571C4B5,0x66CCAA66,0x4890D848,0x03060503,0xF6F701F6,0x0E1C120E, 0x61C2A361,0x356A5F35,0x57AEF957,0xB969D0B9,0x86179186,0xC19958C1,0x1D3A271D,0x9E27B99E, 0xE1D938E1,0xF8EB13F8,0x982BB398,0x11223311,0x69D2BB69,0xD9A970D9,0x8E07898E,0x9433A794, 0x9B2DB69B,0x1E3C221E,0x87159287,0xE9C920E9,0xCE8749CE,0x55AAFF55,0x28507828,0xDFA57ADF, 0x8C038F8C,0xA159F8A1,0x89098089,0x0D1A170D,0xBF65DABF,0xE6D731E6,0x4284C642,0x68D0B868, 0x4182C341,0x9929B099,0x2D5A772D,0x0F1E110F,0xB07BCBB0,0x54A8FC54,0xBB6DD6BB,0x162C3A16 }, { 0xC6A56363,0xF8847C7C,0xEE997777,0xF68D7B7B,0xFF0DF2F2,0xD6BD6B6B,0xDEB16F6F,0x9154C5C5, 0x60503030,0x02030101,0xCEA96767,0x567D2B2B,0xE719FEFE,0xB562D7D7,0x4DE6ABAB,0xEC9A7676, 0x8F45CACA,0x1F9D8282,0x8940C9C9,0xFA877D7D,0xEF15FAFA,0xB2EB5959,0x8EC94747,0xFB0BF0F0, 0x41ECADAD,0xB367D4D4,0x5FFDA2A2,0x45EAAFAF,0x23BF9C9C,0x53F7A4A4,0xE4967272,0x9B5BC0C0, 0x75C2B7B7,0xE11CFDFD,0x3DAE9393,0x4C6A2626,0x6C5A3636,0x7E413F3F,0xF502F7F7,0x834FCCCC, 0x685C3434,0x51F4A5A5,0xD134E5E5,0xF908F1F1,0xE2937171,0xAB73D8D8,0x62533131,0x2A3F1515, 0x080C0404,0x9552C7C7,0x46652323,0x9D5EC3C3,0x30281818,0x37A19696,0x0A0F0505,0x2FB59A9A, 0x0E090707,0x24361212,0x1B9B8080,0xDF3DE2E2,0xCD26EBEB,0x4E692727,0x7FCDB2B2,0xEA9F7575, 0x121B0909,0x1D9E8383,0x58742C2C,0x342E1A1A,0x362D1B1B,0xDCB26E6E,0xB4EE5A5A,0x5BFBA0A0, 0xA4F65252,0x764D3B3B,0xB761D6D6,0x7DCEB3B3,0x527B2929,0xDD3EE3E3,0x5E712F2F,0x13978484, 0xA6F55353,0xB968D1D1,0000000000,0xC12CEDED,0x40602020,0xE31FFCFC,0x79C8B1B1,0xB6ED5B5B, 0xD4BE6A6A,0x8D46CBCB,0x67D9BEBE,0x724B3939,0x94DE4A4A,0x98D44C4C,0xB0E85858,0x854ACFCF, 0xBB6BD0D0,0xC52AEFEF,0x4FE5AAAA,0xED16FBFB,0x86C54343,0x9AD74D4D,0x66553333,0x11948585, 0x8ACF4545,0xE910F9F9,0x04060202,0xFE817F7F,0xA0F05050,0x78443C3C,0x25BA9F9F,0x4BE3A8A8, 0xA2F35151,0x5DFEA3A3,0x80C04040,0x058A8F8F,0x3FAD9292,0x21BC9D9D,0x70483838,0xF104F5F5, 0x63DFBCBC,0x77C1B6B6,0xAF75DADA,0x42632121,0x20301010,0xE51AFFFF,0xFD0EF3F3,0xBF6DD2D2, 0x814CCDCD,0x18140C0C,0x26351313,0xC32FECEC,0xBEE15F5F,0x35A29797,0x88CC4444,0x2E391717, 0x9357C4C4,0x55F2A7A7,0xFC827E7E,0x7A473D3D,0xC8AC6464,0xBAE75D5D,0x322B1919,0xE6957373, 0xC0A06060,0x19988181,0x9ED14F4F,0xA37FDCDC,0x44662222,0x547E2A2A,0x3BAB9090,0x0B838888, 0x8CCA4646,0xC729EEEE,0x6BD3B8B8,0x283C1414,0xA779DEDE,0xBCE25E5E,0x161D0B0B,0xAD76DBDB, 0xDB3BE0E0,0x64563232,0x744E3A3A,0x141E0A0A,0x92DB4949,0x0C0A0606,0x486C2424,0xB8E45C5C, 0x9F5DC2C2,0xBD6ED3D3,0x43EFACAC,0xC4A66262,0x39A89191,0x31A49595,0xD337E4E4,0xF28B7979, 0xD532E7E7,0x8B43C8C8,0x6E593737,0xDAB76D6D,0x018C8D8D,0xB164D5D5,0x9CD24E4E,0x49E0A9A9, 0xD8B46C6C,0xACFA5656,0xF307F4F4,0xCF25EAEA,0xCAAF6565,0xF48E7A7A,0x47E9AEAE,0x10180808, 0x6FD5BABA,0xF0887878,0x4A6F2525,0x5C722E2E,0x38241C1C,0x57F1A6A6,0x73C7B4B4,0x9751C6C6, 0xCB23E8E8,0xA17CDDDD,0xE89C7474,0x3E211F1F,0x96DD4B4B,0x61DCBDBD,0x0D868B8B,0x0F858A8A, 0xE0907070,0x7C423E3E,0x71C4B5B5,0xCCAA6666,0x90D84848,0x06050303,0xF701F6F6,0x1C120E0E, 0xC2A36161,0x6A5F3535,0xAEF95757,0x69D0B9B9,0x17918686,0x9958C1C1,0x3A271D1D,0x27B99E9E, 0xD938E1E1,0xEB13F8F8,0x2BB39898,0x22331111,0xD2BB6969,0xA970D9D9,0x07898E8E,0x33A79494, 0x2DB69B9B,0x3C221E1E,0x15928787,0xC920E9E9,0x8749CECE,0xAAFF5555,0x50782828,0xA57ADFDF, 0x038F8C8C,0x59F8A1A1,0x09808989,0x1A170D0D,0x65DABFBF,0xD731E6E6,0x84C64242,0xD0B86868, 0x82C34141,0x29B09999,0x5A772D2D,0x1E110F0F,0x7BCBB0B0,0xA8FC5454,0x6DD6BBBB,0x2C3A1616 } }; static uint32_t fl_tab[4][256] = { { 0x00000063,0x0000007C,0x00000077,0x0000007B,0x000000F2,0x0000006B,0x0000006F,0x000000C5, 0x00000030,0x00000001,0x00000067,0x0000002B,0x000000FE,0x000000D7,0x000000AB,0x00000076, 0x000000CA,0x00000082,0x000000C9,0x0000007D,0x000000FA,0x00000059,0x00000047,0x000000F0, 0x000000AD,0x000000D4,0x000000A2,0x000000AF,0x0000009C,0x000000A4,0x00000072,0x000000C0, 0x000000B7,0x000000FD,0x00000093,0x00000026,0x00000036,0x0000003F,0x000000F7,0x000000CC, 0x00000034,0x000000A5,0x000000E5,0x000000F1,0x00000071,0x000000D8,0x00000031,0x00000015, 0x00000004,0x000000C7,0x00000023,0x000000C3,0x00000018,0x00000096,0x00000005,0x0000009A, 0x00000007,0x00000012,0x00000080,0x000000E2,0x000000EB,0x00000027,0x000000B2,0x00000075, 0x00000009,0x00000083,0x0000002C,0x0000001A,0x0000001B,0x0000006E,0x0000005A,0x000000A0, 0x00000052,0x0000003B,0x000000D6,0x000000B3,0x00000029,0x000000E3,0x0000002F,0x00000084, 0x00000053,0x000000D1,0x00000000,0x000000ED,0x00000020,0x000000FC,0x000000B1,0x0000005B, 0x0000006A,0x000000CB,0x000000BE,0x00000039,0x0000004A,0x0000004C,0x00000058,0x000000CF, 0x000000D0,0x000000EF,0x000000AA,0x000000FB,0x00000043,0x0000004D,0x00000033,0x00000085, 0x00000045,0x000000F9,0x00000002,0x0000007F,0x00000050,0x0000003C,0x0000009F,0x000000A8, 0x00000051,0x000000A3,0x00000040,0x0000008F,0x00000092,0x0000009D,0x00000038,0x000000F5, 0x000000BC,0x000000B6,0x000000DA,0x00000021,0x00000010,0x000000FF,0x000000F3,0x000000D2, 0x000000CD,0x0000000C,0x00000013,0x000000EC,0x0000005F,0x00000097,0x00000044,0x00000017, 0x000000C4,0x000000A7,0x0000007E,0x0000003D,0x00000064,0x0000005D,0x00000019,0x00000073, 0x00000060,0x00000081,0x0000004F,0x000000DC,0x00000022,0x0000002A,0x00000090,0x00000088, 0x00000046,0x000000EE,0x000000B8,0x00000014,0x000000DE,0x0000005E,0x0000000B,0x000000DB, 0x000000E0,0x00000032,0x0000003A,0x0000000A,0x00000049,0x00000006,0x00000024,0x0000005C, 0x000000C2,0x000000D3,0x000000AC,0x00000062,0x00000091,0x00000095,0x000000E4,0x00000079, 0x000000E7,0x000000C8,0x00000037,0x0000006D,0x0000008D,0x000000D5,0x0000004E,0x000000A9, 0x0000006C,0x00000056,0x000000F4,0x000000EA,0x00000065,0x0000007A,0x000000AE,0x00000008, 0x000000BA,0x00000078,0x00000025,0x0000002E,0x0000001C,0x000000A6,0x000000B4,0x000000C6, 0x000000E8,0x000000DD,0x00000074,0x0000001F,0x0000004B,0x000000BD,0x0000008B,0x0000008A, 0x00000070,0x0000003E,0x000000B5,0x00000066,0x00000048,0x00000003,0x000000F6,0x0000000E, 0x00000061,0x00000035,0x00000057,0x000000B9,0x00000086,0x000000C1,0x0000001D,0x0000009E, 0x000000E1,0x000000F8,0x00000098,0x00000011,0x00000069,0x000000D9,0x0000008E,0x00000094, 0x0000009B,0x0000001E,0x00000087,0x000000E9,0x000000CE,0x00000055,0x00000028,0x000000DF, 0x0000008C,0x000000A1,0x00000089,0x0000000D,0x000000BF,0x000000E6,0x00000042,0x00000068, 0x00000041,0x00000099,0x0000002D,0x0000000F,0x000000B0,0x00000054,0x000000BB,0x00000016 }, { 0x00006300,0x00007C00,0x00007700,0x00007B00,0x0000F200,0x00006B00,0x00006F00,0x0000C500, 0x00003000,0x00000100,0x00006700,0x00002B00,0x0000FE00,0x0000D700,0x0000AB00,0x00007600, 0x0000CA00,0x00008200,0x0000C900,0x00007D00,0x0000FA00,0x00005900,0x00004700,0x0000F000, 0x0000AD00,0x0000D400,0x0000A200,0x0000AF00,0x00009C00,0x0000A400,0x00007200,0x0000C000, 0x0000B700,0x0000FD00,0x00009300,0x00002600,0x00003600,0x00003F00,0x0000F700,0x0000CC00, 0x00003400,0x0000A500,0x0000E500,0x0000F100,0x00007100,0x0000D800,0x00003100,0x00001500, 0x00000400,0x0000C700,0x00002300,0x0000C300,0x00001800,0x00009600,0x00000500,0x00009A00, 0x00000700,0x00001200,0x00008000,0x0000E200,0x0000EB00,0x00002700,0x0000B200,0x00007500, 0x00000900,0x00008300,0x00002C00,0x00001A00,0x00001B00,0x00006E00,0x00005A00,0x0000A000, 0x00005200,0x00003B00,0x0000D600,0x0000B300,0x00002900,0x0000E300,0x00002F00,0x00008400, 0x00005300,0x0000D100,0000000000,0x0000ED00,0x00002000,0x0000FC00,0x0000B100,0x00005B00, 0x00006A00,0x0000CB00,0x0000BE00,0x00003900,0x00004A00,0x00004C00,0x00005800,0x0000CF00, 0x0000D000,0x0000EF00,0x0000AA00,0x0000FB00,0x00004300,0x00004D00,0x00003300,0x00008500, 0x00004500,0x0000F900,0x00000200,0x00007F00,0x00005000,0x00003C00,0x00009F00,0x0000A800, 0x00005100,0x0000A300,0x00004000,0x00008F00,0x00009200,0x00009D00,0x00003800,0x0000F500, 0x0000BC00,0x0000B600,0x0000DA00,0x00002100,0x00001000,0x0000FF00,0x0000F300,0x0000D200, 0x0000CD00,0x00000C00,0x00001300,0x0000EC00,0x00005F00,0x00009700,0x00004400,0x00001700, 0x0000C400,0x0000A700,0x00007E00,0x00003D00,0x00006400,0x00005D00,0x00001900,0x00007300, 0x00006000,0x00008100,0x00004F00,0x0000DC00,0x00002200,0x00002A00,0x00009000,0x00008800, 0x00004600,0x0000EE00,0x0000B800,0x00001400,0x0000DE00,0x00005E00,0x00000B00,0x0000DB00, 0x0000E000,0x00003200,0x00003A00,0x00000A00,0x00004900,0x00000600,0x00002400,0x00005C00, 0x0000C200,0x0000D300,0x0000AC00,0x00006200,0x00009100,0x00009500,0x0000E400,0x00007900, 0x0000E700,0x0000C800,0x00003700,0x00006D00,0x00008D00,0x0000D500,0x00004E00,0x0000A900, 0x00006C00,0x00005600,0x0000F400,0x0000EA00,0x00006500,0x00007A00,0x0000AE00,0x00000800, 0x0000BA00,0x00007800,0x00002500,0x00002E00,0x00001C00,0x0000A600,0x0000B400,0x0000C600, 0x0000E800,0x0000DD00,0x00007400,0x00001F00,0x00004B00,0x0000BD00,0x00008B00,0x00008A00, 0x00007000,0x00003E00,0x0000B500,0x00006600,0x00004800,0x00000300,0x0000F600,0x00000E00, 0x00006100,0x00003500,0x00005700,0x0000B900,0x00008600,0x0000C100,0x00001D00,0x00009E00, 0x0000E100,0x0000F800,0x00009800,0x00001100,0x00006900,0x0000D900,0x00008E00,0x00009400, 0x00009B00,0x00001E00,0x00008700,0x0000E900,0x0000CE00,0x00005500,0x00002800,0x0000DF00, 0x00008C00,0x0000A100,0x00008900,0x00000D00,0x0000BF00,0x0000E600,0x00004200,0x00006800, 0x00004100,0x00009900,0x00002D00,0x00000F00,0x0000B000,0x00005400,0x0000BB00,0x00001600 }, { 0x00630000,0x007C0000,0x00770000,0x007B0000,0x00F20000,0x006B0000,0x006F0000,0x00C50000, 0x00300000,0x00010000,0x00670000,0x002B0000,0x00FE0000,0x00D70000,0x00AB0000,0x00760000, 0x00CA0000,0x00820000,0x00C90000,0x007D0000,0x00FA0000,0x00590000,0x00470000,0x00F00000, 0x00AD0000,0x00D40000,0x00A20000,0x00AF0000,0x009C0000,0x00A40000,0x00720000,0x00C00000, 0x00B70000,0x00FD0000,0x00930000,0x00260000,0x00360000,0x003F0000,0x00F70000,0x00CC0000, 0x00340000,0x00A50000,0x00E50000,0x00F10000,0x00710000,0x00D80000,0x00310000,0x00150000, 0x00040000,0x00C70000,0x00230000,0x00C30000,0x00180000,0x00960000,0x00050000,0x009A0000, 0x00070000,0x00120000,0x00800000,0x00E20000,0x00EB0000,0x00270000,0x00B20000,0x00750000, 0x00090000,0x00830000,0x002C0000,0x001A0000,0x001B0000,0x006E0000,0x005A0000,0x00A00000, 0x00520000,0x003B0000,0x00D60000,0x00B30000,0x00290000,0x00E30000,0x002F0000,0x00840000, 0x00530000,0x00D10000,0000000000,0x00ED0000,0x00200000,0x00FC0000,0x00B10000,0x005B0000, 0x006A0000,0x00CB0000,0x00BE0000,0x00390000,0x004A0000,0x004C0000,0x00580000,0x00CF0000, 0x00D00000,0x00EF0000,0x00AA0000,0x00FB0000,0x00430000,0x004D0000,0x00330000,0x00850000, 0x00450000,0x00F90000,0x00020000,0x007F0000,0x00500000,0x003C0000,0x009F0000,0x00A80000, 0x00510000,0x00A30000,0x00400000,0x008F0000,0x00920000,0x009D0000,0x00380000,0x00F50000, 0x00BC0000,0x00B60000,0x00DA0000,0x00210000,0x00100000,0x00FF0000,0x00F30000,0x00D20000, 0x00CD0000,0x000C0000,0x00130000,0x00EC0000,0x005F0000,0x00970000,0x00440000,0x00170000, 0x00C40000,0x00A70000,0x007E0000,0x003D0000,0x00640000,0x005D0000,0x00190000,0x00730000, 0x00600000,0x00810000,0x004F0000,0x00DC0000,0x00220000,0x002A0000,0x00900000,0x00880000, 0x00460000,0x00EE0000,0x00B80000,0x00140000,0x00DE0000,0x005E0000,0x000B0000,0x00DB0000, 0x00E00000,0x00320000,0x003A0000,0x000A0000,0x00490000,0x00060000,0x00240000,0x005C0000, 0x00C20000,0x00D30000,0x00AC0000,0x00620000,0x00910000,0x00950000,0x00E40000,0x00790000, 0x00E70000,0x00C80000,0x00370000,0x006D0000,0x008D0000,0x00D50000,0x004E0000,0x00A90000, 0x006C0000,0x00560000,0x00F40000,0x00EA0000,0x00650000,0x007A0000,0x00AE0000,0x00080000, 0x00BA0000,0x00780000,0x00250000,0x002E0000,0x001C0000,0x00A60000,0x00B40000,0x00C60000, 0x00E80000,0x00DD0000,0x00740000,0x001F0000,0x004B0000,0x00BD0000,0x008B0000,0x008A0000, 0x00700000,0x003E0000,0x00B50000,0x00660000,0x00480000,0x00030000,0x00F60000,0x000E0000, 0x00610000,0x00350000,0x00570000,0x00B90000,0x00860000,0x00C10000,0x001D0000,0x009E0000, 0x00E10000,0x00F80000,0x00980000,0x00110000,0x00690000,0x00D90000,0x008E0000,0x00940000, 0x009B0000,0x001E0000,0x00870000,0x00E90000,0x00CE0000,0x00550000,0x00280000,0x00DF0000, 0x008C0000,0x00A10000,0x00890000,0x000D0000,0x00BF0000,0x00E60000,0x00420000,0x00680000, 0x00410000,0x00990000,0x002D0000,0x000F0000,0x00B00000,0x00540000,0x00BB0000,0x00160000 }, { 0x63000000,0x7C000000,0x77000000,0x7B000000,0xF2000000,0x6B000000,0x6F000000,0xC5000000, 0x30000000,0x01000000,0x67000000,0x2B000000,0xFE000000,0xD7000000,0xAB000000,0x76000000, 0xCA000000,0x82000000,0xC9000000,0x7D000000,0xFA000000,0x59000000,0x47000000,0xF0000000, 0xAD000000,0xD4000000,0xA2000000,0xAF000000,0x9C000000,0xA4000000,0x72000000,0xC0000000, 0xB7000000,0xFD000000,0x93000000,0x26000000,0x36000000,0x3F000000,0xF7000000,0xCC000000, 0x34000000,0xA5000000,0xE5000000,0xF1000000,0x71000000,0xD8000000,0x31000000,0x15000000, 0x04000000,0xC7000000,0x23000000,0xC3000000,0x18000000,0x96000000,0x05000000,0x9A000000, 0x07000000,0x12000000,0x80000000,0xE2000000,0xEB000000,0x27000000,0xB2000000,0x75000000, 0x09000000,0x83000000,0x2C000000,0x1A000000,0x1B000000,0x6E000000,0x5A000000,0xA0000000, 0x52000000,0x3B000000,0xD6000000,0xB3000000,0x29000000,0xE3000000,0x2F000000,0x84000000, 0x53000000,0xD1000000,0000000000,0xED000000,0x20000000,0xFC000000,0xB1000000,0x5B000000, 0x6A000000,0xCB000000,0xBE000000,0x39000000,0x4A000000,0x4C000000,0x58000000,0xCF000000, 0xD0000000,0xEF000000,0xAA000000,0xFB000000,0x43000000,0x4D000000,0x33000000,0x85000000, 0x45000000,0xF9000000,0x02000000,0x7F000000,0x50000000,0x3C000000,0x9F000000,0xA8000000, 0x51000000,0xA3000000,0x40000000,0x8F000000,0x92000000,0x9D000000,0x38000000,0xF5000000, 0xBC000000,0xB6000000,0xDA000000,0x21000000,0x10000000,0xFF000000,0xF3000000,0xD2000000, 0xCD000000,0x0C000000,0x13000000,0xEC000000,0x5F000000,0x97000000,0x44000000,0x17000000, 0xC4000000,0xA7000000,0x7E000000,0x3D000000,0x64000000,0x5D000000,0x19000000,0x73000000, 0x60000000,0x81000000,0x4F000000,0xDC000000,0x22000000,0x2A000000,0x90000000,0x88000000, 0x46000000,0xEE000000,0xB8000000,0x14000000,0xDE000000,0x5E000000,0x0B000000,0xDB000000, 0xE0000000,0x32000000,0x3A000000,0x0A000000,0x49000000,0x06000000,0x24000000,0x5C000000, 0xC2000000,0xD3000000,0xAC000000,0x62000000,0x91000000,0x95000000,0xE4000000,0x79000000, 0xE7000000,0xC8000000,0x37000000,0x6D000000,0x8D000000,0xD5000000,0x4E000000,0xA9000000, 0x6C000000,0x56000000,0xF4000000,0xEA000000,0x65000000,0x7A000000,0xAE000000,0x08000000, 0xBA000000,0x78000000,0x25000000,0x2E000000,0x1C000000,0xA6000000,0xB4000000,0xC6000000, 0xE8000000,0xDD000000,0x74000000,0x1F000000,0x4B000000,0xBD000000,0x8B000000,0x8A000000, 0x70000000,0x3E000000,0xB5000000,0x66000000,0x48000000,0x03000000,0xF6000000,0x0E000000, 0x61000000,0x35000000,0x57000000,0xB9000000,0x86000000,0xC1000000,0x1D000000,0x9E000000, 0xE1000000,0xF8000000,0x98000000,0x11000000,0x69000000,0xD9000000,0x8E000000,0x94000000, 0x9B000000,0x1E000000,0x87000000,0xE9000000,0xCE000000,0x55000000,0x28000000,0xDF000000, 0x8C000000,0xA1000000,0x89000000,0x0D000000,0xBF000000,0xE6000000,0x42000000,0x68000000, 0x41000000,0x99000000,0x2D000000,0x0F000000,0xB0000000,0x54000000,0xBB000000,0x16000000 } }; /*----------------- The workspace ------------------------------*/ static uint32_t Ekey[44]; /* The expanded key */ /*------ The round Function. 4 table lookups and 4 Exors ------*/ #define f_rnd(x, n) \ ( ft_tab[0][byte0(x[n])] \ ^ ft_tab[1][byte1(x[(n + 1) & 3])] \ ^ ft_tab[2][byte2(x[(n + 2) & 3])] \ ^ ft_tab[3][byte3(x[(n + 3) & 3])] ) #define f_round(bo, bi, k) \ bo[0] = f_rnd(bi, 0) ^ k[0]; \ bo[1] = f_rnd(bi, 1) ^ k[1]; \ bo[2] = f_rnd(bi, 2) ^ k[2]; \ bo[3] = f_rnd(bi, 3) ^ k[3]; \ k += 4 /*--- The S Box lookup used in constructing the Key schedule ---*/ #define ls_box(x) \ ( fl_tab[0][byte0(x)] \ ^ fl_tab[1][byte1(x)] \ ^ fl_tab[2][byte2(x)] \ ^ fl_tab[3][byte3(x)] ) /*------------ The last round function (no MixColumn) ----------*/ #define lf_rnd(x, n) \ ( fl_tab[0][byte0(x[n])] \ ^ fl_tab[1][byte1(x[(n + 1) & 3])] \ ^ fl_tab[2][byte2(x[(n + 2) & 3])] \ ^ fl_tab[3][byte3(x[(n + 3) & 3])] ) /*----------------------------------------------------------- * RijndaelKeySchedule * Initialise the key schedule from a supplied key */ void RijndaelKeySchedule(uint8_t key[16]) { uint32_t t; uint32_t *ek=Ekey, /* pointer to the expanded key */ *rc=rnd_con; /* pointer to the round constant */ Ekey[0] = u32_in(key ); Ekey[1] = u32_in(key + 4); Ekey[2] = u32_in(key + 8); Ekey[3] = u32_in(key + 12); while(ek < Ekey + 40) { t = rot3(ek[3]); ek[4] = ek[0] ^ ls_box(t) ^ *rc++; ek[5] = ek[1] ^ ek[4]; ek[6] = ek[2] ^ ek[5]; ek[7] = ek[3] ^ ek[6]; ek += 4; } } /*----------------------------------------------------------- * RijndaelEncrypt * Encrypt an input block */ void RijndaelEncrypt(uint8_t in[16], uint8_t out[16]) { uint32_t b0[4], b1[4], *kp = Ekey; b0[0] = u32_in(in ) ^ *kp++; b0[1] = u32_in(in + 4) ^ *kp++; b0[2] = u32_in(in + 8) ^ *kp++; b0[3] = u32_in(in + 12) ^ *kp++; f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); u32_out(out, lf_rnd(b1, 0) ^ kp[0]); u32_out(out + 4, lf_rnd(b1, 1) ^ kp[1]); u32_out(out + 8, lf_rnd(b1, 2) ^ kp[2]); u32_out(out + 12, lf_rnd(b1, 3) ^ kp[3]); } sipp-3.7.2/src/rtpstream.cpp0000664000000000000000000041400714525516253012720 0ustar /* * 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 * * Author : Deon van der Westhuysen - June 2012 - Vodacom PTY LTD */ #include #include #include #include #include "sipp.hpp" #include #include #include #include #include #include "rtpstream.hpp" #include #include #include #include /* stub to add extra debugging/logging... */ static void debugprint(const char* format, ...) { #if 0 va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); #endif } struct free_delete { void operator()(void* x) { free(x); } }; template using my_unique_ptr = std::unique_ptr; #define RTPSTREAM_FILESPERBLOCK 16 #define RTPSTREAM_THREADBLOCKSIZE 16 #define MAX_UDP_RECV_BUFFER 8192 #define MAX_UDP_SEND_BUFFER 8192 #define TI_NULL_AUDIOIP 0x001 #define TI_NULL_VIDEOIP 0x002 #define TI_NULLIP (TI_NULL_AUDIOIP | TI_NULL_VIDEOIP) #define TI_PAUSERTP 0x004 #define TI_ECHORTP 0x008 /* Not currently implemented */ #define TI_KILLTASK 0x010 #define TI_RECONNECTSOCKET 0x020 #define TI_PLAYFILE 0x040 #define TI_PAUSERTPAPATTERN 0x080 #define TI_PLAYAPATTERN 0x100 #define TI_PAUSERTPVPATTERN 0x200 #define TI_PLAYVPATTERN 0x400 #define TI_CONFIGFLAGS (TI_KILLTASK | TI_RECONNECTSOCKET | TI_PLAYFILE | TI_PLAYAPATTERN | TI_PLAYVPATTERN) #define PATTERN1 0xAA #define PATTERN2 0xBB #define PATTERN3 0xCC #define PATTERN4 0xDD #define PATTERN5 0xEE #define PATTERN6 0xFF #define NUMPATTERNS 6 struct rtp_header_t { uint16_t flags; uint16_t seq; uint32_t timestamp; uint32_t ssrc_id; }; struct threaddata_t { pthread_t id; pthread_mutex_t tasklist_mutex; int busy_list_index; unsigned int max_tasks; volatile unsigned int num_tasks; volatile int del_pending; volatile int exit_flag; taskentry_t *tasklist; }; struct cached_file_t { char filename[RTPSTREAM_MAX_FILENAMELEN]; char *bytes; int filesize; }; struct cached_pattern_t { int id; char *bytes; int filesize; }; cached_file_t *cached_files = NULL; cached_pattern_t *cached_patterns = NULL; int num_cached_files = 0; int next_rtp_port = 0; threaddata_t **ready_threads = NULL; threaddata_t **busy_threads = NULL; int num_busy_threads = 0; int num_ready_threads = 0; int busy_threads_max = 0; int ready_threads_max = 0; unsigned int global_ssrc_id = 0xCA110000; FILE* debugafile = NULL; FILE* debugvfile = NULL; pthread_mutex_t debugamutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t debugvmutex = PTHREAD_MUTEX_INITIALIZER; #ifdef USE_TLS FILE* debuglsrtpafile = NULL; FILE* debugrsrtpafile = NULL; pthread_mutex_t debuglsrtpamutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t debugrsrtpamutex = PTHREAD_MUTEX_INITIALIZER; FILE* debuglsrtpvfile = NULL; FILE* debugrsrtpvfile = NULL; pthread_mutex_t debuglsrtpvmutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t debugrsrtpvmutex = PTHREAD_MUTEX_INITIALIZER; #endif // USE_TLS FILE* debugrefileaudio = NULL; FILE* debugrefilevideo = NULL; pthread_mutex_t debugremutexaudio = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t debugremutexvideo = PTHREAD_MUTEX_INITIALIZER; // RTPSTREAM ECHO pthread_t pthread_audioecho_id; pthread_t pthread_videoecho_id; bool quit_audioecho_thread = false; bool quit_videoecho_thread = false; pthread_mutex_t quit_mutexaudio = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t quit_mutexvideo = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t quit_cvaudio = PTHREAD_COND_INITIALIZER; pthread_cond_t quit_cvvideo = PTHREAD_COND_INITIALIZER; // JLSRTP contexts JLSRTP g_txUACAudio; JLSRTP g_rxUACAudio; JLSRTP g_txUACVideo; JLSRTP g_rxUACVideo; JLSRTP g_rxUASAudio; JLSRTP g_txUASAudio; JLSRTP g_rxUASVideo; JLSRTP g_txUASVideo; pthread_mutex_t uacAudioMutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t uacVideoMutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t uasAudioMutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t uasVideoMutex = PTHREAD_MUTEX_INITIALIZER; //=================================================================================================== static unsigned long long getThreadId(pthread_t p) { unsigned long long retVal = -1; #if defined(__APPLE__) int rc = -1; uint64_t thread_id = 0; rc = pthread_threadid_np(p, &thread_id); if (rc == 0) { retVal = thread_id; } else { retVal = -1; } #elif defined(__CYGWIN__) retVal = -1; // CygWin uses dummy thread IDs in pthread_t #else // !__APPLE__ && !__CYGWIN__ retVal = (unsigned long long)p; #endif // __APPLE__ return retVal; } void printAudioHexUS(char const* note, unsigned char const* string, unsigned int size, unsigned long long extrainfo, int moreinfo) { if ((debugafile != NULL) && (note != NULL) && (string != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugamutex); fprintf(debugafile, "TID: %lu %s %u 0x%llx %d [", pthread_self(), note, size, extrainfo, moreinfo); for (unsigned int i = 0; i < size; i++) { fprintf(debugafile, "%02X", 0x000000FF & string[i]); } fprintf(debugafile, "]\n"); pthread_mutex_unlock(&debugamutex); } } void printVideoHexUS(char const* note, unsigned char const* string, unsigned int size, unsigned long long extrainfo, int moreinfo) { if ((debugvfile != NULL) && (note != NULL) && (string != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugvmutex); fprintf(debugvfile, "TID: %lu %s %u 0x%llx %d [", pthread_self(), note, size, extrainfo, moreinfo); for (unsigned int i = 0; i < size; i++) { fprintf(debugvfile, "%02X", 0x000000FF & string[i]); } fprintf(debugvfile, "]\n"); pthread_mutex_unlock(&debugvmutex); } } void printAudioHex(char const* note, char const* string, unsigned int size, unsigned long long extrainfo, int moreinfo) { if ((debugafile != NULL) && (note != NULL) && (string != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugamutex); fprintf(debugafile, "TID: %lu %s %u 0x%llx %d [", pthread_self(), note, size, extrainfo, moreinfo); for (unsigned int i = 0; i < size; i++) { fprintf(debugafile, "%02X", 0x000000FF & string[i]); } fprintf(debugafile, "]\n"); pthread_mutex_unlock(&debugamutex); } } void printAudioVector(char const* note, std::vector const &v) { if ((debugafile != NULL) && (note != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugamutex); fprintf(debugafile, "TID: %lu %s\n", pthread_self(), note); for (unsigned int i = 0; i < v.size(); i++) { fprintf(debugafile, "%lu\n", v[i]); } pthread_mutex_unlock(&debugamutex); } } void printVideoHex(char const* note, char const* string, unsigned int size, unsigned long long extrainfo, int moreinfo) { if ((debugvfile != NULL) && (note != NULL) && (string != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugvmutex); fprintf(debugvfile, "TID: %lu %s %u 0x%llx %d [", pthread_self(), note, size, extrainfo, moreinfo); for (unsigned int i = 0; i < size; i++) { fprintf(debugvfile, "%02X", 0x000000FF & string[i]); } fprintf(debugvfile, "]\n"); pthread_mutex_unlock(&debugvmutex); } } void printVideoVector(char const* note, std::vector const &v) { if ((debugvfile != NULL) && (note != NULL) && rtpcheck_debug) { pthread_mutex_lock(&debugvmutex); fprintf(debugvfile, "TID: %lu %s\n", pthread_self(), note); for (unsigned int i = 0; i < v.size(); i++) { fprintf(debugvfile, "%lu\n", v[i]); } pthread_mutex_unlock(&debugvmutex); } } #ifdef USE_TLS void printLocalAudioSrtpStuff(SrtpAudioInfoParams &p) { if (debuglsrtpafile != NULL) { pthread_mutex_lock(&debuglsrtpamutex); fprintf(debuglsrtpafile, "audio_found : %d\n", p.audio_found); fprintf(debuglsrtpafile, "primary_audio_cryptotag : %d\n", p.primary_audio_cryptotag); fprintf(debuglsrtpafile, "secondary_audio_cryptotag : %d\n", p.secondary_audio_cryptotag); fprintf(debuglsrtpafile, "primary_audio_cryptosuite : %s\n", p.primary_audio_cryptosuite); fprintf(debuglsrtpafile, "secondary_audio_cryptosuite : %s\n", p.secondary_audio_cryptosuite); fprintf(debuglsrtpafile, "primary_audio_cryptokeyparams : %s\n", p.primary_audio_cryptokeyparams); fprintf(debuglsrtpafile, "secondary_audio_cryptokeyparams : %s\n", p.secondary_audio_cryptokeyparams); fprintf(debuglsrtpafile, "primary_unencrypted_audio_srtp : %d\n", p.primary_unencrypted_audio_srtp); fprintf(debuglsrtpafile, "secondary_unencrypted_audio_srtp: %d\n", p.secondary_unencrypted_audio_srtp); pthread_mutex_unlock(&debuglsrtpamutex); } } void printRemoteAudioSrtpStuff(SrtpAudioInfoParams &p) { if (debugrsrtpafile != NULL) { pthread_mutex_lock(&debugrsrtpamutex); fprintf(debugrsrtpafile, "audio_found : %d\n", p.audio_found); fprintf(debugrsrtpafile, "primary_audio_cryptotag : %d\n", p.primary_audio_cryptotag); fprintf(debugrsrtpafile, "secondary_audio_cryptotag : %d\n", p.secondary_audio_cryptotag); fprintf(debugrsrtpafile, "primary_audio_cryptosuite : %s\n", p.primary_audio_cryptosuite); fprintf(debugrsrtpafile, "secondary_audio_cryptosuite : %s\n", p.secondary_audio_cryptosuite); fprintf(debugrsrtpafile, "primary_audio_cryptokeyparams : %s\n", p.primary_audio_cryptokeyparams); fprintf(debugrsrtpafile, "secondary_audio_cryptokeyparams : %s\n", p.secondary_audio_cryptokeyparams); fprintf(debugrsrtpafile, "primary_unencrypted_audio_srtp : %d\n", p.primary_unencrypted_audio_srtp); fprintf(debugrsrtpafile, "secondary_unencrypted_audio_srtp: %d\n", p.secondary_unencrypted_audio_srtp); pthread_mutex_unlock(&debugrsrtpamutex); } } void printLocalVideoSrtpStuff(SrtpVideoInfoParams &p) { if (debuglsrtpvfile != NULL) { pthread_mutex_lock(&debuglsrtpvmutex); fprintf(debuglsrtpvfile, "video_found : %d\n", p.video_found); fprintf(debuglsrtpvfile, "primary_video_cryptotag : %d\n", p.primary_video_cryptotag); fprintf(debuglsrtpvfile, "secondary_video_cryptotag : %d\n", p.secondary_video_cryptotag); fprintf(debuglsrtpvfile, "primary_video_cryptosuite : %s\n", p.primary_video_cryptosuite); fprintf(debuglsrtpvfile, "secondary_video_cryptosuite : %s\n", p.secondary_video_cryptosuite); fprintf(debuglsrtpvfile, "primary_video_cryptokeyparams : %s\n", p.primary_video_cryptokeyparams); fprintf(debuglsrtpvfile, "secondary_video_cryptokeyparams : %s\n", p.secondary_video_cryptokeyparams); fprintf(debuglsrtpvfile, "primary_unencrypted_video_srtp : %d\n", p.primary_unencrypted_video_srtp); fprintf(debuglsrtpvfile, "secondary_unencrypted_video_srtp: %d\n", p.secondary_unencrypted_video_srtp); pthread_mutex_unlock(&debuglsrtpvmutex); } } void printRemoteVideoSrtpStuff(SrtpVideoInfoParams &p) { if (debugrsrtpvfile != NULL) { pthread_mutex_lock(&debugrsrtpvmutex); fprintf(debugrsrtpvfile, "video_found : %d\n", p.video_found); fprintf(debugrsrtpvfile, "primary_video_cryptotag : %d\n", p.primary_video_cryptotag); fprintf(debugrsrtpvfile, "secondary_video_cryptotag : %d\n", p.secondary_video_cryptotag); fprintf(debugrsrtpvfile, "primary_video_cryptosuite : %s\n", p.primary_video_cryptosuite); fprintf(debugrsrtpvfile, "secondary_video_cryptosuite : %s\n", p.secondary_video_cryptosuite); fprintf(debugrsrtpvfile, "primary_video_cryptokeyparams : %s\n", p.primary_video_cryptokeyparams); fprintf(debugrsrtpvfile, "secondary_video_cryptokeyparams : %s\n", p.secondary_video_cryptokeyparams); fprintf(debugrsrtpvfile, "primary_unencrypted_video_srtp : %d\n", p.primary_unencrypted_video_srtp); fprintf(debugrsrtpvfile, "secondary_unencrypted_video_srtp: %d\n", p.secondary_unencrypted_video_srtp); pthread_mutex_unlock(&debugrsrtpvmutex); } } #endif // USE_TLS int set_bit(unsigned long* context, int value) { int retVal = -1; if (context != NULL) { if (value > 0) { *context |= (1 << (value - 1)); retVal = value; } else { retVal = 0; } } else { retVal = -1; } return retVal; } int clear_bit(unsigned long* context, int value) { int retVal = -1; if (context != NULL) { if (value > 0) { *context &= ~(1 << (value - 1)); retVal = value; } else { retVal = 0; } } else { retVal = -1; } return retVal; } /* code checked */ static void rtpstream_free_taskinfo(taskentry_t* taskinfo) { if (taskinfo) { #ifdef USE_TLS /* audio SRTP echo activity indicators */ taskinfo->audio_srtp_echo_active = 0; taskinfo->video_srtp_echo_active = 0; #endif // USE_TLS /* close sockets associated with this call */ if (taskinfo->audio_rtp_socket != -1) { close(taskinfo->audio_rtp_socket); } if (taskinfo->audio_rtcp_socket != -1) { close(taskinfo->audio_rtcp_socket); } if (taskinfo->video_rtp_socket != -1) { close(taskinfo->video_rtp_socket); } if (taskinfo->video_rtcp_socket != -1) { close(taskinfo->video_rtcp_socket); } /* cleanup pthread library structure */ pthread_mutex_destroy(&(taskinfo->mutex)); free(taskinfo); } } /* code checked */ static void rtpstream_process_task_flags(taskentry_t* taskinfo) { if (taskinfo->flags & TI_RECONNECTSOCKET) { int remote_addr_len; int rc = -1; remote_addr_len = media_ip_is_ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* If we have valid ip and port numbers for audio rtp stream */ if (!(taskinfo->flags & TI_NULL_AUDIOIP)) { if (taskinfo->audio_rtcp_socket != -1) { rc = connect(taskinfo->audio_rtcp_socket, (struct sockaddr *) & (taskinfo->remote_audio_rtcp_addr), remote_addr_len); if (rc < 0) { debugprint("closing audio rtcp socket %d due to error %d in rtpstream_process_task_flags taskinfo = %p\n", taskinfo->audio_rtcp_socket, errno, taskinfo); close(taskinfo->audio_rtcp_socket); taskinfo->audio_rtcp_socket = -1; } } if (taskinfo->audio_rtp_socket != -1) { #ifdef USE_TLS if (!taskinfo->audio_srtp_echo_active) { #else // !USE_TLS if (1) { #endif // USE_TLS rc = connect(taskinfo->audio_rtp_socket, (struct sockaddr *) & (taskinfo->remote_audio_rtp_addr), remote_addr_len); if (rc < 0) { debugprint("closing audio rtp socket %d due to error %d in rtpstream_process_task_flags taskinfo = %p\n", taskinfo->audio_rtp_socket, errno, taskinfo); close(taskinfo->audio_rtp_socket); taskinfo->audio_rtp_socket = -1; } } else { /* Do NOT perform connect() when doing SRTP echo */ } } } /* If we have valid ip and port numbers for video rtp stream */ if (!(taskinfo->flags & TI_NULL_VIDEOIP)) { if (taskinfo->video_rtcp_socket != -1) { rc = connect(taskinfo->video_rtcp_socket, (struct sockaddr *) & (taskinfo->remote_video_rtcp_addr), remote_addr_len); if (rc < 0) { debugprint("closing video rtcp socket %d due to error %d in rtpstream_process_task_flags taskinfo = %p\n", taskinfo->video_rtcp_socket, errno, taskinfo); close(taskinfo->video_rtcp_socket); taskinfo->video_rtcp_socket = -1; } } if (taskinfo->video_rtp_socket != -1) { #ifdef USE_TLS if (!taskinfo->video_srtp_echo_active) { #else // !USE_TLS if (1) { #endif // USE_TLS rc = connect(taskinfo->video_rtp_socket, (struct sockaddr *) & (taskinfo->remote_video_rtp_addr), remote_addr_len); if (rc < 0) { debugprint("closing video rtp socket %d due to error %d in rtpstream_process_task_flags taskinfo = %p\n", taskinfo->video_rtp_socket, errno, taskinfo); close(taskinfo->video_rtp_socket); taskinfo->video_rtp_socket = -1; } } else { /* Do NOT perform connect() when doing SRTP echo */ } } } taskinfo->flags &= ~TI_RECONNECTSOCKET; pthread_mutex_unlock(&(taskinfo->mutex)); } if (taskinfo->flags & TI_PLAYFILE) { /* copy playback information */ taskinfo->audio_pattern_id = taskinfo->new_audio_pattern_id; taskinfo->audio_loop_count = taskinfo->new_audio_loop_count; taskinfo->audio_file_bytes_start = taskinfo->new_audio_file_bytes; taskinfo->audio_current_file_bytes = taskinfo->new_audio_file_bytes; taskinfo->audio_file_num_bytes = taskinfo->new_audio_file_size; taskinfo->audio_file_bytes_left = taskinfo->new_audio_file_size; taskinfo->audio_payload_type = taskinfo->new_audio_payload_type; taskinfo->audio_ms_per_packet = taskinfo->new_audio_ms_per_packet; taskinfo->audio_bytes_per_packet = taskinfo->new_audio_bytes_per_packet; taskinfo->audio_timeticks_per_packet = taskinfo->new_audio_timeticks_per_packet; taskinfo->audio_timeticks_per_ms = taskinfo->audio_timeticks_per_packet/taskinfo->audio_ms_per_packet; taskinfo->last_audio_timestamp = getmilliseconds() * taskinfo->audio_timeticks_per_ms; taskinfo->flags &= ~TI_PLAYFILE; } if (taskinfo->flags & TI_PLAYAPATTERN) { /* copy playback information */ taskinfo->audio_pattern_id = taskinfo->new_audio_pattern_id; taskinfo->audio_loop_count = taskinfo->new_audio_loop_count; taskinfo->audio_file_bytes_start = taskinfo->new_audio_file_bytes; taskinfo->audio_current_file_bytes = taskinfo->new_audio_file_bytes; taskinfo->audio_file_num_bytes = taskinfo->new_audio_file_size; taskinfo->audio_file_bytes_left = taskinfo->new_audio_file_size; taskinfo->audio_payload_type = taskinfo->new_audio_payload_type; taskinfo->audio_ms_per_packet = taskinfo->new_audio_ms_per_packet; taskinfo->audio_bytes_per_packet = taskinfo->new_audio_bytes_per_packet; taskinfo->audio_timeticks_per_packet = taskinfo->new_audio_timeticks_per_packet; taskinfo->audio_timeticks_per_ms = taskinfo->audio_timeticks_per_packet/taskinfo->audio_ms_per_packet; taskinfo->last_audio_timestamp = getmilliseconds() * taskinfo->audio_timeticks_per_ms; taskinfo->flags &= ~TI_PLAYAPATTERN; } if (taskinfo->flags & TI_PLAYVPATTERN) { /* copy playback information */ taskinfo->video_pattern_id = taskinfo->new_video_pattern_id; taskinfo->video_loop_count = taskinfo->new_video_loop_count; taskinfo->video_file_bytes_start = taskinfo->new_video_file_bytes; taskinfo->video_current_file_bytes = taskinfo->new_video_file_bytes; taskinfo->video_file_num_bytes = taskinfo->new_video_file_size; taskinfo->video_file_bytes_left = taskinfo->new_video_file_size; taskinfo->video_payload_type = taskinfo->new_video_payload_type; taskinfo->video_ms_per_packet = taskinfo->new_video_ms_per_packet; taskinfo->video_bytes_per_packet = taskinfo->new_video_bytes_per_packet; taskinfo->video_timeticks_per_packet = taskinfo->new_video_timeticks_per_packet; taskinfo->video_timeticks_per_ms = taskinfo->video_timeticks_per_packet/taskinfo->video_ms_per_packet; taskinfo->last_video_timestamp = getmilliseconds() * taskinfo->video_timeticks_per_ms; taskinfo->flags &= ~TI_PLAYVPATTERN; } } /**** todo - check code ****/ static unsigned long rtpstream_playrtptask(taskentry_t* taskinfo, unsigned long timenow_ms, unsigned long* comparison_acheck, std::vector &rs_apackets, unsigned long* comparison_vcheck, std::vector &rs_vpackets, unsigned int taskindex) { int rc; unsigned long next_wake; unsigned long long target_timestamp; int compresult; struct timeval tv; fd_set readfds; std::vector rtp_header; std::vector payload_data; std::vector audio_out; std::vector audio_in; std::vector video_out; std::vector video_in; unsigned short host_flags = 0; unsigned short host_seqnum = 0; unsigned int host_timestamp = 0; unsigned int host_ssrc = 0; unsigned int audio_in_size = 0; unsigned int video_in_size = 0; #ifdef USE_TLS unsigned short audio_seq_in = 0; unsigned short video_seq_in = 0; #endif union { rtp_header_t hdr; char buffer[MAX_UDP_RECV_BUFFER]; } udp_recv_temp; union { rtp_header_t hdr; char buffer[MAX_UDP_RECV_BUFFER]; } udp_recv_audio; union { rtp_header_t hdr; char buffer[MAX_UDP_SEND_BUFFER]; } udp_send_audio; union { rtp_header_t hdr; char buffer[MAX_UDP_RECV_BUFFER]; } udp_recv_video; union { rtp_header_t hdr; char buffer[MAX_UDP_SEND_BUFFER]; } udp_send_video; tv.tv_sec = 0; tv.tv_usec = 10000; /* 10ms */ *comparison_acheck = 0; *comparison_vcheck = 0; printAudioHex("----AUDIO RTP SOCKET----", "", 0, taskindex, taskinfo->audio_rtp_socket); printVideoHex("----VIDEO RTP SOCKET----", "", 0, taskindex, taskinfo->video_rtp_socket); /* OK, now to play - sockets are supposed to be non-blocking */ /* no support for video stream at this stage. will need some work */ next_wake = timenow_ms + 100; /* default next wakeup time */ if ((taskinfo->audio_rtp_socket != -1) && (taskindex < rs_apackets.size())) { /* are we playing back an audio file/pattern? */ if (taskinfo->audio_loop_count) { target_timestamp = timenow_ms * taskinfo->audio_timeticks_per_ms; next_wake = timenow_ms + taskinfo->audio_ms_per_packet - timenow_ms%taskinfo->audio_ms_per_packet; if (taskinfo->flags & (TI_NULL_AUDIOIP | TI_PAUSERTP | TI_PAUSERTPAPATTERN)) { /* when paused, set timestamp so stream appears to be up to date */ taskinfo->last_audio_timestamp = target_timestamp; } if (taskinfo->last_audio_timestamp < target_timestamp) { /* need to send rtp payload - build rtp packet header... */ memset(udp_send_audio.buffer, 0, sizeof(udp_send_audio)); udp_send_audio.hdr.flags = htons(0x8000 | taskinfo->audio_payload_type); udp_send_audio.hdr.seq = htons(taskinfo->audio_seq_out); udp_send_audio.hdr.timestamp = htonl((uint32_t) (taskinfo->last_audio_timestamp & 0XFFFFFFFF)); udp_send_audio.hdr.ssrc_id = htonl(taskinfo->audio_ssrc_id); /* add payload data to the packet - handle buffer wraparound */ if (taskinfo->audio_file_bytes_left >= taskinfo->audio_bytes_per_packet) { /* no need for fancy acrobatics */ memcpy(udp_send_audio.buffer + sizeof(rtp_header_t), taskinfo->audio_current_file_bytes, taskinfo->audio_bytes_per_packet); } else { /* copy from end and then begining of file. does not handle the */ /* case where file is shorter than the packet length!! */ memcpy(udp_send_audio.buffer + sizeof(rtp_header_t), taskinfo->audio_current_file_bytes, taskinfo->audio_file_bytes_left); memcpy(udp_send_audio.buffer + sizeof(rtp_header_t) + taskinfo->audio_file_bytes_left, taskinfo->audio_file_bytes_start, taskinfo->audio_bytes_per_packet - taskinfo->audio_file_bytes_left); } pthread_mutex_lock(&uacAudioMutex); #ifdef USE_TLS if (g_txUACAudio.getCryptoTag() != 0) { // GRAB RTP HEADER rtp_header.resize(sizeof(rtp_header_t), 0); memcpy(rtp_header.data(), udp_send_audio.buffer, sizeof(rtp_header_t) /*12*/); // GRAB RTP PAYLOAD DATA payload_data.resize(taskinfo->audio_bytes_per_packet, 0); memcpy(payload_data.data(), udp_send_audio.buffer + sizeof(rtp_header_t), taskinfo->audio_bytes_per_packet); // ENCRYPT rc = g_txUACAudio.processOutgoingPacket(taskinfo->audio_seq_out, rtp_header, payload_data, audio_out); printAudioHex("TXUACAUDIO -- processOutgoingPacket() rc == ", "", 0, rc, 0); } else #endif // USE_TLS { // NOENCRYPTION audio_out.resize(sizeof(rtp_header_t) + taskinfo->audio_bytes_per_packet, 0); memcpy(audio_out.data(), udp_send_audio.buffer, sizeof(rtp_header_t) + taskinfo->audio_bytes_per_packet); } /* now send the actual packet */ rc = send(taskinfo->audio_rtp_socket, audio_out.data(), audio_out.size(), 0); if (rc < 0) { printAudioHex("SEND FAILED: ", "", 0, rc, errno); /* handle sending errors */ if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { next_wake = timenow_ms + 2; /* retry after short sleep */ } else { /* this looks like a permanent error - should we ignore ENETUNREACH? */ debugprint("closing rtp socket %d due to error %d in rtpstream_new_call callinfo=%p\n", taskinfo->audio_rtp_socket, errno); close(taskinfo->audio_rtp_socket); taskinfo->audio_rtp_socket = -1; } } else { /* statistics - only count successful sends */ rtpstream_abytes_out += taskinfo->audio_bytes_per_packet + sizeof(rtp_header_t); rtpstream_apckts++; // GLOBAL RTP packet counter rs_apackets[taskindex]++; // TASK-specific RTP packet counter printAudioHexUS("SIPP SUCCESS SEND LOG: ", audio_out.data(), audio_out.size(), rc, rtpstream_apckts); FD_ZERO(&readfds); FD_SET(taskinfo->audio_rtp_socket, &readfds); rc = select(taskinfo->audio_rtp_socket + 1, &readfds, NULL, NULL, &tv); if (FD_ISSET(taskinfo->audio_rtp_socket, &readfds)) { /* this is temp code - will have to reorganize if/when we include echo functionality */ /* just keep listening on rtp socket (is this really required?) - ignore any errors */ #ifdef USE_TLS if (g_rxUACAudio.getCryptoTag() != 0) { audio_in_size = sizeof(rtp_header_t) + taskinfo->audio_bytes_per_packet + g_rxUACAudio.getAuthenticationTagSize(); } else #endif // USE_TLS { // NOENCRYPTION audio_in_size = sizeof(rtp_header_t) + taskinfo->audio_bytes_per_packet; } audio_in.resize(audio_in_size, 0); while ((rc = recv(taskinfo->audio_rtp_socket, audio_in.data(), audio_in.size(), 0)) >= 0) { /* for now we will just ignore any received data or receive errors */ /* separate code path for RTP echo */ rtpstream_abytes_in += rc; printAudioHexUS("SIPP SUCCESS RECV LOG: ", audio_in.data(), audio_in.size(), rc, rtpstream_apckts); } #ifdef USE_TLS if (g_rxUACAudio.getCryptoTag() != 0) { // DECRYPT rtp_header.clear(); payload_data.clear(); audio_seq_in = ntohs(((rtp_header_t*)audio_in.data())->seq); rc = g_rxUACAudio.processIncomingPacket(audio_seq_in, audio_in, rtp_header, payload_data); printAudioHex("RXUACAUDIO -- processIncomingPacket() rc == ", "", 0, rc, 0); host_flags = ntohs(((rtp_header_t*)audio_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)audio_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)audio_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)audio_in.data())->ssrc_id); audio_in[0] = (host_flags >> 8) & 0xFF; audio_in[1] = host_flags & 0xFF; audio_in[2] = (host_seqnum >> 8) & 0xFF; audio_in[3] = host_seqnum & 0xFF; audio_in[4] = (host_timestamp >> 24) & 0xFF; audio_in[5] = (host_timestamp >> 16) & 0xFF; audio_in[6] = (host_timestamp >> 8) & 0xFF; audio_in[7] = host_timestamp & 0xFF; audio_in[8] = (host_ssrc >> 24) & 0xFF; audio_in[9] = (host_ssrc >> 16) & 0xFF; audio_in[10] = (host_ssrc >> 8) & 0xFF; audio_in[11] = host_ssrc & 0xFF; memset(udp_recv_audio.buffer, 0, sizeof(udp_recv_audio)); memcpy(udp_recv_audio.buffer, rtp_header.data(), rtp_header.size()); memcpy(udp_recv_audio.buffer + sizeof(rtp_header_t), payload_data.data(), payload_data.size()); } else #endif // USE_TLS { // NOENCRYPTION host_flags = ntohs(((rtp_header_t*)audio_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)audio_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)audio_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)audio_in.data())->ssrc_id); audio_in[0] = (host_flags >> 8) & 0xFF; audio_in[1] = host_flags & 0xFF; audio_in[2] = (host_seqnum >> 8) & 0xFF; audio_in[3] = host_seqnum & 0xFF; audio_in[4] = (host_timestamp >> 24) & 0xFF; audio_in[5] = (host_timestamp >> 16) & 0xFF; audio_in[6] = (host_timestamp >> 8) & 0xFF; audio_in[7] = host_timestamp & 0xFF; audio_in[8] = (host_ssrc >> 24) & 0xFF; audio_in[9] = (host_ssrc >> 16) & 0xFF; audio_in[10] = (host_ssrc >> 8) & 0xFF; audio_in[11] = host_ssrc & 0xFF; memset(udp_recv_audio.buffer, 0, sizeof(udp_recv_audio)); memcpy(udp_recv_audio.buffer, audio_in.data(), audio_in.size()); } // VALIDATION TEST compresult = 0; compresult = memcmp(udp_send_audio.buffer + sizeof(rtp_header_t), udp_recv_audio.buffer + sizeof(rtp_header_t), taskinfo->audio_bytes_per_packet /* PAYLOAD comparison ONLY -- header EXCLUDED*/); if (compresult == 0) { // SUCCESS printAudioHex("COMPARISON OK ", "", 0, taskinfo->audio_comparison_errors, rtpstream_apckts); *comparison_acheck = 0; } else { // FAILURE taskinfo->audio_comparison_errors++; printAudioHex("COMPARISON FAILED", "", 0, taskinfo->audio_comparison_errors, rtpstream_apckts); *comparison_acheck = 1; } } else { taskinfo->audio_comparison_errors++; printAudioHex("NODATA", "", 0, taskinfo->audio_comparison_errors, rtpstream_apckts); *comparison_acheck = 1; } /* advance playback pointer to next packet */ taskinfo->audio_seq_out++; /* must change if timer ticks per packet can be fractional */ taskinfo->last_audio_timestamp += taskinfo->audio_timeticks_per_packet; taskinfo->audio_file_bytes_left -= taskinfo->audio_bytes_per_packet; if (taskinfo->audio_file_bytes_left > 0) { taskinfo->audio_current_file_bytes += taskinfo->audio_bytes_per_packet; } else { taskinfo->audio_current_file_bytes = taskinfo->audio_file_bytes_start - taskinfo->audio_file_bytes_left; taskinfo->audio_file_bytes_left += taskinfo->audio_file_num_bytes; if (taskinfo->audio_loop_count > 0) { /* one less loop to play. -1 (infinite loops) will stay as is */ taskinfo->audio_loop_count--; } } if (taskinfo->last_audio_timestamp < target_timestamp) { /* no sleep if we are behind */ next_wake = timenow_ms; } } /* if (rc < 0) */ pthread_mutex_unlock(&uacAudioMutex); } /* if (taskinfo->last_audio_timestamp < target_timestamp) */ else { printAudioHex("TIMESTAMP NOT QUITE RIGHT...", "", 0, 0, 0); *comparison_acheck = -1; } } /* if (taskinfo->audio_loop_count) */ else { /* not busy playing back a file - put possible rtp echo code here. */ } } // if (taskinfo->audio_rtp_socket != -1) if (taskinfo->audio_rtcp_socket != -1) { /* just keep listening on rtcp socket (is this really required?) - ignore any errors */ while ((rc = recv(taskinfo->audio_rtcp_socket, udp_recv_temp.buffer, sizeof(udp_recv_temp.buffer), 0)) >= 0) { /* * rtpstream_abytes_in += rc; */ } } if ((taskinfo->video_rtp_socket != -1) && (taskindex < rs_vpackets.size())) { /* are we playing back a video file/pattern? */ if (taskinfo->video_loop_count) { target_timestamp = timenow_ms * taskinfo->video_timeticks_per_ms; next_wake = timenow_ms + taskinfo->video_ms_per_packet - timenow_ms%taskinfo->video_ms_per_packet; if (taskinfo->flags & (TI_NULL_VIDEOIP | TI_PAUSERTP | TI_PAUSERTPVPATTERN)) { /* when paused, set timestamp so stream appears to be up to date */ taskinfo->last_video_timestamp = target_timestamp; } if (taskinfo->last_video_timestamp < target_timestamp) { /* need to send rtp payload - build rtp packet header... */ memset(udp_send_video.buffer, 0, sizeof(udp_send_video)); udp_send_video.hdr.flags = htons(0x8000 | taskinfo->video_payload_type); udp_send_video.hdr.seq = htons(taskinfo->video_seq_out); udp_send_video.hdr.timestamp = htonl((uint32_t) (taskinfo->last_video_timestamp & 0XFFFFFFFF)); udp_send_video.hdr.ssrc_id = htonl(taskinfo->video_ssrc_id); /* add payload data to the packet - handle buffer wraparound */ if (taskinfo->video_file_bytes_left >= taskinfo->video_bytes_per_packet) { /* no need for fancy acrobatics */ memcpy(udp_send_video.buffer + sizeof(rtp_header_t), taskinfo->video_current_file_bytes, taskinfo->video_bytes_per_packet); } else { /* copy from end and then begining of file. does not handle the */ /* case where file is shorter than the packet length!! */ memcpy(udp_send_video.buffer + sizeof(rtp_header_t), taskinfo->video_current_file_bytes, taskinfo->video_file_bytes_left); memcpy(udp_send_video.buffer + sizeof(rtp_header_t) + taskinfo->video_file_bytes_left, taskinfo->video_file_bytes_start, taskinfo->video_bytes_per_packet - taskinfo->video_file_bytes_left); } pthread_mutex_lock(&uacVideoMutex); #ifdef USE_TLS if (g_txUACVideo.getCryptoTag() != 0) { // GRAB RTP HEADER rtp_header.resize(sizeof(rtp_header_t), 0); memcpy(rtp_header.data(), udp_send_video.buffer, sizeof(rtp_header_t) /*12*/); // GRAB RTP PAYLOAD DATA payload_data.resize(taskinfo->video_bytes_per_packet, 0); memcpy(payload_data.data(), udp_send_video.buffer + sizeof(rtp_header_t), taskinfo->video_bytes_per_packet); // ENCRYPT rc = g_txUACVideo.processOutgoingPacket(taskinfo->video_seq_out, rtp_header, payload_data, video_out); printVideoHex("TXUACVIDEO -- processOutgoingPacket() rc == ", "", 0, rc, 0); } else #endif // USE_TLS { // NOENCRYPTION video_out.resize(sizeof(rtp_header_t) + taskinfo->video_bytes_per_packet, 0); memcpy(video_out.data(), udp_send_video.buffer, sizeof(rtp_header_t) + taskinfo->video_bytes_per_packet); } /* now send the actual packet */ rc = send(taskinfo->video_rtp_socket, video_out.data(), video_out.size(), 0); if (rc < 0) { printVideoHex("SEND FAILED: ", "", 0, rc, errno); /* handle sending errors */ if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { next_wake = timenow_ms + 2; /* retry after short sleep */ } else { /* this looks like a permanent error - should we ignore ENETUNREACH? */ debugprint("closing rtp socket %d due to error %d in rtpstream_new_call callinfo=%p\n", taskinfo->video_rtp_socket, errno); close(taskinfo->video_rtp_socket); taskinfo->video_rtp_socket = -1; } } else { /* statistics - only count successful sends */ rtpstream_vbytes_out += taskinfo->video_bytes_per_packet + sizeof(rtp_header_t); rtpstream_vpckts++; // GLOBAL RTP packet counter rs_vpackets[taskindex]++; // TASK-specific RTP packet counter printVideoHexUS("SIPP SUCCESS SEND LOG: ", video_out.data(), video_out.size(), rc, rtpstream_vpckts); FD_ZERO(&readfds); FD_SET(taskinfo->video_rtp_socket, &readfds); rc = select(taskinfo->video_rtp_socket + 1, &readfds, NULL, NULL, &tv); if (FD_ISSET(taskinfo->video_rtp_socket, &readfds)) { /* this is temp code - will have to reorganize if/when we include echo functionality */ /* just keep listening on rtp socket (is this really required?) - ignore any errors */ #ifdef USE_TLS if (g_rxUACVideo.getCryptoTag() != 0) { video_in_size = sizeof(rtp_header_t) + taskinfo->video_bytes_per_packet + g_rxUACVideo.getAuthenticationTagSize(); } else #endif // USE_TLS { // NOENCRYPTION video_in_size = sizeof(rtp_header_t) + taskinfo->video_bytes_per_packet; } video_in.resize(video_in_size, 0); while ((rc = recv(taskinfo->video_rtp_socket, video_in.data(), video_in.size(), 0)) >= 0) { /* for now we will just ignore any received data or receive errors */ /* separate code path for RTP echo */ rtpstream_vbytes_in += rc; printVideoHexUS("SIPP SUCCESS RECV LOG: ", video_in.data(), video_in.size(), rc, rtpstream_vpckts); } #ifdef USE_TLS if (g_rxUACVideo.getCryptoTag() != 0) { // DECRYPT rtp_header.clear(); payload_data.clear(); video_seq_in = ntohs(((rtp_header_t*)video_in.data())->seq); rc = g_rxUACVideo.processIncomingPacket(video_seq_in, video_in, rtp_header, payload_data); printVideoHex("RXUACVIDEO -- processIncomingPacket() rc == ", "", 0, rc, 0); host_flags = ntohs(((rtp_header_t*)video_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)video_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)video_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)video_in.data())->ssrc_id); video_in[0] = (host_flags >> 8) & 0xFF; video_in[1] = host_flags & 0xFF; video_in[2] = (host_seqnum >> 8) & 0xFF; video_in[3] = host_seqnum & 0xFF; video_in[4] = (host_timestamp >> 24) & 0xFF; video_in[5] = (host_timestamp >> 16) & 0xFF; video_in[6] = (host_timestamp >> 8) & 0xFF; video_in[7] = host_timestamp & 0xFF; video_in[8] = (host_ssrc >> 24) & 0xFF; video_in[9] = (host_ssrc >> 16) & 0xFF; video_in[10] = (host_ssrc >> 8) & 0xFF; video_in[11] = host_ssrc & 0xFF; memset(udp_recv_video.buffer, 0, sizeof(udp_recv_video)); memcpy(udp_recv_video.buffer, rtp_header.data(), rtp_header.size()); memcpy(udp_recv_video.buffer + sizeof(rtp_header_t), payload_data.data(), payload_data.size()); } else #endif // USE_TLS { // NOENCRYPTION host_flags = ntohs(((rtp_header_t*)video_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)video_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)video_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)video_in.data())->ssrc_id); video_in[0] = (host_flags >> 8) & 0xFF; video_in[1] = host_flags & 0xFF; video_in[2] = (host_seqnum >> 8) & 0xFF; video_in[3] = host_seqnum & 0xFF; video_in[4] = (host_timestamp >> 24) & 0xFF; video_in[5] = (host_timestamp >> 16) & 0xFF; video_in[6] = (host_timestamp >> 8) & 0xFF; video_in[7] = host_timestamp & 0xFF; video_in[8] = (host_ssrc >> 24) & 0xFF; video_in[9] = (host_ssrc >> 16) & 0xFF; video_in[10] = (host_ssrc >> 8) & 0xFF; video_in[11] = host_ssrc & 0xFF; memset(udp_recv_video.buffer, 0, sizeof(udp_recv_video)); memcpy(udp_recv_video.buffer, video_in.data(), video_in.size()); } // VALIDATION TEST compresult = 0; compresult = memcmp(udp_send_video.buffer + sizeof(rtp_header_t), udp_recv_video.buffer + sizeof(rtp_header_t), taskinfo->video_bytes_per_packet /* PAYLOAD comparison ONLY -- header EXCLUDED*/); if (compresult == 0) { // SUCCESS printVideoHex("COMPARISON OK ", "", 0, taskinfo->video_comparison_errors, rtpstream_vpckts); *comparison_vcheck = 0; } else { // FAILURE taskinfo->video_comparison_errors++; printVideoHex("COMPARISON FAILED", "", 0, taskinfo->video_comparison_errors, rtpstream_vpckts); *comparison_vcheck = 1; } } else { taskinfo->video_comparison_errors++; printVideoHex("NODATA", "", 0, taskinfo->video_comparison_errors, rtpstream_vpckts); *comparison_vcheck = 1; } /* advance playback pointer to next packet */ taskinfo->video_seq_out++; /* must change if timer ticks per packet can be fractional */ taskinfo->last_video_timestamp += taskinfo->video_timeticks_per_packet; taskinfo->video_file_bytes_left -= taskinfo->video_bytes_per_packet; if (taskinfo->video_file_bytes_left > 0) { taskinfo->video_current_file_bytes += taskinfo->video_bytes_per_packet; } else { taskinfo->video_current_file_bytes = taskinfo->video_file_bytes_start - taskinfo->video_file_bytes_left; taskinfo->video_file_bytes_left += taskinfo->video_file_num_bytes; if (taskinfo->video_loop_count > 0) { /* one less loop to play. -1 (infinite loops) will stay as is */ taskinfo->video_loop_count--; } } if (taskinfo->last_video_timestamp < target_timestamp) { /* no sleep if we are behind */ next_wake = timenow_ms; } } /* if (rc < 0) */ pthread_mutex_unlock(&uacVideoMutex); } /* if (taskinfo->last_video_timestamp < target_timestamp) */ else { printVideoHex("TIMESTAMP NOT QUITE RIGHT...", "", 0, 0, 0); *comparison_vcheck = -1; } } /* if (taskinfo->video_loop_count) */ else { /* not busy playing back a file - put possible rtp echo code here. */ } } if (taskinfo->video_rtcp_socket != -1) { /* just keep listening on rtcp socket (is this really required?) - ignore any errors */ while ((rc = recv(taskinfo->video_rtcp_socket, udp_recv_temp.buffer, sizeof(udp_recv_temp), 0)) >= 0) { /* * rtpstream_vbytes_in += rc; */ } } return next_wake; } /* code checked */ static void* rtpstream_playback_thread(void* params) { threaddata_t *threaddata = (threaddata_t *) params; taskentry_t *taskinfo; unsigned int taskindex; unsigned long timenow_ms; unsigned long waketime_ms; int sleeptime_us; unsigned long comparison_acheck; unsigned long comparison_vcheck; unsigned long rtpresult; std::vector rs_apackets; std::vector rs_vpackets; std::vector rs_artpcheck; std::vector rs_vrtpcheck; double verdict; comparison_acheck = 0; comparison_vcheck = 0; rtpresult = 0; /* includes BOTH AUDIO/VIDEO checks */ rs_apackets.resize(threaddata->max_tasks); rs_vpackets.resize(threaddata->max_tasks); rs_artpcheck.resize(threaddata->max_tasks); rs_vrtpcheck.resize(threaddata->max_tasks); verdict = 0.0; rtpstream_numthreads++; /* perhaps wrap this in a mutex? */ // INITIALIZE AUDIO/VIDEO COMPARISON ERRORS for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { (&threaddata->tasklist)[taskindex]->audio_comparison_errors = 0; (&threaddata->tasklist)[taskindex]->video_comparison_errors = 0; } // ROBUSTNESS CHECK for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { taskinfo = (&threaddata->tasklist)[taskindex]; if (taskinfo->audio_active) { if ( (taskinfo->new_audio_ms_per_packet == 0) || (taskinfo->new_audio_loop_count < -1) || (taskinfo->new_audio_pattern_id < -1) || (taskinfo->new_audio_pattern_id > 6) || (taskinfo->new_audio_payload_type < 0) || (taskinfo->new_audio_payload_type > 127) ) { // AUDIO VALIDATION FAILED -- ABORT MISSION threaddata->exit_flag = 1; } } else if (taskinfo->video_active) { if ( (taskinfo->new_video_ms_per_packet == 0) || (taskinfo->new_video_loop_count < -1) || (taskinfo->new_video_pattern_id < -1) || (taskinfo->new_video_pattern_id > 6) || (taskinfo->new_video_payload_type < 0) || (taskinfo->new_video_payload_type > 127) ) { // VIDEO VALIDATION FAILED -- ABORT MISSION threaddata->exit_flag = 1; } } } while (!threaddata->exit_flag) { timenow_ms = getmilliseconds(); waketime_ms = timenow_ms + 100; /* default sleep 100ms */ /* iterate through tasks and handle playback and other actions */ for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { printAudioHex("----DEBUG CURRENTTASK/NUMTASKS----", "", 0, taskindex, threaddata->num_tasks); printVideoHex("----DEBUG CURRENTTASK/NUMTASKS----", "", 0, taskindex, threaddata->num_tasks); taskinfo = (&threaddata->tasklist)[taskindex]; if (taskinfo->flags & TI_CONFIGFLAGS) { if (taskinfo->flags & TI_KILLTASK) { /* remove this task entry and release its resources */ pthread_mutex_lock(&(threaddata->tasklist_mutex)); (&threaddata->tasklist)[taskindex--] = (&threaddata->tasklist)[--threaddata->num_tasks]; threaddata->del_pending--; /* must decrease del_pending after num_tasks */ pthread_mutex_unlock(&(threaddata->tasklist_mutex)); rtpstream_free_taskinfo(taskinfo); continue; } /* handle any other config related flags */ rtpstream_process_task_flags(taskinfo); } /* should we update current time inbetween tasks? */ if (taskinfo->nextwake_ms <= timenow_ms) { /* task needs to execute now */ taskinfo->nextwake_ms = rtpstream_playrtptask(taskinfo, timenow_ms, &comparison_acheck, rs_apackets, &comparison_vcheck, rs_vpackets, taskindex); if (comparison_acheck == 1) { rs_artpcheck[taskindex]++; printAudioHex("----FAILED RTP CHECK----", "", 0, rs_artpcheck[taskindex], rtpstream_apckts); } else { printAudioHex("----PASSED RTP CHECK----", "", 0, rs_artpcheck[taskindex], rtpstream_apckts); } if (comparison_vcheck == 1) { rs_vrtpcheck[taskindex]++; printVideoHex("----FAILED RTP CHECK----", "", 0, rs_vrtpcheck[taskindex], rtpstream_vpckts); } else { printVideoHex("----PASSED RTP CHECK----", "", 0, rs_vrtpcheck[taskindex], rtpstream_vpckts); } } if (waketime_ms > taskinfo->nextwake_ms) { waketime_ms = taskinfo->nextwake_ms; } } /* sleep until next iteration of playback loop */ sleeptime_us = (waketime_ms - getmilliseconds()) * 1000; if (sleeptime_us > 0) { usleep(sleeptime_us); } } // EXITING... CALCULATE RESULT printAudioVector("----RTPCHECKS----", rs_artpcheck); printVideoVector("----RTPCHECKS----", rs_vrtpcheck); printAudioVector("----PACKET COUNTS----", rs_apackets); printVideoVector("----PACKET COUNTS----", rs_vpackets); for (unsigned int i = 0; i < threaddata->max_tasks; i++) { taskinfo = (&threaddata->tasklist)[i]; if (rs_apackets[i] > 0) { verdict = ((double)rs_artpcheck[i] / (double)rs_apackets[i]); if (verdict >= audiotolerance) { // PACKETS TRANSMITTED IN TASK -- RTP CHECK FAILED set_bit(&rtpresult, taskinfo->audio_pattern_id); } else { // PACKETS TRANSMITTED IN TASK -- RTP CHECK SUCCEEDED // // FIXME // // "rtpresult" is currently limiting us in reporting detailed // results of per-task RTP check success/failures -- // therefore at the present time we use it to indicate // the combined results of ALL tasks RTP checks for ALL // RTP patterns -- which means that bits are currently only // set for a given pattern in a given task if its RTP check // has failed -- this does not matter if its RTP check has // succeeded since "rtpresult" is initialized to ZERO by // default... } } else { // NO PACKETS TRANSMITTED IN TASK -- NO-OP... } if (rs_vpackets[i] > 0) { verdict = ((double)rs_vrtpcheck[i] / (double)rs_vpackets[i]); if (verdict >= videotolerance) { // PACKETS TRANSMITTED IN TASK -- RTP CHECK FAILED set_bit(&rtpresult, taskinfo->video_pattern_id); } else { // PACKETS TRANSMITTED IN TASK -- RTP CHECK SUCCEEDED // // FIXME // // "rtpresult" is currently limiting us in reporting detailed // results of per-task RTP check success/failures -- // therefore at the present time we use it to indicate // the combined results of ALL tasks RTP checks for ALL // RTP patterns -- which means that bits are currently only // set for a given pattern in a given task if its RTP check // has failed -- this does not matter if its RTP check has // succeeded since "rtpresult" is initialized to ZERO by // default... } } else { // NO PACKETS TRANSMITTED IN TASK -- NO-OP... } } /* Free all task and thread resources and exit the thread */ for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { /* check if we should delete this thread, else let owner call clear it */ /* small chance of race condition in this code */ taskinfo = (&threaddata->tasklist)[taskindex]; if (taskinfo->flags & TI_KILLTASK) { rtpstream_free_taskinfo(taskinfo); } else { taskinfo->parent_thread = NULL; /* no longer associated with a thread */ } } pthread_mutex_destroy(&(threaddata->tasklist_mutex)); free(threaddata); rtpstream_numthreads--; /* perhaps wrap this in a mutex? */ // PTHREAD EXIT... printAudioHex("PLAYBACK THREAD EXITING...", "", 0, rtpresult, 0); printVideoHex("PLAYBACK THREAD EXITING...", "", 0, rtpresult, 0); pthread_exit((void*) rtpresult); return NULL; } /* code checked */ static int rtpstream_start_task(rtpstream_callinfo_t* callinfo) { int ready_index; int allocsize; threaddata_t **threadlist; threaddata_t *threaddata; pthread_t threadID; /* safety check... */ if (!callinfo->taskinfo) { return 0; } /* we count on the fact that only one thread can add/remove playback tasks */ /* thus we don't have mutexes to protect the thread list objects. */ for (ready_index = 0; ready_index < num_ready_threads; ready_index++) { /* ready threads have a spare task slot or should have one very shortly */ /* if we find a task with no spare slots, just skip to the next one. */ if (ready_threads[ready_index]->num_tasks < ready_threads[ready_index]->max_tasks) { /* we found a thread with an open task slot. */ break; } } if (ready_index == num_ready_threads) { /* did not find a thread with spare task slots, thus we create one here */ if (num_ready_threads >= ready_threads_max) { /* need to allocate more memory for thread list */ ready_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(ready_threads, sizeof(*ready_threads) * ready_threads_max); if (!threadlist) { /* could not allocate bigger block... worry [about it later] */ ready_threads_max -= RTPSTREAM_THREADBLOCKSIZE; return 0; } ready_threads = threadlist; } /* create and initialise data structure for new thread */ allocsize = sizeof(*threaddata) + sizeof(threaddata->tasklist) * (rtp_tasks_per_thread - 1); threaddata = (threaddata_t *) malloc(allocsize); if (!threaddata) { return 0; } memset(threaddata, 0, allocsize); threaddata->max_tasks = rtp_tasks_per_thread; threaddata->busy_list_index = -1; pthread_mutex_init(&(threaddata->tasklist_mutex), NULL); /* create the thread itself */ if (pthread_create(&threadID, NULL, rtpstream_playback_thread, threaddata)) { /* error creating the thread */ free(threaddata); return 0; } threaddata->id = threadID; printAudioHex("CREATED THREAD: ", "", 0, getThreadId(threadID), 0); printVideoHex("CREATED THREAD: ", "", 0, getThreadId(threadID), 0); /* Add thread to list of ready (spare capacity) threads */ ready_threads[num_ready_threads++] = threaddata; } /* now add new task to a spare slot in our thread tasklist */ threaddata = ready_threads[ready_index]; callinfo->taskinfo->parent_thread = threaddata; callinfo->threadID = threaddata->id; pthread_mutex_lock(&(threaddata->tasklist_mutex)); (&threaddata->tasklist)[threaddata->num_tasks++] = callinfo->taskinfo; pthread_mutex_unlock(&(threaddata->tasklist_mutex)); /* this check relies on playback thread to decrement num_tasks before */ /* decrementing del_pending -- else we need to lock before this test */ if ((threaddata->del_pending == 0) && (threaddata->num_tasks >= threaddata->max_tasks)) { /* move this thread to the busy list - no free task slots */ /* first check if the busy list is big enough to hold new thread */ if (num_busy_threads >= busy_threads_max) { /* need to allocate more memory for thread list */ busy_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(busy_threads, sizeof(*busy_threads) * busy_threads_max); if (!threadlist) { /* could not allocate bigger block... leave thread in ready list */ busy_threads_max -= RTPSTREAM_THREADBLOCKSIZE; return 1; /* success, sort of */ } busy_threads = threadlist; } /* add to busy list */ threaddata->busy_list_index = num_busy_threads; busy_threads[num_busy_threads++] = threaddata; /* remove from ready list */ ready_threads[ready_index] = ready_threads[--num_ready_threads]; } return 1; /* done! */ } /* code checked */ static void rtpstream_stop_task(rtpstream_callinfo_t* callinfo) { threaddata_t **threadlist; taskentry_t *taskinfo = callinfo->taskinfo; int busy_index; if (taskinfo) { if (taskinfo->parent_thread) { /* this call's task is registered with an executing thread */ /* first move owning thread to the ready list - will be ready soon */ busy_index = taskinfo->parent_thread->busy_list_index; if (busy_index >= 0) { /* make sure we have enough entries in ready list */ if (num_ready_threads >= ready_threads_max) { /* need to allocate more memory for thread list */ ready_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(ready_threads, sizeof(*ready_threads) * ready_threads_max); if (!threadlist) { /* could not allocate bigger block... reset max threads */ /* this is a problem - ready thread gets "lost" on busy list */ ready_threads_max -= RTPSTREAM_THREADBLOCKSIZE; } else { ready_threads = threadlist; } } if (num_ready_threads < ready_threads_max) { /* OK, got space on ready list, move to ready list */ busy_threads[busy_index]->busy_list_index = -1; ready_threads[num_ready_threads++] = busy_threads[busy_index]; num_busy_threads--; /* fill up gap in the busy thread list */ if (busy_index != num_busy_threads) { busy_threads[busy_index] = busy_threads[num_busy_threads]; busy_threads[busy_index]->busy_list_index = busy_index; } } } /* then ask the thread to destory this task (and its memory) */ pthread_mutex_lock(&(taskinfo->parent_thread->tasklist_mutex)); taskinfo->parent_thread->del_pending++; taskinfo->flags |= TI_KILLTASK; pthread_mutex_unlock(&(taskinfo->parent_thread->tasklist_mutex)); // PTHREAD IS NOT JOINABLE HERE... } else { /* no playback thread owner, just free it */ rtpstream_free_taskinfo(taskinfo); } callinfo->taskinfo = NULL; } } /* code checked */ int rtpstream_new_call(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_new_call callinfo=%p\n", callinfo); taskentry_t *taskinfo; /* general init */ memset(callinfo, 0, sizeof(*callinfo)); // zero remote audio/video ports callinfo->remote_audioport = 0; callinfo->remote_videoport = 0; taskinfo = (taskentry_t *) malloc(sizeof(*taskinfo)); if (!taskinfo) { /* cannot allocate taskinfo memory - bubble error up */ return 0; } callinfo->taskinfo = taskinfo; memset(taskinfo, 0, sizeof(*taskinfo)); taskinfo->flags = TI_NULLIP; /* socket descriptors */ taskinfo->audio_rtp_socket = -1; taskinfo->audio_rtcp_socket = -1; taskinfo->video_rtp_socket = -1; taskinfo->video_rtcp_socket = -1; #ifdef USE_TLS /* audio/video SRTP echo activity indicators */ taskinfo->audio_srtp_echo_active = 0; taskinfo->video_srtp_echo_active = 0; #endif // USE_TLS /* rtp stream members */ taskinfo->audio_ssrc_id = global_ssrc_id++; taskinfo->video_ssrc_id = global_ssrc_id++; /* pthread mutexes */ pthread_mutex_init(&(callinfo->taskinfo->mutex), NULL); return 1; } /* code checked */ void rtpstream_end_call(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_end_call callinfo=%p\n", callinfo); /* stop playback thread(s) for this call */ rtpstream_stop_task(callinfo); // zero remote audio/video ports callinfo->remote_audioport = 0; callinfo->remote_videoport = 0; } /* code checked */ int rtpstream_cache_file(char* filename, int mode /* 0: FILE -- 1: PATTERN */, int id, int bytes_per_packet, int stream_type) { int count = 0; cached_file_t *newfilecachelist; cached_pattern_t *newpatterncachelist; char *filecontents; struct stat statbuffer; FILE *f; debugprint("rtpstream_cache_file filename = %s mode = %d id = %d bytes_per_packet = %d stream_type = %d\n", filename, mode, id, bytes_per_packet, stream_type); if ((debugafile == NULL) && rtpcheck_debug && (stream_type == 0)) { debugafile = fopen("debugafile", "w"); if (debugafile == NULL) { /* error encountered opening audio debug file */ return -1; } } if ((debugvfile == NULL) && rtpcheck_debug && (stream_type == 1)) { debugvfile = fopen("debugvfile", "w"); if (debugvfile == NULL) { /* error encountered opening video debug file */ return -1; } } if (mode == 1) { if ((id < 1) || (id > NUMPATTERNS)) { /* invalid pattern ID specified */ return -1; } /* cached pattern entries are stored in a dynamically grown array. */ /* could use a binary (or avl) tree but number of files should */ /* be small and doesn't really justify the effort. */ while (count < num_cached_files) { count++; } if (!(num_cached_files%RTPSTREAM_FILESPERBLOCK)) { /* Time to allocate more memory for the next block of files */ newpatterncachelist = (cached_pattern_t*) realloc(cached_patterns, sizeof(*cached_patterns) * (num_cached_files + RTPSTREAM_FILESPERBLOCK)); if (!newpatterncachelist) { /* out of memory */ return -1; } cached_patterns = newpatterncachelist; } cached_patterns[num_cached_files].bytes = (char*)malloc(bytes_per_packet); if (cached_patterns[num_cached_files].bytes == NULL) { /* out of memory */ return -1; } if (id == 1) { memset(cached_patterns[num_cached_files].bytes, PATTERN1, bytes_per_packet); } else if (id == 2) { memset(cached_patterns[num_cached_files].bytes, PATTERN2, bytes_per_packet); } else if (id == 3) { memset(cached_patterns[num_cached_files].bytes, PATTERN3, bytes_per_packet); } else if (id == 4) { memset(cached_patterns[num_cached_files].bytes, PATTERN4, bytes_per_packet); } else if (id == 5) { memset(cached_patterns[num_cached_files].bytes, PATTERN5, bytes_per_packet); } else if (id == 6) { memset(cached_patterns[num_cached_files].bytes, PATTERN6, bytes_per_packet); } cached_patterns[num_cached_files].filesize = bytes_per_packet; cached_patterns[num_cached_files].id = id; return num_cached_files++; /* one new cached pattern */ } else { /* cached file entries are stored in a dynamically grown array. */ /* could use a binary (or avl) tree but number of files should */ /* be small and doesn't really justify the effort. */ while (count < num_cached_files) { if (!strcmp(cached_files[count].filename, filename)) { /* found the file already loaded. just return index */ return count; } count++; } /* Allocate memory and load file */ if (stat(filename, &statbuffer)) { /* could not get file information */ return -1; } f = fopen(filename, "rb"); if (!f) { /* could not open file */ return -1; } filecontents = (char *)malloc(statbuffer.st_size); if (!filecontents) { /* could not alloc mem */ return -1; } if (!fread(filecontents, statbuffer.st_size, 1, f)) { /* could not read file */ free(filecontents); return -1; } fclose(f); if (!(num_cached_files%RTPSTREAM_FILESPERBLOCK)) { /* Time to allocate more memory for the next block of files */ newfilecachelist = (cached_file_t*) realloc(cached_files, sizeof(*cached_files) * (num_cached_files + RTPSTREAM_FILESPERBLOCK)); if (!newfilecachelist) { /* out of memory */ free(filecontents); return -1; } cached_files = newfilecachelist; } cached_files[num_cached_files].bytes = filecontents; strncpy(cached_files[num_cached_files].filename, filename, sizeof(cached_files[num_cached_files].filename) - 1); cached_files[num_cached_files].filesize = statbuffer.st_size; return num_cached_files++; } } static int rtpstream_setsocketoptions(int sock) { /* set socket non-blocking */ int flags = fcntl(sock, F_GETFL, 0); if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { return 0; } /* set buffer size */ unsigned int buffsize = rtp_buffsize; /* Increase buffer sizes for this sockets */ if(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&buffsize, sizeof(buffsize))) { return 0; } if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&buffsize, sizeof(buffsize))) { return 0; } return 1; /* success */ } /* code checked */ static int rtpstream_get_localport(int* rtpsocket, int* rtcpsocket) { int port_number; int tries; struct sockaddr_storage address; int max_tries = (min_rtp_port < (max_rtp_port - 2)) ? 100 : 1; debugprint("rtpstream_get_localport\n"); next_rtp_port = min_rtp_port; /* initialise address family and IP address for media socket */ memset(&address, 0, sizeof(address)); address.ss_family = media_ip_is_ipv6 ? AF_INET6 : AF_INET; if ((media_ip_is_ipv6? inet_pton(AF_INET6, media_ip, &((_RCAST(struct sockaddr_in6 *, &address))->sin6_addr)): inet_pton(AF_INET, media_ip, &((_RCAST(struct sockaddr_in *, &address))->sin_addr))) != 1) { WARNING("Could not set up media IP for RTP streaming"); return 0; } /* create new UDP listen socket */ *rtpsocket = socket(media_ip_is_ipv6?PF_INET6:PF_INET, SOCK_DGRAM, 0); if (*rtpsocket == -1) { WARNING("Could not open socket for RTP streaming: %s", strerror(errno)); return 0; } for (tries = 0; tries < max_tries; tries++) { /* try a sequence of port numbers until we find one where we can bind */ /* should normally be the first port we try, unless we have long-running */ /* calls or somebody else is nicking ports. */ port_number = next_rtp_port; /* skip rtp ports in multiples of 2 (allow for rtp plus rtcp) */ next_rtp_port += 2; if (next_rtp_port > (max_rtp_port - 1)) { next_rtp_port = min_rtp_port; } sockaddr_update_port(&address, port_number); if (::bind(*rtpsocket, (sockaddr*)&address, sizeof(address)) == 0) { break; } } /* Exit here if we didn't get a suitable port for rtp stream */ if (tries == max_tries) { close(*rtpsocket); *rtpsocket = -1; WARNING("Could not bind port for RTP streaming after %d tries", tries); return 0; } if (!rtpstream_setsocketoptions(*rtpsocket)) { close(*rtpsocket); *rtpsocket = -1; WARNING("Could not set socket options for RTP streaming"); return 0; } /* create socket for rtcp - ignore any errors, we only bind so we * won't send icmp-port-unreachable when rtcp arrives */ *rtcpsocket = socket(media_ip_is_ipv6?PF_INET6:PF_INET, SOCK_DGRAM, 0); if (*rtcpsocket != -1) { /* try to bind it to our preferred address */ sockaddr_update_port(&address, port_number + 1); if (::bind(*rtcpsocket, (sockaddr *) (void *)&address, sizeof(address)) == 0) { /* could not bind the rtcp socket to required port. so we delete it */ close(*rtcpsocket); *rtcpsocket = -1; } if (!rtpstream_setsocketoptions(*rtcpsocket)) { close(*rtcpsocket); *rtcpsocket = -1; } } return port_number; } /* code checked */ int rtpstream_get_local_audioport(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_get_local_audioport callinfo=%p", callinfo); int rtp_socket; int rtcp_socket; if (!callinfo->taskinfo) { return 0; } if (callinfo->local_audioport) { /* already a port assigned to this call */ debugprint(" ==> %d\n", callinfo->local_audioport); return callinfo->local_audioport; } callinfo->local_audioport = rtpstream_get_localport(&rtp_socket, &rtcp_socket); debugprint(" ==> %d\n", callinfo->local_audioport); /* assign rtp and rtcp sockets to callinfo. must assign rtcp socket first */ callinfo->taskinfo->audio_rtcp_socket = rtcp_socket; callinfo->taskinfo->audio_rtp_socket = rtp_socket; /* start playback task if not already started */ if (!callinfo->taskinfo->parent_thread) { if (!rtpstream_start_task(callinfo)) { /* error starting playback task */ return 0; } } /* make sure the new socket gets bound to destination address (if any) */ callinfo->taskinfo->flags |= TI_RECONNECTSOCKET; return callinfo->local_audioport; } /* code checked */ int rtpstream_get_local_videoport(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_get_local_videoport callinfo=%p", callinfo); int rtp_socket; int rtcp_socket; if (!callinfo->taskinfo) { return 0; } if (callinfo->local_videoport) { /* already a port assigned to this call */ debugprint(" ==> %d\n", callinfo->local_videoport); return callinfo->local_videoport; } callinfo->local_videoport = rtpstream_get_localport(&rtp_socket, &rtcp_socket); debugprint(" ==> %d\n", callinfo->local_videoport); /* assign rtp and rtcp sockets to callinfo. must assign rtcp socket first */ callinfo->taskinfo->video_rtcp_socket = rtcp_socket; callinfo->taskinfo->video_rtp_socket = rtp_socket; /* start playback task if not already started */ if (!callinfo->taskinfo->parent_thread) { if (!rtpstream_start_task(callinfo)) { /* error starting playback task */ return 0; } } /* make sure the new socket gets bound to destination address (if any) */ callinfo->taskinfo->flags |= TI_RECONNECTSOCKET; return callinfo->local_videoport; } /* code checked */ void rtpstream_set_remote(rtpstream_callinfo_t* callinfo, int ip_ver, const char* ip_addr, int audio_port, int video_port) { struct sockaddr_storage address; struct in_addr *ip4_addr; struct in6_addr *ip6_addr; taskentry_t *taskinfo; unsigned count; int nonzero_ip; debugprint("rtpstream_set_remote callinfo=%p, ip_ver %d ip_addr %s audio %d video %d\n", callinfo, ip_ver, ip_addr, audio_port, video_port); taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return; } nonzero_ip = 0; taskinfo->flags |= TI_NULLIP; /// TODO: this (may) cause a gap in playback, if playback thread gets to exec while this is set and before new IP is checked. /* test that media ip address version match remote ip address version? */ /* initialise address family and IP address for remote socket */ memset(&address, 0, sizeof(address)); if (media_ip_is_ipv6) { /* process ipv6 address */ address.ss_family = AF_INET6; ip6_addr = &((_RCAST(struct sockaddr_in6 *, &address))->sin6_addr); if (inet_pton(AF_INET6, ip_addr, ip6_addr) == 1) { for (count = 0; count < sizeof(*ip6_addr); count++) { if (((char*)ip6_addr)[count]) { nonzero_ip = 1; break; } } } } else { /* process ipv4 address */ address.ss_family = AF_INET; ip4_addr = &((_RCAST(struct sockaddr_in *, &address))->sin_addr); if (inet_pton(AF_INET, ip_addr, ip4_addr) == 1) { for (count = 0; count < sizeof(*ip4_addr); count++) { if (((char*)ip4_addr)[count]) { nonzero_ip = 1; break; } } } } if (!nonzero_ip) { return; } /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->remote_audio_rtp_addr), 0, sizeof(taskinfo->remote_audio_rtp_addr)); memset(&(taskinfo->remote_audio_rtcp_addr), 0, sizeof(taskinfo->remote_audio_rtcp_addr)); memset(&(taskinfo->remote_video_rtp_addr), 0, sizeof(taskinfo->remote_video_rtp_addr)); memset(&(taskinfo->remote_video_rtcp_addr), 0, sizeof(taskinfo->remote_video_rtcp_addr)); /* Audio */ if (audio_port) { // store remote audio port for later reference callinfo->remote_audioport = audio_port; sockaddr_update_port(&address, audio_port); memcpy(&(taskinfo->remote_audio_rtp_addr), &address, sizeof(address)); sockaddr_update_port(&address, audio_port + 1); memcpy(&(taskinfo->remote_audio_rtcp_addr), &address, sizeof(address)); taskinfo->flags &= ~TI_NULL_AUDIOIP; } /* Video */ if (video_port) { // store remote video port for later reference callinfo->remote_videoport = video_port; sockaddr_update_port(&address, video_port); memcpy(&(taskinfo->remote_video_rtp_addr), &address, sizeof(address)); sockaddr_update_port(&address, video_port + 1); memcpy(&(taskinfo->remote_video_rtcp_addr), &address, sizeof(address)); taskinfo->flags &= ~TI_NULL_VIDEOIP; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock(&(taskinfo->mutex)); taskinfo->flags |= TI_RECONNECTSOCKET; /* may want to start a playback (listen) task here if no task running? */ /* only makes sense if we decide to send 0-filled packets on idle */ } #ifdef USE_TLS int rtpstream_set_srtp_audio_local(rtpstream_callinfo_t* callinfo, SrtpAudioInfoParams &p) { taskentry_t *taskinfo; taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return -1; } if (srtpcheck_debug) { if (debuglsrtpafile == NULL) { if (sendMode == MODE_CLIENT) { debuglsrtpafile = fopen("debuglsrtpafile_uac", "w"); } else if (sendMode == MODE_SERVER) { debuglsrtpafile = fopen("debuglsrtpafile_uas", "w"); } if (debuglsrtpafile == NULL) { /* error encountered opening local srtp debug file */ return -1; } } } printLocalAudioSrtpStuff(p); /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->local_srtp_audio_params), 0, sizeof(taskinfo->local_srtp_audio_params)); /* Audio */ if (p.audio_found) { taskinfo->local_srtp_audio_params.audio_found = true; taskinfo->local_srtp_audio_params.primary_audio_cryptotag = p.primary_audio_cryptotag; taskinfo->local_srtp_audio_params.secondary_audio_cryptotag = p.secondary_audio_cryptotag; strncpy(taskinfo->local_srtp_audio_params.primary_audio_cryptosuite, p.primary_audio_cryptosuite, 23); strncpy(taskinfo->local_srtp_audio_params.secondary_audio_cryptosuite, p.secondary_audio_cryptosuite, 23); strncpy(taskinfo->local_srtp_audio_params.primary_audio_cryptokeyparams, p.primary_audio_cryptokeyparams, 40); strncpy(taskinfo->local_srtp_audio_params.secondary_audio_cryptokeyparams, p.secondary_audio_cryptokeyparams, 40); taskinfo->local_srtp_audio_params.primary_unencrypted_audio_srtp = p.primary_unencrypted_audio_srtp; taskinfo->local_srtp_audio_params.secondary_unencrypted_audio_srtp = p.secondary_unencrypted_audio_srtp; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock(&(taskinfo->mutex)); if (srtpcheck_debug) { if (debuglsrtpafile) { fclose(debuglsrtpafile); debuglsrtpafile = NULL; } } return 0; } int rtpstream_set_srtp_audio_remote(rtpstream_callinfo_t* callinfo, SrtpAudioInfoParams &p) { taskentry_t *taskinfo; taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return -1; } if (srtpcheck_debug) { if (debugrsrtpafile == NULL) { if (sendMode == MODE_CLIENT) { debugrsrtpafile = fopen("debugrsrtpafile_uac", "w"); } else if (sendMode == MODE_SERVER) { debugrsrtpafile = fopen("debugrsrtpafile_uas", "w"); } if (debugrsrtpafile == NULL) { /* error encountered opening local srtp debug file */ return -1; } } } printRemoteAudioSrtpStuff(p); /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->remote_srtp_audio_params), 0, sizeof(taskinfo->remote_srtp_audio_params)); /* Audio */ if (p.audio_found) { taskinfo->remote_srtp_audio_params.audio_found = true; taskinfo->remote_srtp_audio_params.primary_audio_cryptotag = p.primary_audio_cryptotag; taskinfo->remote_srtp_audio_params.secondary_audio_cryptotag = p.secondary_audio_cryptotag; strncpy(taskinfo->remote_srtp_audio_params.primary_audio_cryptosuite, p.primary_audio_cryptosuite, 23); strncpy(taskinfo->remote_srtp_audio_params.secondary_audio_cryptosuite, p.secondary_audio_cryptosuite, 23); strncpy(taskinfo->remote_srtp_audio_params.primary_audio_cryptokeyparams, p.primary_audio_cryptokeyparams, 40); strncpy(taskinfo->remote_srtp_audio_params.secondary_audio_cryptokeyparams, p.secondary_audio_cryptokeyparams, 40); taskinfo->remote_srtp_audio_params.primary_unencrypted_audio_srtp = p.primary_unencrypted_audio_srtp; taskinfo->remote_srtp_audio_params.secondary_unencrypted_audio_srtp = p.secondary_unencrypted_audio_srtp; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock(&(taskinfo->mutex)); if (srtpcheck_debug) { if (debugrsrtpafile) { fclose(debugrsrtpafile); debugrsrtpafile = NULL; } } return 0; } int rtpstream_set_srtp_video_local(rtpstream_callinfo_t* callinfo, SrtpVideoInfoParams &p) { taskentry_t *taskinfo; taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return -1; } if (srtpcheck_debug) { if (debuglsrtpvfile == NULL) { if (sendMode == MODE_CLIENT) { debuglsrtpvfile = fopen("debuglsrtpvfile_uac", "w"); } else if (sendMode == MODE_SERVER) { debuglsrtpvfile = fopen("debuglsrtpvfile_uas", "w"); } if (debuglsrtpvfile == NULL) { /* error encountered opening local srtp debug file */ return -1; } } } printLocalVideoSrtpStuff(p); /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->local_srtp_video_params), 0, sizeof(taskinfo->local_srtp_video_params)); /* Video */ if (p.video_found) { taskinfo->local_srtp_video_params.video_found = true; taskinfo->local_srtp_video_params.primary_video_cryptotag = p.primary_video_cryptotag; taskinfo->local_srtp_video_params.secondary_video_cryptotag = p.secondary_video_cryptotag; strncpy(taskinfo->local_srtp_video_params.primary_video_cryptosuite, p.primary_video_cryptosuite, 23); strncpy(taskinfo->local_srtp_video_params.secondary_video_cryptosuite, p.secondary_video_cryptosuite, 23); strncpy(taskinfo->local_srtp_video_params.primary_video_cryptokeyparams, p.primary_video_cryptokeyparams, 40); strncpy(taskinfo->local_srtp_video_params.secondary_video_cryptokeyparams, p.secondary_video_cryptokeyparams, 40); taskinfo->local_srtp_video_params.primary_unencrypted_video_srtp = p.primary_unencrypted_video_srtp; taskinfo->local_srtp_video_params.secondary_unencrypted_video_srtp = p.secondary_unencrypted_video_srtp; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock(&(taskinfo->mutex)); if (srtpcheck_debug) { if (debuglsrtpvfile) { fclose(debuglsrtpvfile); debuglsrtpvfile = NULL; } } return 0; } int rtpstream_set_srtp_video_remote(rtpstream_callinfo_t* callinfo, SrtpVideoInfoParams &p) { taskentry_t *taskinfo; taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return -1; } if (srtpcheck_debug) { if (debugrsrtpvfile == NULL) { if (sendMode == MODE_CLIENT) { debugrsrtpvfile = fopen("debugrsrtpvfile_uac", "w"); } else if (sendMode == MODE_SERVER) { debugrsrtpvfile = fopen("debugrsrtpvfile_uas", "w"); } if (debugrsrtpvfile == NULL) { /* error encountered opening local srtp debug file */ return -1; } } } printRemoteVideoSrtpStuff(p); /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->remote_srtp_video_params), 0, sizeof(taskinfo->remote_srtp_video_params)); /* Video */ if (p.video_found) { taskinfo->remote_srtp_video_params.video_found = true; taskinfo->remote_srtp_video_params.primary_video_cryptotag = p.primary_video_cryptotag; taskinfo->remote_srtp_video_params.secondary_video_cryptotag = p.secondary_video_cryptotag; strncpy(taskinfo->remote_srtp_video_params.primary_video_cryptosuite, p.primary_video_cryptosuite, 23); strncpy(taskinfo->remote_srtp_video_params.secondary_video_cryptosuite, p.secondary_video_cryptosuite, 23); strncpy(taskinfo->remote_srtp_video_params.primary_video_cryptokeyparams, p.primary_video_cryptokeyparams, 40); strncpy(taskinfo->remote_srtp_video_params.secondary_video_cryptokeyparams, p.secondary_video_cryptokeyparams, 40); taskinfo->remote_srtp_video_params.primary_unencrypted_video_srtp = p.primary_unencrypted_video_srtp; taskinfo->remote_srtp_video_params.secondary_unencrypted_video_srtp = p.secondary_unencrypted_video_srtp; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock(&(taskinfo->mutex)); if (srtpcheck_debug) { if (debugrsrtpvfile) { fclose(debugrsrtpvfile); debugrsrtpvfile = NULL; } } return 0; } #endif // USE_TLS static inline uint32_t uint_val(const char *ptr) { // Read as little-endian. Do not dereference as int, since it can be misaligned. return static_cast((ptr[0]) | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); } // wav format details: // https://www.fatalerrors.org/a/detailed-explanation-of-wav-file-format.html static int get_wav_header_size(const char *data, int size) { const char *ptr = data; const char *limit = data + size; if (size < 42) return 0; // Since all the values are interpreted as little endian, the tags are reversed. if (uint_val(ptr) != 'FFIR') return 0; ptr += 8; if (uint_val(ptr) != 'EVAW') return ptr - data; ptr += 4; for (;;) { if (ptr + 8 > limit) break; const uint32_t chunk = uint_val(ptr); const uint32_t chunk_size = uint_val(ptr + 4); ptr += 8; if (ptr > limit) return limit - data; if (chunk == 'atad') return ptr - data; ptr += chunk_size; } return ptr - data; } /* code checked */ void rtpstream_play(rtpstream_callinfo_t* callinfo, rtpstream_actinfo_t* actioninfo) { debugprint("rtpstream_play callinfo=%p filename %s pattern_id %d loop %d bytes %d payload %d ptime %d tick %d\n", callinfo, actioninfo->filename, actioninfo->pattern_id, actioninfo->loop_count, actioninfo->bytes_per_packet, actioninfo->payload_type, actioninfo->ms_per_packet, actioninfo->ticks_per_packet); int file_index = rtpstream_cache_file(actioninfo->filename, 0 /* FILE MODE */, actioninfo->pattern_id, actioninfo->bytes_per_packet, 0 /* AUDIO */); taskentry_t *taskinfo = callinfo->taskinfo; if (file_index < 0) { return; /* cannot find file to play */ } if (!taskinfo) { return; /* no task data structure */ } /* make sure we have an open socket from which to play the audio file */ rtpstream_get_local_audioport(callinfo); /* save file parameter in taskinfo structure */ taskinfo->new_audio_pattern_id = actioninfo->pattern_id; taskinfo->new_audio_loop_count = actioninfo->loop_count; taskinfo->new_audio_bytes_per_packet = actioninfo->bytes_per_packet; taskinfo->new_audio_file_size = cached_files[file_index].filesize; taskinfo->new_audio_file_bytes = cached_files[file_index].bytes; taskinfo->new_audio_ms_per_packet = actioninfo->ms_per_packet; taskinfo->new_audio_timeticks_per_packet = actioninfo->ticks_per_packet; taskinfo->new_audio_payload_type = actioninfo->payload_type; taskinfo->audio_active = actioninfo->audio_active; taskinfo->video_active = actioninfo->video_active; /* Allow the caller to supply WAV files instead of raw audio, by skipping past headers. */ /* Doesn't actually parse/convert anything! */ const int header_size = get_wav_header_size(taskinfo->new_audio_file_bytes, taskinfo->new_audio_file_size); if (header_size > 0 && taskinfo->new_audio_file_size >= header_size) { taskinfo->new_audio_file_bytes += header_size; taskinfo->new_audio_file_size -= header_size; } /* set flag that we have a new file to play */ taskinfo->flags |= TI_PLAYFILE; } /* code checked */ void rtpstream_pause(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_pause callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags |= TI_PAUSERTP; } } /* code checked */ void rtpstream_resume(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_resume callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags &= ~TI_PAUSERTP; } } void rtpstream_playapattern(rtpstream_callinfo_t* callinfo, rtpstream_actinfo_t* actioninfo, JLSRTP& txUACAudio, JLSRTP& rxUACAudio) { debugprint("rtpstream_playapattern callinfo=%p filename %s pattern_id %d loop %d bytes %d payload %d ptime %d tick %d\n", callinfo, actioninfo->filename, actioninfo->pattern_id, actioninfo->loop_count, actioninfo->bytes_per_packet, actioninfo->payload_type, actioninfo->ms_per_packet, actioninfo->ticks_per_packet); int file_index = rtpstream_cache_file(actioninfo->filename, 1 /* PATTERN MODE */, actioninfo->pattern_id, actioninfo->bytes_per_packet, 0 /* AUDIO */); taskentry_t *taskinfo = callinfo->taskinfo; if (file_index < 0) { return; /* ERROR encountered */ } if (!taskinfo) { return; /* no task data structure */ } /* make sure we have an open socket from which to play the audio file */ rtpstream_get_local_audioport(callinfo); /* save file parameter in taskinfo structure */ taskinfo->new_audio_pattern_id = actioninfo->pattern_id; taskinfo->new_audio_payload_type = actioninfo->payload_type; taskinfo->new_audio_loop_count = actioninfo->loop_count; taskinfo->new_audio_file_size = cached_patterns[file_index].filesize; taskinfo->new_audio_file_bytes = cached_patterns[file_index].bytes; taskinfo->new_audio_ms_per_packet = actioninfo->ms_per_packet; taskinfo->new_audio_bytes_per_packet = actioninfo->bytes_per_packet; taskinfo->new_audio_timeticks_per_packet = actioninfo->ticks_per_packet; taskinfo->audio_comparison_errors = 0; taskinfo->audio_active = actioninfo->audio_active; taskinfo->video_active = actioninfo->video_active; /* set flag that we have a new file to play */ taskinfo->flags |= TI_PLAYAPATTERN; #ifdef USE_TLS pthread_mutex_lock(&uacAudioMutex); g_txUACAudio = txUACAudio; g_rxUACAudio = rxUACAudio; pthread_mutex_unlock(&uacAudioMutex); #endif // USE_TLS } void rtpstream_pauseapattern(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_pauseapattern callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags |= TI_PAUSERTPAPATTERN; } } void rtpstream_resumeapattern(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_resumeapattern callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags &= ~TI_PAUSERTPAPATTERN; } } void rtpstream_playvpattern(rtpstream_callinfo_t* callinfo, rtpstream_actinfo_t* actioninfo, JLSRTP& txUACVideo, JLSRTP& rxUACVideo) { debugprint("rtpstream_playvpattern callinfo=%p filename %s pattern_id %d loop %d bytes %d payload %d ptime %d tick %d\n", callinfo, actioninfo->filename, actioninfo->pattern_id, actioninfo->loop_count, actioninfo->bytes_per_packet, actioninfo->payload_type, actioninfo->ms_per_packet, actioninfo->ticks_per_packet); int file_index = rtpstream_cache_file(actioninfo->filename, 1 /* PATTERN MODE */, actioninfo->pattern_id, actioninfo->bytes_per_packet, 1 /* VIDEO */); taskentry_t *taskinfo = callinfo->taskinfo; if (file_index < 0) { return; /* ERROR encountered */ } if (!taskinfo) { return; /* no task data structure */ } /* make sure we have an open socket from which to play the video file */ rtpstream_get_local_videoport(callinfo); /* save file parameter in taskinfo structure */ taskinfo->new_video_pattern_id = actioninfo->pattern_id; taskinfo->new_video_payload_type = actioninfo->payload_type; taskinfo->new_video_loop_count = actioninfo->loop_count; taskinfo->new_video_file_size = cached_patterns[file_index].filesize; taskinfo->new_video_file_bytes = cached_patterns[file_index].bytes; taskinfo->new_video_ms_per_packet = actioninfo->ms_per_packet; taskinfo->new_video_bytes_per_packet = actioninfo->bytes_per_packet; taskinfo->new_video_timeticks_per_packet = actioninfo->ticks_per_packet; taskinfo->video_comparison_errors = 0; taskinfo->audio_active = actioninfo->audio_active; taskinfo->video_active = actioninfo->video_active; /* set flag that we have a new file to play */ taskinfo->flags |= TI_PLAYVPATTERN; #ifdef USE_TLS pthread_mutex_lock(&uacVideoMutex); g_txUACVideo = txUACVideo; g_rxUACVideo = rxUACVideo; pthread_mutex_unlock(&uacVideoMutex); #endif // USE_TLS } void rtpstream_pausevpattern(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_pausevpattern callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags |= TI_PAUSERTPVPATTERN; } } void rtpstream_resumevpattern(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_resumevpattern callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags &= ~TI_PAUSERTPVPATTERN; } } void rtpstream_audioecho_thread(void* param) { int exit_code = 0; #ifdef USE_TLS my_unique_ptr msg { reinterpret_cast(malloc(media_bufsize)) }; ssize_t nr; ssize_t ns; sipp_socklen_t len; struct sockaddr_storage remote_rtp_addr; sigset_t mask; int rc = 0; struct timespec tspec; int sock = 0; int flags; std::vector rtp_header; std::vector payload_data; std::vector audio_packet_in; std::vector audio_packet_out; unsigned short seq_num = 0; unsigned short host_flags = 0; unsigned short host_seqnum = 0; unsigned int host_timestamp = 0; unsigned int host_ssrc = 0; bool abnormal_termination = false; ParamPass p; tspec.tv_sec = 0; tspec.tv_nsec = 10000000; /* 10ms */ p.p = param; if (param != NULL) { sock = p.i; } if ((flags = fcntl(sock, F_GETFL, 0)) < 0) { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtp_audioecho_thread(): fcntl() GETFL UNBLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexaudio); pthread_exit((void*) 1); } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtp_audioecho_thread(): fcntl() SETFL UNBLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexaudio); pthread_exit((void*) 2); } sigfillset(&mask); /* Mask all allowed signals */ rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); if (rc) { //WARNING("pthread_sigmask returned %d in rtpstream_echo_thread", rc); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "pthread_sigmask returned %d in rtpstream_audioecho_thread", rc); } pthread_mutex_unlock(&debugremutexaudio); pthread_exit((void*) 3); } pthread_mutex_lock(&quit_mutexaudio); while (!quit_audioecho_thread) { rc = pthread_cond_timedwait(&quit_cvaudio, &quit_mutexaudio, &tspec); if ((rc == ETIMEDOUT) && !quit_audioecho_thread) { pthread_mutex_lock(&uasAudioMutex); nr = 0; memset(msg.get(), 0, media_bufsize); len = sizeof(remote_rtp_addr); audio_packet_in.resize(sizeof(rtp_header_t) + g_rxUASAudio.getSrtpPayloadSize() + g_rxUASAudio.getAuthenticationTagSize(), 0); nr = recvfrom(sock, audio_packet_in.data(), audio_packet_in.size(), MSG_DONTWAIT /* NON-BLOCKING */, (sockaddr *) (void *) &remote_rtp_addr, &len); if (nr >= 0) { // Good to go -- buffer should contain "nr" bytes seq_num = 0; seq_num = (audio_packet_in[2] << 8) | audio_packet_in[3]; pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "DATA SUCCESSFULLY RECEIVED [AUDIO] nr = %ld...", nr); } for (int i = 0; i < 12; i++) { if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "%02X", 0xFFFFFFFF & audio_packet_in[i]); } } if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "\n"); } pthread_mutex_unlock(&debugremutexaudio); if (g_rxUASAudio.getCryptoTag() != 0) { rtp_header.clear(); payload_data.clear(); // DECRYPT rc = g_rxUASAudio.processIncomingPacket(seq_num, audio_packet_in, rtp_header, payload_data); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "RXUASAUDIO -- processIncomingPacket() rc == %d\n", rc); } pthread_mutex_unlock(&debugremutexaudio); host_flags = ntohs(((rtp_header_t*)audio_packet_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)audio_packet_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)audio_packet_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)audio_packet_in.data())->ssrc_id); audio_packet_in[0] = (host_flags >> 8) & 0xFF; audio_packet_in[1] = host_flags & 0xFF; audio_packet_in[2] = (host_seqnum >> 8) & 0xFF; audio_packet_in[3] = host_seqnum & 0xFF; audio_packet_in[4] = (host_timestamp >> 24) & 0xFF; audio_packet_in[5] = (host_timestamp >> 16) & 0xFF; audio_packet_in[6] = (host_timestamp >> 8) & 0xFF; audio_packet_in[7] = host_timestamp & 0xFF; audio_packet_in[8] = (host_ssrc >> 24) & 0xFF; audio_packet_in[9] = (host_ssrc >> 16) & 0xFF; audio_packet_in[10] = (host_ssrc >> 8) & 0xFF; audio_packet_in[11] = host_ssrc & 0xFF; memcpy(msg.get(), rtp_header.data(), rtp_header.size()); memcpy(msg.get() + sizeof(rtp_header_t), payload_data.data(), payload_data.size()); } if (g_txUASAudio.getCryptoTag() != 0) { audio_packet_out.clear(); // GRAB RTP HEADER rtp_header.resize(sizeof(rtp_header_t), 0); memcpy(rtp_header.data(), msg.get(), sizeof(rtp_header_t) /*12*/); // GRAB RTP PAYLOAD DATA payload_data.resize(g_txUASAudio.getSrtpPayloadSize(), 0); memcpy(payload_data.data(), msg.get() + sizeof(rtp_header_t), g_txUASAudio.getSrtpPayloadSize()); // ENCRYPT rc = g_txUASAudio.processOutgoingPacket(seq_num, rtp_header, payload_data, audio_packet_out); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "TXUASAUDIO -- processOutgoingPacket() rc == %d\n", rc); } pthread_mutex_unlock(&debugremutexaudio); } ns = sendto(sock, audio_packet_out.data(), sizeof(rtp_header_t) + g_txUASAudio.getSrtpPayloadSize() + g_txUASAudio.getAuthenticationTagSize(), MSG_DONTWAIT, (sockaddr *) (void *) &remote_rtp_addr, len); if (ns != nr) { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "DATA SUCCESSFULLY SENT [AUDIO] seq_num = [%u] -- MISMATCHED RECV/SENT BYTE COUNT -- errno = %d nr = %ld ns = %ld\n", seq_num, errno, nr, ns); } pthread_mutex_unlock(&debugremutexaudio); } else { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "DATA SUCCESSFULLY SENT [AUDIO] seq_num = [%u]...\n", seq_num); } pthread_mutex_unlock(&debugremutexaudio); } rtp_pckts++; rtp_bytes += ns; } else if ((nr < 0) && (errno == EAGAIN)) { // No data to be read (no activity on socket) //pthread_mutex_lock(&debugremutexaudio); //if (debugrefileaudio != NULL) //{ // fprintf(debugrefileaudio, "No activity on audioecho socket (EAGAIN)...\n"); //} //pthread_mutex_unlock(&debugremutexaudio); } else { // Other error occurred during read //WARNING("%s %i", "Error on RTP echo reception - stopping rtpstream echo - errno = ", errno); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "Error on RTP echo reception - unable to perform rtpstream audioecho - errno = %d\n", errno); } pthread_mutex_unlock(&debugremutexaudio); abnormal_termination = true; } pthread_mutex_unlock(&uasAudioMutex); } else { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtp_audioecho_thread(): pthread_cond_timedwait() non-timeout: rc: %d quit_audioecho_thread: %d\n", rc, quit_audioecho_thread); } pthread_mutex_unlock(&debugremutexaudio); } } pthread_mutex_unlock(&quit_mutexaudio); if ((flags = fcntl(sock, F_GETFL, 0)) < 0) { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtp_audioecho_thread(): fcntl() GETFL BLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexaudio); pthread_exit((void*) 6); } if (fcntl(sock, F_SETFL, flags & (~O_NONBLOCK)) < 0) { pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtp_audioecho_thread(): fcntl() SETFL BLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexaudio); pthread_exit((void*) 7); } if (abnormal_termination) { exit_code = -1; } else { exit_code = 0; } #else // !USE_TLS exit_code = 0; // dummy #endif // USE_TLS pthread_exit((void*) (intptr_t) exit_code); } void rtpstream_videoecho_thread(void* param) { int exit_code = 0; #ifdef USE_TLS my_unique_ptr msg { reinterpret_cast(malloc(media_bufsize)) }; ssize_t nr; ssize_t ns; sipp_socklen_t len; struct sockaddr_storage remote_rtp_addr; sigset_t mask; int rc = 0; struct timespec tspec; int sock = 0; int flags; std::vector rtp_header; std::vector payload_data; std::vector video_packet_in; std::vector video_packet_out; unsigned short seq_num = 0; unsigned short host_flags = 0; unsigned short host_seqnum = 0; unsigned int host_timestamp = 0; unsigned int host_ssrc = 0; bool abnormal_termination = false; ParamPass p; tspec.tv_sec = 0; tspec.tv_nsec = 10000000; /* 10ms */ p.p = param; if (param != NULL) { sock = p.i; } if ((flags = fcntl(sock, F_GETFL, 0)) < 0) { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtp_videoecho_thread(): fcntl() GETFL UNBLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexvideo); pthread_exit((void*) 1); } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtp_videoecho_thread(): fcntl() SETFL UNBLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexvideo); pthread_exit((void*) 2); } sigfillset(&mask); /* Mask all allowed signals */ rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); if (rc) { //WARNING("pthread_sigmask returned %d in rtpstream_echo_thread", rc); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "pthread_sigmask returned %d in rtpstream_videoecho_thread", rc); } pthread_mutex_unlock(&debugremutexvideo); pthread_exit((void*) 3); } pthread_mutex_lock(&quit_mutexvideo); while (!quit_videoecho_thread) { rc = pthread_cond_timedwait(&quit_cvvideo, &quit_mutexvideo, &tspec); if ((rc == ETIMEDOUT) && !quit_videoecho_thread) { pthread_mutex_lock(&uasVideoMutex); nr = 0; memset(msg.get(), 0, media_bufsize); len = sizeof(remote_rtp_addr); video_packet_in.resize(sizeof(rtp_header_t) + g_rxUASVideo.getSrtpPayloadSize() + g_rxUASVideo.getAuthenticationTagSize(), 0); nr = recvfrom(sock, video_packet_in.data(), video_packet_in.size(), MSG_DONTWAIT /* NON-BLOCKING */, (sockaddr *) (void *) &remote_rtp_addr, &len); if (nr >= 0) { // Good to go -- buffer should contain "nr" bytes seq_num = 0; seq_num = (video_packet_in[2] << 8) | video_packet_in[3]; pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "DATA SUCCESSFULLY RECEIVED [VIDEO] nr = %ld...", nr); } for (int i = 0; i < 12; i++) { if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "%02X", 0xFFFFFFFF & video_packet_in[i]); } } if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "\n"); } pthread_mutex_unlock(&debugremutexvideo); if (g_rxUASVideo.getCryptoTag() != 0) { rtp_header.clear(); payload_data.clear(); // DECRYPT rc = g_rxUASVideo.processIncomingPacket(seq_num, video_packet_in, rtp_header, payload_data); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "RXUASVIDEO -- processIncomingPacket() rc == %d\n", rc); } pthread_mutex_unlock(&debugremutexvideo); host_flags = ntohs(((rtp_header_t*)video_packet_in.data())->flags); host_seqnum = ntohs(((rtp_header_t*)video_packet_in.data())->seq); host_timestamp = ntohl(((rtp_header_t*)video_packet_in.data())->timestamp); host_ssrc = ntohl(((rtp_header_t*)video_packet_in.data())->ssrc_id); video_packet_in[0] = (host_flags >> 8) & 0xFF; video_packet_in[1] = host_flags & 0xFF; video_packet_in[2] = (host_seqnum >> 8) & 0xFF; video_packet_in[3] = host_seqnum & 0xFF; video_packet_in[4] = (host_timestamp >> 24) & 0xFF; video_packet_in[5] = (host_timestamp >> 16) & 0xFF; video_packet_in[6] = (host_timestamp >> 8) & 0xFF; video_packet_in[7] = host_timestamp & 0xFF; video_packet_in[8] = (host_ssrc >> 24) & 0xFF; video_packet_in[9] = (host_ssrc >> 16) & 0xFF; video_packet_in[10] = (host_ssrc >> 8) & 0xFF; video_packet_in[11] = host_ssrc & 0xFF; memcpy(msg.get(), rtp_header.data(), rtp_header.size()); memcpy(msg.get() + sizeof(rtp_header_t), payload_data.data(), payload_data.size()); } if (g_txUASVideo.getCryptoTag() != 0) { video_packet_out.clear(); // ENCRYPT // GRAB RTP HEADER rtp_header.resize(sizeof(rtp_header_t), 0); memcpy(rtp_header.data(), msg.get(), sizeof(rtp_header_t) /*12*/); // GRAB RTP PAYLOAD DATA payload_data.resize(g_txUASVideo.getSrtpPayloadSize(), 0); memcpy(payload_data.data(), msg.get() + sizeof(rtp_header_t), g_txUASVideo.getSrtpPayloadSize()); // ENCRYPT rc = g_txUASVideo.processOutgoingPacket(seq_num, rtp_header, payload_data, video_packet_out); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "TXUASVIDEO -- processOutgoingPacket() rc == %d\n", rc); } pthread_mutex_unlock(&debugremutexvideo); } ns = sendto(sock, video_packet_out.data(), sizeof(rtp_header_t) + g_txUASVideo.getSrtpPayloadSize() + g_txUASVideo.getAuthenticationTagSize(), MSG_DONTWAIT, (sockaddr *) (void *) &remote_rtp_addr, len); if (ns != nr) { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "DATA SUCCESSFULLY SENT [VIDEO] seq_num = [%u] -- MISMATCHED RECV/SENT BYTE COUNT -- errno = %d nr = %ld ns = %ld\n", seq_num, errno, nr, ns); } pthread_mutex_unlock(&debugremutexvideo); } else { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "DATA SUCCESSFULLY SENT [VIDEO] seq_num[%u]...\n", seq_num); } pthread_mutex_unlock(&debugremutexvideo); } rtp2_pckts++; rtp2_bytes += ns; } else if ((nr < 0) && (errno == EAGAIN)) { // No data to be read (no activity on socket) //pthread_mutex_lock(&debugremutexvideo); //if (debugrefilevideo != NULL) //{ //fprintf(debugrefilevideo, "No activity on videoecho socket (EAGAIN)...\n"); //} //pthread_mutex_unlock(&debugremutexvideo); } else { // Other error occurred during read //WARNING("%s %i", "Error on RTP echo reception - stopping rtpstream echo - errno = ", errno); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "Error on RTP echo reception - unable to perform rtpstream videoecho - errno = %d\n", errno); } pthread_mutex_unlock(&debugremutexvideo); abnormal_termination = true; } pthread_mutex_unlock(&uasVideoMutex); } else { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtp_videoecho_thread(): pthread_cond_timedwait() non-timeout: rc: %d quit_videoecho_thread: %d\n", rc, quit_videoecho_thread); } pthread_mutex_unlock(&debugremutexvideo); } } pthread_mutex_unlock(&quit_mutexvideo); if ((flags = fcntl(sock, F_GETFL, 0)) < 0) { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtp_videoecho_thread(): fcntl() GETFL BLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexvideo); pthread_exit((void*) 6); } if (fcntl(sock, F_SETFL, flags & (~O_NONBLOCK)) < 0) { pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtp_videoecho_thread(): fcntl() SETFL BLOCK failed...\n"); } pthread_mutex_unlock(&debugremutexvideo); pthread_exit((void*) 7); } if (abnormal_termination) { exit_code = -1; } else { exit_code = 0; } #else // !USE_TLS exit_code = 0; // dummy #endif // USE_TLS pthread_exit((void*) (intptr_t) exit_code); } int rtpstream_rtpecho_startaudio(rtpstream_callinfo_t* callinfo, JLSRTP& rxUASAudio, JLSRTP& txUASAudio) { debugprint("rtpstream_rtpecho_startaudio callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS ParamPass p; taskinfo->audio_srtp_echo_active = 1; pthread_mutex_lock(&debugremutexaudio); if (srtpcheck_debug) { if (debugrefileaudio == NULL) { debugrefileaudio = fopen("debugrefileaudio", "w"); if (debugrefileaudio == NULL) { /* error encountered opening audio debug file */ pthread_mutex_lock(&debugremutexaudio); return -2; } } } pthread_mutex_unlock(&debugremutexaudio); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtpstream_rtpecho_startaudio reached...\n"); } printLocalAudioSrtpStuff(taskinfo->local_srtp_audio_params); printRemoteAudioSrtpStuff(taskinfo->remote_srtp_audio_params); pthread_mutex_unlock(&debugremutexaudio); /* Create first RTP echo thread for audio */ pthread_mutex_lock(&uasAudioMutex); g_rxUASAudio = rxUASAudio; g_txUASAudio = txUASAudio; pthread_mutex_unlock(&uasAudioMutex); p.i = taskinfo->audio_rtp_socket; if (taskinfo->audio_rtp_socket > 0) { if (pthread_create(&pthread_audioecho_id, NULL, (void *(*) (void *)) rtpstream_audioecho_thread, p.p) == -1) { ERROR_NO("Unable to create RTP audio echo thread"); return -7; } } #endif // USE_TLS return 0; } int rtpstream_rtpecho_updateaudio(rtpstream_callinfo_t* callinfo, JLSRTP& rxUASAudio, JLSRTP& txUASAudio) { debugprint("rtpstream_rtpecho_updateaudio callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS taskinfo->audio_srtp_echo_active = 1; pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtpstream_rtpecho_updateaudio reached...\n"); } printLocalAudioSrtpStuff(taskinfo->local_srtp_audio_params); printRemoteAudioSrtpStuff(taskinfo->remote_srtp_audio_params); pthread_mutex_unlock(&debugremutexaudio); pthread_mutex_lock(&uasAudioMutex); g_rxUASAudio = rxUASAudio; g_txUASAudio = txUASAudio; pthread_mutex_unlock(&uasAudioMutex); #endif // USE_TLS return 0; } int rtpstream_rtpecho_stopaudio(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_rtpecho_stopaudio callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; ResultCheck r; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS taskinfo->audio_srtp_echo_active = 0; pthread_mutex_lock(&quit_mutexaudio); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "MAIN: Setting quit_audioecho_thread flag to TRUE...\n"); } pthread_mutex_unlock(&debugremutexaudio); quit_audioecho_thread = true; pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "MAIN: Sending QUIT signal...\n"); } pthread_mutex_unlock(&debugremutexaudio); pthread_cond_signal(&quit_cvaudio); pthread_mutex_unlock(&quit_mutexaudio); pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "rtpstream_rtpecho_stopaudio reached...\n"); } printLocalAudioSrtpStuff(taskinfo->local_srtp_audio_params); printRemoteAudioSrtpStuff(taskinfo->remote_srtp_audio_params); pthread_mutex_unlock(&debugremutexaudio); if (pthread_join(pthread_audioecho_id, &r.p) == 0) { // successfully joined audio thread pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "successfully joined audio thread: %d\n", r.i); } pthread_mutex_unlock(&debugremutexaudio); } else { // error joining audio thread pthread_mutex_lock(&debugremutexaudio); if (debugrefileaudio != NULL) { fprintf(debugrefileaudio, "error joining audio thread: %d\n", r.i); } pthread_mutex_unlock(&debugremutexaudio); } pthread_mutex_lock(&debugremutexaudio); if (srtpcheck_debug) { if (debugrefileaudio) { fclose(debugrefileaudio); } } pthread_mutex_unlock(&debugremutexaudio); #else // !USE_TLS r.i = 0; // dummy #endif // USE_TLS return r.i; } int rtpstream_rtpecho_startvideo(rtpstream_callinfo_t* callinfo, JLSRTP& rxUASVideo, JLSRTP& txUASVideo) { debugprint("rtpstream_rtpecho_startvideo callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS ParamPass p; taskinfo->video_srtp_echo_active = 1; pthread_mutex_lock(&debugremutexvideo); if (srtpcheck_debug) { if (debugrefilevideo == NULL) { debugrefilevideo = fopen("debugrefilevideo", "w"); if (debugrefilevideo == NULL) { /* error encountered opening audio debug file */ pthread_mutex_unlock(&debugremutexvideo); return -2; } } } pthread_mutex_unlock(&debugremutexvideo); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtpstream_rtpecho_startvideo reached...\n"); } printLocalVideoSrtpStuff(taskinfo->local_srtp_video_params); printRemoteVideoSrtpStuff(taskinfo->remote_srtp_video_params); pthread_mutex_unlock(&debugremutexvideo); /* Create second RTP echo thread for video */ pthread_mutex_lock(&uasVideoMutex); g_rxUASVideo = rxUASVideo; g_txUASVideo = txUASVideo; pthread_mutex_unlock(&uasVideoMutex); p.i = taskinfo->video_rtp_socket; if (taskinfo->video_rtp_socket > 0) { if (pthread_create(&pthread_videoecho_id, NULL, (void *(*) (void *)) rtpstream_videoecho_thread, p.p) == -1) { ERROR_NO("Unable to create RTP video echo thread"); return -8; } } #endif // USE_TLS return 0; } int rtpstream_rtpecho_updatevideo(rtpstream_callinfo_t* callinfo, JLSRTP& rxUASVideo, JLSRTP& txUASVideo) { debugprint("rtpstream_rtpecho_updatevideo callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS taskinfo->video_srtp_echo_active = 1; pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtpstream_rtpecho_updatevideo reached...\n"); } printLocalVideoSrtpStuff(taskinfo->local_srtp_video_params); printRemoteVideoSrtpStuff(taskinfo->remote_srtp_video_params); pthread_mutex_unlock(&debugremutexvideo); pthread_mutex_lock(&uasVideoMutex); g_rxUASVideo = rxUASVideo; g_txUASVideo = txUASVideo; pthread_mutex_unlock(&uasVideoMutex); #endif // USE_TLS return 0; } int rtpstream_rtpecho_stopvideo(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_rtpecho_stopvideo callinfo=%p\n", callinfo); taskentry_t *taskinfo = callinfo->taskinfo; ResultCheck r; if (!taskinfo) { return -1; /* no task data structure */ } #ifdef USE_TLS taskinfo->video_srtp_echo_active = 0; pthread_mutex_lock(&quit_mutexvideo); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "MAIN: Setting quit_videoecho_thread flags to TRUE...\n"); } pthread_mutex_unlock(&debugremutexvideo); quit_videoecho_thread = true; pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "MAIN: Sending QUIT signal...\n"); } pthread_mutex_unlock(&debugremutexvideo); pthread_cond_signal(&quit_cvvideo); pthread_mutex_unlock(&quit_mutexvideo); pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "rtpstream_rtpecho_stopvideo reached...\n"); } printLocalVideoSrtpStuff(taskinfo->local_srtp_video_params); printRemoteVideoSrtpStuff(taskinfo->remote_srtp_video_params); pthread_mutex_unlock(&debugremutexvideo); if (pthread_join(pthread_videoecho_id, &r.p) == 0) { // successfully joined video thread pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "successfully joined video thread: %d\n", r.i); } pthread_mutex_unlock(&debugremutexvideo); } else { // error joining video thread pthread_mutex_lock(&debugremutexvideo); if (debugrefilevideo != NULL) { fprintf(debugrefilevideo, "error joining video thread: %d\n", r.i); } pthread_mutex_unlock(&debugremutexvideo); } pthread_mutex_lock(&debugremutexvideo); if (srtpcheck_debug) { if (debugrefilevideo) { fclose(debugrefilevideo); } } pthread_mutex_unlock(&debugremutexvideo); #else // !USE_TLS r.i = 0; // dummy #endif // USE_TLS return r.i; } /* code checked */ int rtpstream_shutdown(std::unordered_map& threadIDs) { int count = 0; void* rtpresult; int total_rtpresults; rtpresult = NULL; total_rtpresults = 0; debugprint("rtpstream_shutdown\n"); /* signal all playback threads that they should exit */ if (ready_threads) { for (count = 0; count < num_ready_threads; count++) { ready_threads[count]->exit_flag = 1; } free(ready_threads); ready_threads = NULL; } if (busy_threads) { for (count = 0; count < num_busy_threads; count++) { busy_threads[count]->exit_flag = 1; } free(busy_threads); busy_threads = NULL; } /* first make sure no playback threads are accessing the file buffers */ /* else small chance the playback thread tries to access freed memory */ while (rtpstream_numthreads) { usleep(50000); } // PTHREAD JOIN HERE... for (std::unordered_map::iterator iter = threadIDs.begin(); iter != threadIDs.end(); ++iter) { printAudioHex("EXISTING THREADID: ", "", 0, getThreadId(iter->first), 0); printVideoHex("EXISTING THREADID: ", "", 0, getThreadId(iter->first), 0); if (pthread_join(iter->first, &rtpresult)) { // error joining thread printAudioHex("ERROR RETURNED BY PTHREAD_JOIN!", "", 0, 0, 0); printVideoHex("ERROR RETURNED BY PTHREAD_JOIN!", "", 0, 0, 0); return -2; } total_rtpresults |= (int)(long long)rtpresult; printAudioHex("JOINED THREAD: ", "", 0, (long long)rtpresult, total_rtpresults); printVideoHex("JOINED THREAD: ", "", 0, (long long)rtpresult, total_rtpresults); } /* now free cached file bytes and structure */ if (cached_files) { for (count = 0; count < num_cached_files; count++) { free(cached_files[count].bytes); } free(cached_files); cached_files = NULL; } /* now free cached patterns bytes and structure */ if (cached_patterns) { for (count = 0; count < num_cached_files; count++) { free(cached_patterns[count].bytes); } free(cached_patterns); cached_patterns = NULL; } if (debugvfile && rtpcheck_debug) { fclose(debugvfile); } if (debugafile && rtpcheck_debug) { fclose(debugafile); } pthread_mutex_destroy(&debugamutex); return total_rtpresults; } sipp-3.7.2/src/scenario.cpp0000664000000000000000000040625414525516253012507 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Venkatesh * Lee Ballard * Guillaume TEISSIER from FTR&D * Wolfgang Beck * Marc Van Diest from Belgacom * Charles P. Wright from IBM Research * Michael Stovenour */ #include #include "config.h" #include "sipp.hpp" #ifdef HAVE_GSL #include #include #include #endif /************************ Class Constructor *************************/ message::message(int index, const char *desc) { this->index = index; this->desc = desc; pause_distribution = NULL; // delete on exit pause_variable = -1; pause_desc = NULL; // free on exit sessions = 0; bShouldRecordRoutes = 0; bShouldAuthenticate = 0; send_scheme = NULL; // delete on exit retrans_delay = 0; timeout = 0; recv_response = 0; recv_request = NULL; // free on exit optional = 0; advance_state = true; regexp_match = 0; regexp_compile = NULL; // regfree (if not NULL) and free on exit /* Anyway */ start_rtd = 0; stop_rtd = 0; repeat_rtd = 0; lost = -1; crlf = 0; ignoresdp = false; hide = 0; display_str = NULL; // free on exit test = -1; condexec = -1; condexec_inverse = false; chance = 0;/* meaning always */ next = -1; nextLabel = NULL; // free on exit on_timeout = -1; onTimeoutLabel = NULL; // free on exit timewait = false; /* 3pcc extended mode */ peer_dest = NULL; // free on exit peer_src = NULL; // free on exit /* Statistics */ nb_sent = 0; nb_recv = 0; nb_sent_retrans = 0; nb_recv_retrans = 0; nb_timeout = 0; nb_unexp = 0; nb_lost = 0; counter = 0; M_actions = NULL; // delete on exit M_type = 0; M_sendCmdData = NULL; // delete on exit M_nbCmdSent = 0; M_nbCmdRecv = 0; content_length_flag = ContentLengthNoPresent; /* How to match responses to this message. */ start_txn = 0; response_txn = 0; ack_txn = 0; recv_response_for_cseq_method_list = NULL; // free on exit } message::~message() { delete(pause_distribution); free(pause_desc); delete(send_scheme); free(recv_request); if (regexp_compile != NULL) { regfree(regexp_compile); } free(regexp_compile); free(display_str); free(nextLabel); free(onTimeoutLabel); free(peer_dest); free(peer_src); delete(M_actions); delete(M_sendCmdData); free(recv_response_for_cseq_method_list); } /******** Global variables which compose the scenario file **********/ scenario *main_scenario; scenario *ooc_scenario; scenario *aa_scenario; scenario *display_scenario; /* This mode setting refers to whether we open calls autonomously (MODE_CLIENT) * or in response to requests (MODE_SERVER). */ int creationMode = MODE_CLIENT; /* Send mode. Do we send to a fixed address or to the last one we got. */ int sendMode = MODE_CLIENT; /* This describes what our 3PCC behavior is. */ int thirdPartyMode = MODE_3PCC_NONE; /*************** Helper functions for various types *****************/ long get_long(const char *ptr, const char *what) { char *endptr; long ret; ret = strtol(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid integer!", what, ptr); } return ret; } unsigned long long get_long_long(const char *ptr, const char *what) { char *endptr; unsigned long long ret; ret = strtoull(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid integer!", what, ptr); } return ret; } /* This function returns a time in milliseconds from a string. * The multiplier is used to convert from the default input type into * milliseconds. For example, for seconds you should use 1000 and for * milliseconds use 1. */ long get_time(const char *ptr, const char *what, int multiplier) { char *endptr; const char *p; long ret = 0; double dret; int i; if (!isdigit(*ptr)) { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } for (i = 0, p = ptr; *p; p++) { if (*p == ':') { i++; } } if (i == 1) { /* mm:ss */ ERROR("%s, \"%s\" mm:ss not implemented yet!", what, ptr); } else if (i == 2) { /* hh:mm:ss */ ERROR("%s, \"%s\" hh:mm:ss not implemented yet!", what, ptr); } else if (i != 0) { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } dret = strtod(ptr, &endptr); if (*endptr) { if (!strcmp(endptr, "s")) { /* Seconds */ ret = (long)(dret * 1000); } else if (!strcmp(endptr, "ms")) { /* Milliseconds. */ ret = (long)dret; } else if (!strcmp(endptr, "m")) { /* Minutes. */ ret = (long)(dret * 60000); } else if (!strcmp(endptr, "h")) { /* Hours. */ ret = (long)(dret * 60 * 60 * 1000); } else { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } } else { ret = (long)(dret * multiplier); } return ret; } double get_double(const char *ptr, const char *what) { char *endptr; double ret; ret = strtod(ptr, &endptr); if (*endptr) { ERROR("%s, \"%s\" is not a floating point number!", what, ptr); } return ret; } /* If the value is enclosed in [brackets], it is assumed to be * a command-line supplied keyword value (-key). */ static char* xp_get_keyword_value(const char *name) { const char* ptr = xp_get_value(name); return ptr ? strdup(ptr) : NULL; } static char* xp_get_string(const char *name, const char *what) { const char *ptr; char *unescaped; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } unescaped = (char *)malloc(strlen(ptr) + 1); if (!unescaped) { ERROR("Out of memory!"); } xp_unescape(ptr, unescaped); return unescaped; } static double xp_get_double(const char *name, const char *what) { const char *ptr; char *helptext; double val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_double(ptr, helptext); free(helptext); return val; } static long xp_get_long(const char *name, const char *what) { const char *ptr; char *helptext; long val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_long(ptr, helptext); free(helptext); return val; } static long xp_get_long(const char *name, const char *what, long defval) { if (!(xp_get_value(name))) { return defval; } return xp_get_long(name, what); } static bool xp_get_bool(const char *name, const char *what) { const char *ptr; char *helptext; bool val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_bool(ptr, helptext); free(helptext); return val; } static bool xp_get_bool(const char *name, const char *what, bool defval) { if (!(xp_get_value(name))) { return defval; } return xp_get_bool(name, what); } int scenario::get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck) { /* Check the name's validity. */ if (txnName[0] == '\0') { ERROR("Variable names may not be empty for %s", what); } if (strcspn(txnName, "$,") != strlen(txnName)) { ERROR("Variable names may not contain $ or , for %s", what); } /* If this transaction has already been used, then we have nothing to do. */ str_int_map::iterator txn_it = txnMap.find(txnName); if (txn_it != txnMap.end()) { if (start) { /* We need to fill in the invite field. */ transactions[txn_it->second - 1].started++; } else if (isAck) { transactions[txn_it->second - 1].acks++; } else { transactions[txn_it->second - 1].responses++; } return txn_it->second; } /* Assign this variable the next slot. */ struct txnControlInfo transaction; transaction.name = strdup(txnName); if (start) { transaction.started = 1; transaction.responses = 0; transaction.acks = 0; transaction.isInvite = isInvite; } else if (isAck) { /* Does not start or respond to this txn. */ transaction.started = 0; transaction.responses = 0; transaction.acks = 1; transaction.isInvite = false; } else { transaction.started = 0; transaction.responses = 1; transaction.acks = 0; transaction.isInvite = false; } transactions.push_back(transaction); int txnNum = transactions.size(); txnMap[txnName] = txnNum; return txnNum; } int scenario::find_var(const char *varName) { return allocVars->find(varName, false); } void scenario::addRtpTaskThreadID(pthread_t id) { threadIDs[id] = "threadID"; } void scenario::removeRtpTaskThreadID(pthread_t id) { threadIDs.erase(id); } std::unordered_map& scenario::fetchRtpTaskThreadIDs() { return threadIDs; } int scenario::get_var(const char *varName, const char *what) { /* Check the name's validity. */ if (varName[0] == '\0') { ERROR("Transaction names may not be empty for %s", what); } if (strcspn(varName, "$,") != strlen(varName)) { ERROR("Transaction names may not contain '$' or ',' for %s", what); } return allocVars->find(varName, true); } int scenario::xp_get_var(const char *name, const char *what) { const char *ptr; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' variable parameter.", what, name); } return get_var(ptr, what); } static int xp_get_optional(const char *name, const char *what) { const char *ptr = xp_get_value(name); if (!ptr) { return OPTIONAL_FALSE; } if(!strcmp(ptr, "true")) { return OPTIONAL_TRUE; } else if(!strcmp(ptr, "global")) { return OPTIONAL_GLOBAL; } else if(!strcmp(ptr, "false")) { return OPTIONAL_FALSE; } else { ERROR("Could not understand optional value for %s: %s", what, ptr); } return OPTIONAL_FALSE; } int scenario::xp_get_var(const char *name, const char *what, int defval) { const char *ptr; if (!(ptr = xp_get_value(name))) { return defval; } return xp_get_var(name, what); } bool get_bool(const char *ptr, const char *what) { char *endptr; long ret; if (!strcasecmp(ptr, "true")) { return true; } if (!strcasecmp(ptr, "false")) { return false; } ret = strtol(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid boolean!", what, ptr); } return ret ? true : false; } /* Pretty print a time. */ int time_string(double ms, char *res, int reslen) { if (ms < 10000) { /* Less then 10 seconds we represent accurately. */ if ((int)(ms + 0.9999) == (int)(ms)) { /* We have an integer, or close enough to it. */ return snprintf(res, reslen, "%dms", (int)ms); } else { if (ms < 1000) { return snprintf(res, reslen, "%.2lfms", ms); } else { return snprintf(res, reslen, "%.1lfms", ms); } } } else if (ms < 60000) { /* We round to 100ms for times less than a minute. */ return snprintf(res, reslen, "%.1fs", ms/1000); } else if (ms < 60 * 60000) { /* We round to 1s for times more than a minute. */ int s = (unsigned int)(ms / 1000); int m = s / 60; s %= 60; return snprintf(res, reslen, "%d:%02d", m, s); } else { int s = (unsigned int)(ms / 1000); int m = s / 60; int h = m / 60; s %= 60; m %= 60; return snprintf(res, reslen, "%d:%02d:%02d", h, m, s); } } /* For backwards compatibility, we assign "true" to slot 1, false to 0, and * allow other valid integers. */ int scenario::get_rtd(const char *ptr, bool start) { if(!strcmp(ptr, (char *)"false")) return 0; if(!strcmp(ptr, (char *)"true")) return stats->findRtd("1", start); return stats->findRtd(ptr, start); } /* Get a counter */ int scenario::get_counter(const char *ptr, const char *what) { /* Check the name's validity. */ if (ptr[0] == '\0') { ERROR("Counter names names may not be empty for %s", what); } if (strcspn(ptr, "$,") != strlen(ptr)) { ERROR("Counter names may not contain $ or , for %s", what); } return stats->findCounter(ptr, true); } /* Some validation functions. */ void scenario::validate_variable_usage() { allocVars->validate(); } void scenario::validate_txn_usage() { for (unsigned int i = 0; i < transactions.size(); i++) { if(transactions[i].started == 0) { ERROR("Transaction %s is never started!", transactions[i].name); } else if(transactions[i].responses == 0) { ERROR("Transaction %s has no responses defined!", transactions[i].name); } if (transactions[i].isInvite && transactions[i].acks == 0) { ERROR("Transaction %s is an INVITE transaction without an ACK!", transactions[i].name); } if (!transactions[i].isInvite && (transactions[i].acks > 0)) { ERROR("Transaction %s is a non-INVITE transaction with an ACK!", transactions[i].name); } } } /* Apply the next and ontimeout labels according to our map. */ void scenario::apply_labels(msgvec v, str_int_map labels) { for (unsigned int i = 0; i < v.size(); i++) { if (v[i]->nextLabel) { str_int_map::iterator label_it = labels.find(v[i]->nextLabel); if (label_it == labels.end()) { ERROR("The label '%s' was not defined (index %d, next attribute)", v[i]->nextLabel, i); } v[i]->next = label_it->second; } if (v[i]->onTimeoutLabel) { str_int_map::iterator label_it = labels.find(v[i]->onTimeoutLabel); if (label_it == labels.end()) { ERROR("The label '%s' was not defined (index %d, ontimeout attribute)", v[i]->onTimeoutLabel, i); } v[i]->on_timeout = label_it->second; } } } int get_cr_number(const char *src) { int res=0; while(*src) { if(*src == '\n') res++; src++; } return res; } static char* clean_cdata(char *ptr, int *removed_crlf = NULL) { char * msg; while((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n')) ptr++; msg = (char *) malloc(strlen(ptr) + 3); if(!msg) { ERROR("Memory Overflow"); } strcpy(msg, ptr); ptr = msg + strlen(msg); ptr--; while((ptr >= msg) && ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n'))) { if(*ptr == '\n' && removed_crlf) { (*removed_crlf)++; } *ptr-- = 0; } if(ptr == msg) { ERROR("Empty cdata in xml scenario file"); } while ((ptr = strstr(msg, "\n "))) { memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); } while ((ptr = strstr(msg, " \n"))) { memmove(ptr, ptr + 1, strlen(ptr)); } while ((ptr = strstr(msg, "\n\t"))) { memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); } while ((ptr = strstr(msg, "\t\n"))) { memmove(ptr, ptr + 1, strlen(ptr)); } if (!strstr(msg, "\n\n")) { strcat(msg, "\n\n"); } return msg; } /********************** Scenario File analyser **********************/ void scenario::checkOptionalRecv(char *elem, unsigned int scenario_file_cursor) { if (last_recv_optional) { ERROR(" before <%s> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", elem, scenario_file_cursor); } last_recv_optional = false; } scenario::scenario(char * filename, int deflt) { char * elem; char *method_list = NULL; unsigned int scenario_file_cursor = 0; int L_content_length = 0 ; char * peer; const char* cptr; last_recv_optional = false; if(filename) { if(!xp_set_xml_buffer_from_file(filename)) { ERROR("Unable to load or parse '%s' xml scenario file", filename); } } else { if(!xp_set_xml_buffer_from_string(default_scenario[deflt])) { ERROR("Unable to load default xml scenario file"); } } stats = new CStat(); allocVars = new AllocVariableTable(userVariables); hidedefault = false; elem = xp_open_element(0); if (!elem) { ERROR("No element in xml scenario file"); } if(strcmp("scenario", elem)) { ERROR("No 'scenario' section in xml scenario file"); } if ((cptr = xp_get_value("name"))) { name = strdup(cptr); } else { name = strdup(""); } duration = 0; found_timewait = false; scenario_file_cursor = 0; while ((elem = xp_open_element(scenario_file_cursor))) { char * ptr; scenario_file_cursor ++; if(!strcmp(elem, "CallLengthRepartition")) { ptr = xp_get_string("value", "CallLengthRepartition"); stats->setRepartitionCallLength(ptr); free(ptr); } else if(!strcmp(elem, "ResponseTimeRepartition")) { ptr = xp_get_string("value", "ResponseTimeRepartition"); stats->setRepartitionResponseTime(ptr); free(ptr); } else if(!strcmp(elem, "Global")) { ptr = xp_get_string("variables", "Global"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { globalVariables->find(currentTabVarName[i], true); } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "User")) { ptr = xp_get_string("variables", "User"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { userVariables->find(currentTabVarName[i], true); } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "Reference")) { ptr = xp_get_string("variables", "Reference"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { int id = allocVars->find(currentTabVarName[i], false); if (id == -1) { ERROR("Could not reference non-existant variable '%s'", currentTabVarName[i]); } } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "DefaultMessage")) { char *id = xp_get_string("id", "DefaultMessage"); if(!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'send' section of xml scenario file"); } char *msg = clean_cdata(ptr); set_default_message(id, msg); free(id); /* XXX: This should really be per scenario. */ } else if(!strcmp(elem, "label")) { ptr = xp_get_string("id", "label"); if (labelMap.find(ptr) != labelMap.end()) { ERROR("The label name '%s' is used twice.", ptr); } labelMap[ptr] = messages.size(); free(ptr); } else if (!strcmp(elem, "init")) { /* We have an init section, which must be full of nops or labels. */ int nop_cursor = 0; char *initelem; while ((initelem = xp_open_element(nop_cursor++))) { if (!strcmp(initelem, "nop")) { /* We should parse this. */ message *nopmsg = new message(initmessages.size(), "scenario initialization"); initmessages.push_back(nopmsg); nopmsg->M_type = MSG_TYPE_NOP; getCommonAttributes(nopmsg); } else if (!strcmp(initelem, "label")) { /* Add an init label. */ cptr = xp_get_value("id"); if (initLabelMap.find(cptr) != initLabelMap.end()) { ERROR("The label name '%s' is used twice.", cptr); } initLabelMap[cptr] = initmessages.size(); } else { ERROR("Invalid element in an init stanza: '%s'", initelem); } xp_close_element(); } } else { /** Message Case */ if (found_timewait) { ERROR(" can only be the last message in a scenario!"); } message *curmsg = new message(messages.size(), name ? name : "unknown scenario"); messages.push_back(curmsg); if(!strcmp(elem, "send")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_SEND; /* Sent messages descriptions */ if(!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'send' section of xml scenario file"); } int removed_clrf = 0; char * msg = clean_cdata(ptr, &removed_clrf); L_content_length = xp_get_content_length(msg); switch (L_content_length) { case -1 : // the msg does not contain content-length field break ; case 0 : curmsg -> content_length_flag = message::ContentLengthValueZero; // Initialize to No present break ; default : curmsg -> content_length_flag = message::ContentLengthValueNoZero; // Initialize to No present break ; } if((msg[strlen(msg) - 1] != '\n') && (removed_clrf)) { strcat(msg, "\n"); } char *tsrc = msg; while(*tsrc++); curmsg -> send_scheme = new SendingMessage(this, msg); free(msg); // If this is a request we are sending, then store our transaction/method matching information. if (!curmsg->send_scheme->isResponse()) { char *method = curmsg->send_scheme->getMethod(); bool isInvite = !strcmp(method, "INVITE"); bool isAck = !strcmp(method, "ACK"); if ((cptr = xp_get_value("start_txn"))) { if (isAck) { ERROR("An ACK message can not start a transaction!"); } curmsg->start_txn = get_txn(cptr, "start transaction", true, isInvite, false); } else if ((cptr = xp_get_value("ack_txn"))) { if (!isAck) { ERROR("The ack_txn attribute is valid only for ACK messages!"); } curmsg->ack_txn = get_txn(cptr, "ack transaction", false, false, true); } else { int len = method_list ? strlen(method_list) : 0; method_list = (char *)realloc(method_list, len + strlen(method) + 1); if (!method_list) { ERROR_NO("Out of memory allocating method_list!"); } strcpy(method_list + len, method); } } else { if (xp_get_value("start_txn")) { ERROR("Responses can not start a transaction"); } if (xp_get_value("ack_txn")) { ERROR("Responses can not ACK a transaction"); } } if (xp_get_value("response_txn")) { ERROR("response_txn can only be used for received messages."); } curmsg -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0); curmsg -> timeout = xp_get_long("timeout", "message send timeout", 0); } else if (!strcmp(elem, "recv")) { curmsg->M_type = MSG_TYPE_RECV; /* Received messages descriptions */ if((cptr = xp_get_value("response"))) { curmsg ->recv_response = get_long(cptr, "response code"); if (method_list) { curmsg->recv_response_for_cseq_method_list = strdup(method_list); } if ((cptr = xp_get_value("response_txn"))) { curmsg->response_txn = get_txn(cptr, "transaction response", false, false, false); } } if ((cptr = xp_get_value("request"))) { curmsg->recv_request = strdup(cptr); if (xp_get_value("response_txn")) { ERROR("response_txn can only be used for received responses."); } } curmsg->optional = xp_get_optional("optional", "recv"); last_recv_optional = curmsg->optional; curmsg->advance_state = xp_get_bool("advance_state", "recv", true); if (!curmsg->advance_state && curmsg->optional == OPTIONAL_FALSE) { ERROR("advance_state is allowed only for optional messages (index = %zu)", messages.size() - 1); } if ((cptr = xp_get_value("regexp_match"))) { if (!strcmp(cptr, "true")) { curmsg->regexp_match = 1; } } curmsg->timeout = xp_get_long("timeout", "message timeout", 0); /* record the route set */ /* TODO disallow optional and rrs to coexist? */ if ((cptr = xp_get_value("rrs"))) { curmsg->bShouldRecordRoutes = get_bool(cptr, "record route set"); } /* record the authentication credentials */ if ((cptr = xp_get_value("auth"))) { bool temp = get_bool(cptr, "message authentication"); curmsg->bShouldAuthenticate = temp; } } else if(!strcmp(elem, "pause") || !strcmp(elem, "timewait")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_PAUSE; if (!strcmp(elem, "timewait")) { curmsg->timewait = true; found_timewait = true; } int var; if ((var = xp_get_var("variable", "pause", -1)) != -1) { curmsg->pause_variable = var; } else { CSample *distribution = parse_distribution(true); bool sanity_check = xp_get_bool("sanity_check", "pause", true); double pause_duration = distribution->cdfInv(0.99); if (sanity_check && (pause_duration > INT_MAX)) { char percentile[100]; char desc[100]; distribution->timeDescr(desc, sizeof(desc)); time_string(pause_duration, percentile, sizeof(percentile)); ERROR("The distribution %s has a 99th percentile of %s, which is larger than INT_MAX. You should chose different parameters.", desc, percentile); } curmsg->pause_distribution = distribution; /* Update scenario duration with max duration */ duration += (int)pause_duration; } } else if(!strcmp(elem, "nop")) { checkOptionalRecv(elem, scenario_file_cursor); /* Does nothing at SIP level. This message type can be used to handle * actions, increment counters, or for RTDs. */ curmsg->M_type = MSG_TYPE_NOP; } else if(!strcmp(elem, "recvCmd")) { curmsg->M_type = MSG_TYPE_RECVCMD; curmsg->optional = xp_get_optional("optional", "recv"); last_recv_optional = curmsg->optional; /* 3pcc extended mode */ if ((cptr = xp_get_value("src"))) { curmsg->peer_src = strdup(cptr); } else if (extendedTwinSippMode) { ERROR("You must specify a 'src' for recvCmd when using extended 3pcc mode!"); } } else if(!strcmp(elem, "sendCmd")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_SENDCMD; /* Sent messages descriptions */ /* 3pcc extended mode */ if ((cptr = xp_get_value("dest"))) { peer = strdup(cptr); curmsg->peer_dest = peer; peer_map::iterator peer_it; peer_it = peers.find(peer_map::key_type(peer)); if(peer_it == peers.end()) /* the peer (slave or master) has not been added in the map (first occurrence in the scenario) */ { T_peer_infos infos = {}; infos.peer_socket = 0; strncpy(infos.peer_host, get_peer_addr(peer), sizeof(infos.peer_host) - 1); peers[std::string(peer)] = infos; } } else if (extendedTwinSippMode) { ERROR("You must specify a 'dest' for sendCmd with extended 3pcc mode!"); } if (!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'sendCmd' section of xml scenario file"); } char *msg = clean_cdata(ptr); curmsg -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */); free(msg); } else { ERROR("Unknown element '%s' in xml scenario file", elem); } getCommonAttributes(curmsg); } /** end * Message case */ xp_close_element(); } // end while free(method_list); /* Close scenario element */ xp_close_element(); if (xp_is_invalid()) { ERROR("Invalid XML in scenario"); } str_int_map::iterator label_it = labelMap.find("_unexp.main"); if (label_it != labelMap.end()) { unexpected_jump = label_it->second; } else { unexpected_jump = -1; } retaddr = find_var("_unexp.retaddr"); pausedaddr = find_var("_unexp.pausedaddr"); /* Patch up the labels. */ apply_labels(messages, labelMap); apply_labels(initmessages, initLabelMap); /* Some post-scenario loading validation. */ stats->validateRtds(); /* Make sure that all variables are used more than once. */ validate_variable_usage(); /* Make sure that all started transactions have responses, and vice versa. */ validate_txn_usage(); if (messages.size() == 0) { ERROR("Did not find any messages inside of scenario!"); } } void scenario::runInit() { call *initcall; if (initmessages.size() > 0) { initcall = new call(main_scenario, NULL, NULL, "///main-init", 0, false, false, true); initcall->run(); } } void clear_int_str(int_str_map m) { for(int_str_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { free(it->second); m.erase(it); } } void clear_str_int(str_int_map m) { for(str_int_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { m.erase(it); } } void clear_int_int(int_int_map m) { for(int_int_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { m.erase(it); } } scenario::~scenario() { for (msgvec::iterator i = messages.begin(); i != messages.end(); i++) { delete *i; } messages.clear(); free(name); allocVars->putTable(); delete stats; for (unsigned int i = 0; i < transactions.size(); i++) { free(transactions[i].name); } transactions.clear(); clear_str_int(labelMap); clear_str_int(initLabelMap); clear_str_int(txnMap); } CSample *parse_distribution(bool oldstyle = false) { CSample *distribution = NULL; const char *distname; const char *ptr = 0; if(!(distname = xp_get_value("distribution"))) { if (!oldstyle) { ERROR("statistically distributed actions or pauses requires 'distribution' parameter"); } if ((ptr = xp_get_value("normal"))) { distname = "normal"; } else if ((ptr = xp_get_value("exponential"))) { distname = "exponential"; } else if ((ptr = xp_get_value("lognormal"))) { distname = "lognormal"; } else if ((ptr = xp_get_value("weibull"))) { distname = "weibull"; } else if ((ptr = xp_get_value("pareto"))) { distname = "pareto"; } else if ((ptr = xp_get_value("gamma"))) { distname = "gamma"; } else if ((ptr = xp_get_value("min"))) { distname = "uniform"; } else if ((ptr = xp_get_value("max"))) { distname = "uniform"; } else if ((ptr = xp_get_value("milliseconds"))) { double val = get_double(ptr, "Pause milliseconds"); return new CFixed(val); } else { return new CDefaultPause(); } } if (!strcmp(distname, "fixed")) { double value = xp_get_double("value", "Fixed distribution"); distribution = new CFixed(value); } else if (!strcmp(distname, "uniform")) { double min = xp_get_double("min", "Uniform distribution"); double max = xp_get_double("max", "Uniform distribution"); distribution = new CUniform(min, max); #ifdef HAVE_GSL } else if (!strcmp(distname, "normal")) { double mean = xp_get_double("mean", "Normal distribution"); double stdev = xp_get_double("stdev", "Normal distribution"); distribution = new CNormal(mean, stdev); } else if (!strcmp(distname, "lognormal")) { double mean = xp_get_double("mean", "Lognormal distribution"); double stdev = xp_get_double("stdev", "Lognormal distribution"); distribution = new CLogNormal(mean, stdev); } else if (!strcmp(distname, "exponential")) { double mean = xp_get_double("mean", "Exponential distribution"); distribution = new CExponential(mean); } else if (!strcmp(distname, "weibull")) { double lambda = xp_get_double("lambda", "Weibull distribution"); double k = xp_get_double("k", "Weibull distribution"); distribution = new CWeibull(lambda, k); } else if (!strcmp(distname, "pareto")) { double k = xp_get_double("k", "Pareto distribution"); double xsubm = xp_get_double("x_m", "Pareto distribution"); distribution = new CPareto(k, xsubm); } else if (!strcmp(distname, "gpareto")) { double shape = xp_get_double("shape", "Generalized Pareto distribution"); double scale = xp_get_double("scale", "Generalized Pareto distribution"); double location = xp_get_double("location", "Generalized Pareto distribution"); distribution = new CGPareto(shape, scale, location); } else if (!strcmp(distname, "gamma")) { double k = xp_get_double("k", "Gamma distribution"); double theta = xp_get_double("theta", "Gamma distribution"); distribution = new CGamma(k, theta); } else if (!strcmp(distname, "negbin")) { double n = xp_get_double("n", "Negative Binomial distribution"); double p = xp_get_double("p", "Negative Binomial distribution"); distribution = new CNegBin(n, p); #else } else if (!strcmp(distname, "normal") || !strcmp(distname, "lognormal") || !strcmp(distname, "exponential") || !strcmp(distname, "pareto") || !strcmp(distname, "gamma") || !strcmp(distname, "negbin") || !strcmp(distname, "weibull")) { ERROR("The distribution '%s' is only available with GSL", distname); #endif } else { ERROR("Unknown distribution: %s", ptr); } return distribution; } /* 3pcc extended mode: * get the correspondances between * slave and master names and their * addresses */ void parse_slave_cfg() { FILE * f; char line[MAX_PEER_SIZE]; char * temp_peer; char * temp_host; char * peer_host; f = fopen(slave_cfg_file, "r"); if(f) { while (fgets(line, MAX_PEER_SIZE, f) != NULL) { temp_peer = strtok(line, ";"); if (!temp_peer) continue; temp_host = strtok(NULL, ";"); if (!temp_host) continue; peer_host = strdup(temp_host); if (!peer_host) ERROR("Cannot allocate memory!"); peer_addrs[std::string(temp_peer)] = peer_host; } } else { ERROR("Can not open slave_cfg file %s", slave_cfg_file); } fclose(f); } // Determine in which mode the sipp tool has been // launched (client, server, 3pcc client, 3pcc server, 3pcc extended master or slave) void scenario::computeSippMode() { bool isRecvCmdFound = false; bool isSendCmdFound = false; creationMode = -1; sendMode = -1; thirdPartyMode = MODE_3PCC_NONE; assert(messages.size() > 0); for(unsigned int i=0; iM_type) { case MSG_TYPE_PAUSE: case MSG_TYPE_NOP: /* Allow pauses or nops to go first. */ continue; case MSG_TYPE_SEND: if (sendMode == -1) { sendMode = MODE_CLIENT; } if (creationMode == -1) { creationMode = MODE_CLIENT; } break; case MSG_TYPE_RECV: if (sendMode == -1) { sendMode = MODE_SERVER; } if (creationMode == -1) { creationMode = MODE_SERVER; } break; case MSG_TYPE_SENDCMD: isSendCmdFound = true; if (creationMode == -1) { creationMode = MODE_CLIENT; } if(!isRecvCmdFound) { if (creationMode == MODE_SERVER) { /* * If it is a server already, then start it in * 3PCC A passive mode */ if(twinSippMode) { thirdPartyMode = MODE_3PCC_A_PASSIVE; } else if (extendedTwinSippMode) { thirdPartyMode = MODE_MASTER_PASSIVE; } } else { if(twinSippMode) { thirdPartyMode = MODE_3PCC_CONTROLLER_A; } else if (extendedTwinSippMode) { thirdPartyMode = MODE_MASTER; } } if((thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_MASTER) && !master_name) { ERROR("Inconsistency between command line and scenario: master scenario but -master option not set"); } if(!twinSippMode && !extendedTwinSippMode) ERROR("sendCmd message found in scenario but no twin sipp" " address has been passed! Use -3pcc option or 3pcc extended mode"); } break; case MSG_TYPE_RECVCMD: if (creationMode == -1) { creationMode = MODE_SERVER; } isRecvCmdFound = true; if(!isSendCmdFound) { if(twinSippMode) { thirdPartyMode = MODE_3PCC_CONTROLLER_B; } else if(extendedTwinSippMode) { thirdPartyMode = MODE_SLAVE; if(!slave_number) { ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set"); } else { thirdPartyMode = MODE_SLAVE; } } if(!twinSippMode && !extendedTwinSippMode) ERROR("recvCmd message found in scenario but no " "twin sipp address has been passed! Use " "-3pcc option\n"); } break; default: break; } } if(creationMode == -1) ERROR("Unable to determine creation mode of the tool (server, client)"); if(sendMode == -1) ERROR("Unable to determine send mode of the tool (server, client)"); } void scenario::handle_rhs(CAction *tmpAction, const char *what) { if (xp_get_value("value")) { tmpAction->setDoubleValue(xp_get_double("value", what)); if (xp_get_value("variable")) { ERROR("Value and variable are mutually exclusive for %s action!", what); } } else if (xp_get_value("variable")) { tmpAction->setVarInId(xp_get_var("variable", what)); if (xp_get_value("value")) { ERROR("Value and variable are mutually exclusive for %s action!", what); } } else { ERROR("No value or variable defined for %s action!", what); } } void scenario::handle_arithmetic(CAction *tmpAction, const char *what) { tmpAction->setVarId(xp_get_var("assign_to", what)); handle_rhs(tmpAction, what); } void scenario::parseAction(CActions *actions) { char * actionElem; unsigned int recvScenarioLen = 0; char ** currentTabVarName = NULL; int currentNbVarNames; int sub_currentNbVarId; char* ptr; const char* cptr; while((actionElem = xp_open_element(recvScenarioLen))) { CAction *tmpAction = new CAction(this); if(!strcmp(actionElem, "ereg")) { ptr = xp_get_string("regexp", "ereg"); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_REGEXP); // warning - although these are detected for both msg and hdr // they are only implemented for search_in="hdr" tmpAction->setCaseIndep(xp_get_bool("case_indep", "ereg", false)); tmpAction->setHeadersOnly(xp_get_bool("start_line", "ereg", false)); if ((cptr = xp_get_value("search_in"))) { tmpAction->setOccurrence(1); if (strcmp(cptr, "msg") == 0) { tmpAction->setLookingPlace(CAction::E_LP_MSG); tmpAction->setLookingChar (NULL); } else if (strcmp(cptr, "body") == 0) { tmpAction->setLookingPlace(CAction::E_LP_BODY); tmpAction->setLookingChar (NULL); } else if (strcmp(cptr, "var") == 0) { tmpAction->setVarInId(xp_get_var("variable", "ereg")); tmpAction->setLookingPlace(CAction::E_LP_VAR); } else if (strcmp(cptr, "hdr") == 0) { cptr = xp_get_value("header"); if (!cptr || !strlen(cptr)) { ERROR("search_in=\"hdr\" requires header field"); } tmpAction->setLookingPlace(CAction::E_LP_HDR); tmpAction->setLookingChar(cptr); if ((cptr = xp_get_value("occurrence"))) { tmpAction->setOccurrence(atol(cptr)); } else if ((cptr = xp_get_value("occurence"))) { /* old misspelling */ tmpAction->setOccurrence(atol(cptr)); } } else { ERROR("Unknown search_in value %s", cptr); } } else { tmpAction->setLookingPlace(CAction::E_LP_MSG); tmpAction->setLookingChar(NULL); } // end if-else search_in if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "ereg", false)); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for ereg!"); } } else { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "ereg", false)); } if (!(cptr = xp_get_value("assign_to"))) { ERROR("assign_to value is missing"); } createStringTable(cptr, ¤tTabVarName, ¤tNbVarNames); int varId = get_var(currentTabVarName[0], "assign_to"); tmpAction->setVarId(varId); tmpAction->setRegExp(ptr); if (currentNbVarNames > 1 ) { sub_currentNbVarId = currentNbVarNames - 1 ; tmpAction->setNbSubVarId(sub_currentNbVarId); for(int i=1; i<= sub_currentNbVarId; i++) { int varId = get_var(currentTabVarName[i], "sub expression assign_to"); tmpAction->setSubVarId(varId); } } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } /* end !strcmp(actionElem, "ereg") */ else if(!strcmp(actionElem, "log")) { ptr = xp_get_string("message", "log"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_TO_FILE); } else if(!strcmp(actionElem, "warning")) { ptr = xp_get_string("message", "warning"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_WARNING); } else if(!strcmp(actionElem, "error")) { ptr = xp_get_string("message", "error"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_ERROR); } else if(!strcmp(actionElem, "assign")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_VALUE); handle_arithmetic(tmpAction, "assign"); } else if(!strcmp(actionElem, "assignstr")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_STRING); tmpAction->setVarId(xp_get_var("assign_to", "assignstr")); ptr = xp_get_string("value", "assignstr"); tmpAction->setMessage(ptr); free(ptr); } else if(!strcmp(actionElem, "gettimeofday")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY); if (!(cptr = xp_get_value("assign_to"))) { ERROR("assign_to value is missing"); } createStringTable(cptr, ¤tTabVarName, ¤tNbVarNames); if (currentNbVarNames != 2 ) { ERROR("The gettimeofday action requires two output variables!"); } tmpAction->setNbSubVarId(1); int varId = get_var(currentTabVarName[0], "gettimeofday seconds assign_to"); tmpAction->setVarId(varId); varId = get_var(currentTabVarName[1], "gettimeofday useconds assign_to"); tmpAction->setSubVarId(varId); freeStringTable(currentTabVarName, currentNbVarNames); } else if(!strcmp(actionElem, "index")) { tmpAction->setVarId(xp_get_var("assign_to", "index")); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_INDEX); } else if(!strcmp(actionElem, "jump")) { tmpAction->setActionType(CAction::E_AT_JUMP); handle_rhs(tmpAction, "jump"); } else if(!strcmp(actionElem, "pauserestore")) { tmpAction->setActionType(CAction::E_AT_PAUSE_RESTORE); handle_rhs(tmpAction, "pauserestore"); } else if(!strcmp(actionElem, "add")) { tmpAction->setActionType(CAction::E_AT_VAR_ADD); handle_arithmetic(tmpAction, "add"); } else if(!strcmp(actionElem, "subtract")) { tmpAction->setActionType(CAction::E_AT_VAR_SUBTRACT); handle_arithmetic(tmpAction, "subtract"); } else if(!strcmp(actionElem, "multiply")) { tmpAction->setActionType(CAction::E_AT_VAR_MULTIPLY); handle_arithmetic(tmpAction, "multiply"); } else if(!strcmp(actionElem, "divide")) { tmpAction->setActionType(CAction::E_AT_VAR_DIVIDE); handle_arithmetic(tmpAction, "divide"); if (tmpAction->getVarInId() == 0) { if (tmpAction->getDoubleValue() == 0.0) { ERROR("divide actions can not have a value of zero!"); } } } else if(!strcmp(actionElem, "sample")) { tmpAction->setVarId(xp_get_var("assign_to", "sample")); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_SAMPLE); tmpAction->setDistribution(parse_distribution()); } else if(!strcmp(actionElem, "todouble")) { tmpAction->setActionType(CAction::E_AT_VAR_TO_DOUBLE); tmpAction->setVarId(xp_get_var("assign_to", "todouble")); tmpAction->setVarInId(xp_get_var("variable", "todouble")); } else if(!strcmp(actionElem, "test")) { if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "test")); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for test!"); } } else if (xp_get_value("check_it_inverse")) { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "test")); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (xp_get_value("assign_to") || (!xp_get_value("check_it") && !xp_get_value("check_it_inverse")) ) { tmpAction->setVarId(xp_get_var("assign_to", "test")); } tmpAction->setVarInId(xp_get_var("variable", "test")); if (xp_get_value("value")) { tmpAction->setDoubleValue(xp_get_double("value", "test")); if (xp_get_value("variable2")) { ERROR("Can not have both a value and a variable2 for test!"); } } else { tmpAction->setVarIn2Id(xp_get_var("variable2", "test")); } tmpAction->setActionType(CAction::E_AT_VAR_TEST); ptr = xp_get_string("compare", "test"); if (!strcmp(ptr, "equal")) { tmpAction->setComparator(CAction::E_C_EQ); } else if (!strcmp(ptr, "not_equal")) { tmpAction->setComparator(CAction::E_C_NE); } else if (!strcmp(ptr, "greater_than")) { tmpAction->setComparator(CAction::E_C_GT); } else if (!strcmp(ptr, "less_than")) { tmpAction->setComparator(CAction::E_C_LT); } else if (!strcmp(ptr, "greater_than_equal")) { tmpAction->setComparator(CAction::E_C_GEQ); } else if (!strcmp(ptr, "less_than_equal")) { tmpAction->setComparator(CAction::E_C_LEQ); } else { ERROR("Invalid 'compare' parameter: %s", ptr); } free(ptr); } else if(!strcmp(actionElem, "verifyauth")) { tmpAction->setVarId(xp_get_var("assign_to", "verifyauth")); char* username_ptr = xp_get_string("username", "verifyauth"); char* password_ptr = xp_get_string("password", "verifyauth"); tmpAction->setMessage(username_ptr, 0); tmpAction->setMessage(password_ptr, 1); tmpAction->setActionType(CAction::E_AT_VERIFY_AUTH); free(username_ptr); free(password_ptr); username_ptr = password_ptr = NULL; } else if(!strcmp(actionElem, "lookup")) { tmpAction->setVarId(xp_get_var("assign_to", "lookup")); tmpAction->setMessage(xp_get_string("file", "lookup"), 0); tmpAction->setMessage(xp_get_string("key", "lookup"), 1); tmpAction->setActionType(CAction::E_AT_LOOKUP); } else if(!strcmp(actionElem, "insert")) { tmpAction->setMessage(xp_get_string("file", "insert"), 0); tmpAction->setMessage(xp_get_string("value", "insert"), 1); tmpAction->setActionType(CAction::E_AT_INSERT); } else if(!strcmp(actionElem, "replace")) { tmpAction->setMessage(xp_get_string("file", "replace"), 0); tmpAction->setMessage(xp_get_string("line", "replace"), 1); tmpAction->setMessage(xp_get_string("value", "replace"), 2); tmpAction->setActionType(CAction::E_AT_REPLACE); } else if(!strcmp(actionElem, "setdest")) { tmpAction->setMessage(xp_get_string("host", actionElem), 0); tmpAction->setMessage(xp_get_string("port", actionElem), 1); tmpAction->setMessage(xp_get_string("protocol", actionElem), 2); tmpAction->setActionType(CAction::E_AT_SET_DEST); } else if(!strcmp(actionElem, "closecon")) { tmpAction->setActionType(CAction::E_AT_CLOSE_CON); } else if(!strcmp(actionElem, "strcmp")) { if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "strcmp")); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for strcmp!"); } } else if (xp_get_value("check_it_inverse")) { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "strcmp")); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (xp_get_value("assign_to") || (!xp_get_value("check_it") && !xp_get_value("check_it_inverse")) ) { tmpAction->setVarId(xp_get_var("assign_to", "strcmp")); } tmpAction->setVarInId(xp_get_var("variable", "strcmp")); if (xp_get_value("value")) { tmpAction->setStringValue(xp_get_string("value", "strcmp")); if (xp_get_value("variable2")) { ERROR("Can not have both a value and a variable2 for strcmp!"); } } else { tmpAction->setVarIn2Id(xp_get_var("variable2", "strcmp")); } tmpAction->setActionType(CAction::E_AT_VAR_STRCMP); } else if(!strcmp(actionElem, "trim")) { tmpAction->setVarId(xp_get_var("assign_to", "trim")); tmpAction->setActionType(CAction::E_AT_VAR_TRIM); } else if(!strcmp(actionElem, "urldecode")) { tmpAction->setVarId(xp_get_var("variable", "urldecode")); tmpAction->setActionType(CAction::E_AT_VAR_URLDECODE); } else if(!strcmp(actionElem, "urlencode")) { tmpAction->setVarId(xp_get_var("variable", "urlencode")); tmpAction->setActionType(CAction::E_AT_VAR_URLENCODE); } else if(!strcmp(actionElem, "exec")) { if ((cptr = xp_get_value("command"))) { tmpAction->setActionType(CAction::E_AT_EXECUTE_CMD); tmpAction->setMessage(cptr); } else if((cptr = xp_get_value("int_cmd"))) { CAction::T_IntCmdType type(CAction::E_INTCMD_STOPCALL); /* assume the default */ if (strcmp(cptr, "stop_now") == 0) { type = CAction::E_INTCMD_STOP_NOW; } else if (strcmp(cptr, "stop_gracefully") == 0) { type = CAction::E_INTCMD_STOP_ALL; } else if (strcmp(cptr, "stop_call") == 0) { type = CAction::E_INTCMD_STOPCALL; } /* the action is well formed, adding it in the */ /* tmpActionTable */ tmpAction->setActionType(CAction::E_AT_EXEC_INTCMD); tmpAction->setIntCmd(type); #ifdef PCAPPLAY } else if ((ptr = xp_get_keyword_value("play_pcap_audio"))) { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_AUDIO); hasMedia = 1; free(ptr); } else if ((ptr = xp_get_keyword_value("play_pcap_image"))) { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_IMAGE); hasMedia = 1; free(ptr); } else if ((ptr = xp_get_keyword_value("play_pcap_video"))) { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_VIDEO); hasMedia = 1; free(ptr); } else if ((cptr = xp_get_value("play_dtmf"))) { tmpAction->setMessage(cptr); tmpAction->setActionType(CAction::E_AT_PLAY_DTMF); hasMedia = 1; #else } else if (xp_get_value("play_pcap_audio")) { ERROR("Scenario specifies a play_pcap_audio action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_pcap_image")) { ERROR("Scenario specifies a play_pcap_image action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_pcap_video")) { ERROR("Scenario specifies a play_pcap_video action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_dtmf")) { ERROR("Scenario specifies a play_dtmf action, but this version of SIPp does not have PCAP support"); #endif } else if ((ptr = xp_get_keyword_value("rtp_stream"))) { hasMedia = 1; if (!strcmp(ptr, "pauseapattern")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PAUSEAPATTERN); } else if (!strcmp(ptr, "resumeapattern")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RESUMEAPATTERN); } else if (!strncmp(ptr, "apattern", 8)) { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PLAYAPATTERN); } else if (!strcmp(ptr, "pausevpattern")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PAUSEVPATTERN); } else if (!strcmp(ptr, "resumevpattern")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RESUMEVPATTERN); } else if (!strncmp(ptr, "vpattern", 8)) { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PLAYVPATTERN); } else if (!strcmp(ptr, "pause")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PAUSE); } else if (!strcmp(ptr, "resume")) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RESUME); } else { tmpAction->setMessage(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PLAY); } free(ptr); } else if ((ptr = xp_get_keyword_value("rtp_echo"))) { hasMedia = 1; if (!strncmp(ptr, "startaudio", 10)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_STARTAUDIO); } else if (!strncmp(ptr, "updateaudio", 11)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_UPDATEAUDIO); } else if (!strncmp(ptr, "stopaudio", 9)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_STOPAUDIO); } else if (!strncmp(ptr, "startvideo", 10)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_STARTVIDEO); } else if (!strncmp(ptr, "updatevideo", 11)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_UPDATEVIDEO); } else if (!strncmp(ptr, "stopvideo", 9)) { tmpAction->setRTPEchoActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RTPECHO_STOPVIDEO); } } else { ERROR("illegal in the scenario"); } } else if (!strcmp(actionElem, "rtp_echo")) { tmpAction->setActionType(CAction::E_AT_RTP_ECHO); handle_rhs(tmpAction, "rtp_echo"); } else { ERROR("Unknown action: %s", actionElem); } /* If the action was not well-formed, there should have already been an * ERROR declaration, thus it is safe to add it here at the end of the loop. */ actions->setAction(tmpAction); xp_close_element(); recvScenarioLen++; } // end while } // Action list for the message indexed by message_index in // the scenario void scenario::getActionForThisMessage(message *message) { char * actionElem; if (!(actionElem = xp_open_element(0))) { return; } if (strcmp(actionElem, "action")) { xp_close_element(); return; } /* We actually have an action element. */ if (message->M_actions != NULL) { ERROR("Duplicate action for %s index %d", message->desc, message->index); } message->M_actions = new CActions(); parseAction(message->M_actions); xp_close_element(); } void scenario::getBookKeeping(message *message) { const char *ptr; if ((ptr = xp_get_value("rtd"))) { message->stop_rtd = get_rtd(ptr, false); } if ((ptr = xp_get_value("repeat_rtd"))) { if (message->stop_rtd) { message->repeat_rtd = get_bool(ptr, "repeat_rtd"); } else { ERROR("There is a repeat_rtd element without an rtd element"); } } if ((ptr = xp_get_value("start_rtd"))) { message->start_rtd = get_rtd(ptr, true); } if ((ptr = xp_get_value("counter"))) { message->counter = get_counter(ptr, "counter"); } } void scenario::getCommonAttributes(message *message) { const char *ptr; getBookKeeping(message); getActionForThisMessage(message); if ((ptr = xp_get_value("lost"))) { message -> lost = get_double(ptr, "lost percentage"); lose_packets = 1; } if ((ptr = xp_get_value("crlf"))) { message -> crlf = 1; } if ((ptr = xp_get_value("ignoresdp"))) { message->ignoresdp = get_bool(ptr, "ignoresdp"); } if (xp_get_value("hiderest")) { hidedefault = xp_get_bool("hiderest", "hiderest"); } message -> hide = xp_get_bool("hide", "hide", hidedefault); if((ptr = xp_get_value((char *)"display"))) { message -> display_str = strdup(ptr); } message -> condexec = xp_get_var("condexec", "condexec variable", -1); message -> condexec_inverse = xp_get_bool("condexec_inverse", "condexec_inverse", false); if ((ptr = xp_get_value("next"))) { if (found_timewait) { ERROR("next labels are not allowed in elements."); } message->nextLabel = strdup(ptr); message->test = xp_get_var("test", "test variable", -1); if ( 0 != ( ptr = xp_get_value((char *)"chance") ) ) { float chance = get_double(ptr,"chance"); /* probability of branch to next */ if (( chance < 0.0 ) || (chance > 1.0 )) { ERROR("Chance %s not in range [0..1]", ptr); } message -> chance = (int)((1.0-chance)*RAND_MAX); } else { message -> chance = 0; /* always */ } } if ((ptr = xp_get_value((char *)"ontimeout"))) { if (found_timewait) { ERROR("ontimeout labels are not allowed in elements."); } message -> onTimeoutLabel = strdup(ptr); } } // char* manipulation : create a int[] from a char* // test first is the char* is formed by int separeted by coma // and then create the table int isWellFormed(char * P_listeStr, int * nombre) { char * ptr = P_listeStr; int sizeOf; bool isANumber; (*nombre) = 0; sizeOf = strlen(P_listeStr); // getting the number if(sizeOf > 0) { // is the string well formed ? [0-9] [,] isANumber = false; for(int i=0; i<=sizeOf; i++) { switch(ptr[i]) { case ',': if(isANumber == false) { return(0); } else { (*nombre)++; } isANumber = false; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': isANumber = true; break; case '\t': case ' ' : break; case '\0': if(isANumber == false) { return(0); } else { (*nombre)++; } break; default: return(0); } } // end for } return(1); } int createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList) { int nb=0; char * ptr = P_listeStr; char * ptr_prev = P_listeStr; unsigned int current_int; if(P_listeStr) { if(isWellFormed(P_listeStr, sizeOfList) == 1) { (*listeInteger) = new unsigned int[(*sizeOfList)]; while((*ptr) != ('\0')) { if((*ptr) == ',') { sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; ptr_prev = ptr+1; } ptr++; } // Read the last sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; return(1); } return(0); } else return(0); } int createStringTable(const char* inputString, char*** stringList, int* sizeOfList) { if(!inputString) { return 0; } *stringList = NULL; *sizeOfList = 0; /* FIXME: temporary workaround: needs rewrite */ char* input = const_cast(inputString); do { char* p = strchr(input, ','); if (p) { *p++ = '\0'; } *stringList = (char **)realloc(*stringList, sizeof(char *) * (*sizeOfList + 1)); (*stringList)[*sizeOfList] = strdup(input); (*sizeOfList)++; input = p; } while (input); return 1; } void freeStringTable(char ** stringList, int sizeOfList) { for (int i = 0; i < sizeOfList; i++) { free(stringList[i]); } free(stringList); } /* These are the names of the scenarios, they must match the default_scenario table. */ const char *scenario_table[] = { "uac", "uas", "regexp", "3pcc-C-A", "3pcc-C-B", "3pcc-A", "3pcc-B", "branchc", "branchs", "uac_pcap", "ooc_default", "ooc_dummy", }; int find_scenario(const char *scenario) { int i, max; max = sizeof(scenario_table)/sizeof(scenario_table[0]); for (i = 0; i < max; i++) { if (!strcmp(scenario_table[i], scenario)) { return i; } } ERROR("Invalid default scenario name '%s'", scenario); return -1; } // TIP: to integrate an existing XML scenario, use the following sed line: // cat ../3pcc-controller-B.xml | sed -e 's/\"/\\\"/g' -e 's/\(.*\)/\"\1\\n\"/' const char * default_scenario [] = { /************* Default_scenario[0] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n" , /************* Default_scenario[1] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[2] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " retrievedIp: [$1]\n" " retrievedContact:[$6]\n" " retrievedSdpOrigin:[$3]\n" " retrievedSdpOrigin-username:[$4]\n" " retrievedSdpOrigin-session-id:[$5]\n" " retrievedSdpOrigin-version:[$8]\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[3] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " [$2]\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" "\n" "\n", /************* Default_scenario[4] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " [$1]\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" "\n" "\n" "\n", /************* Default_scenario[5] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[6] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n", /************* Default_scenario[7] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag07[call_number]\n" " To: ua1 \n" " Call-ID: [call_id]\n" " CSeq: 1 REGISTER\n" " Contact: sip:ua1@[local_ip]:[local_port]\n" " Content-Length: 0\n" " Expires: 300\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" "\n", /************* Default_scenario[8] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" " Expires: 300\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n", /* Although this scenario will not work without pcap play enabled, there is no * harm in including it in the binary anyway, because the user could have * dumped it and passed it with -sf. */ /************* Default_scenario[9] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[local_ip_type] [local_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 8 101\n" " a=rtpmap:8 PCMA/8000\n" " a=rtpmap:101 telephone-event/8000\n" " a=fmtp:101 0-11,16\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n", "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n", }; sipp-3.7.2/src/screen.cpp0000664000000000000000000010500614525516253012152 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #include #include "screen.hpp" #include "sipp.hpp" /* Export these so others needn't include curses.h */ int key_backspace = KEY_BACKSPACE; int key_dc = KEY_DC; int screen_inited = 0; ScreenPrinter* sp; double last_artpstream_rate_out = 0; double last_vrtpstream_rate_out = 0; double last_artpstream_rate_in = 0; double last_vrtpstream_rate_in = 0; /* ERR is actually -1, but this prevents us from needing to use curses.h in * sipp.cpp. */ int screen_readkey() { int c = getch(); if (c == ERR) { return -1; } return c; } void screen_exit() { if (!screen_inited) { return; } clear(); refresh(); endwin(); screen_inited = 0; } void screen_init() { if (backgroundMode || screen_inited) { return; } screen_inited = 1; setlocale(LC_ALL, ""); initscr(); cbreak(); noecho(); clear(); } void print_statistics(int last) { if (backgroundMode == false && display_scenario) { sp->redraw(); } } void ScreenPrinter::print_closing_stats() { M_last = true; get_lines(); for (auto line : lines) { printf("%s\n", line.c_str()); } if (currentScreenToDisplay != DISPLAY_STAT_SCREEN) { currentScreenToDisplay = DISPLAY_STAT_SCREEN; get_lines(); for (auto line : lines) { printf("%s\n", line.c_str()); } } } void ScreenPrinter::print_to_file(FILE* f) { get_lines(); for (auto line : lines) { fprintf(f, "%s\n", line.c_str()); } } extern int command_mode; extern char* command_buffer; void ScreenPrinter::redraw() { if (!M_headless) { get_lines(); erase(); for (auto line : lines) { printw("%s\n", line.c_str()); } if (command_mode) { printw("\nCommand: %s", command_buffer ? command_buffer : ""); } refresh(); } } void ScreenPrinter::get_lines() { lines.clear(); switch (currentScreenToDisplay) { case DISPLAY_STAT_SCREEN: lines.push_back("----------------------------- Statistics Screen " "------- [1-9]: Change Screen --"); draw_stats_screen(); break; case DISPLAY_REPARTITION_SCREEN: lines.push_back("---------------------------- Repartition Screen " "------- [1-9]: Change Screen --"); draw_repartition_screen(1); break; case DISPLAY_VARIABLE_SCREEN: lines.push_back("----------------------------- Variables Screen " "-------- [1-9]: Change Screen --"); draw_vars_screen(); break; case DISPLAY_TDM_MAP_SCREEN: lines.push_back("------------------------------ TDM map Screen " "--------- [1-9]: Change Screen --"); draw_tdm_screen(); break; case DISPLAY_SECONDARY_REPARTITION_SCREEN: lines.push_back("--------------------------- Repartition " + std::to_string(currentRepartitionToDisplay) + " Screen ------ [1-9]: Change Screen --"); draw_repartition_screen(currentRepartitionToDisplay); break; case DISPLAY_SCENARIO_SCREEN: default: lines.push_back("------------------------------ Scenario Screen " "-------- [1-9]: Change Screen --"); draw_scenario_screen(); break; } unsigned const bufsiz = 80; char buf[bufsiz]; if (!M_last && screen_last_error[0]) { char* errstart = screen_last_error; int colonsleft = 3; /* We want to skip the time. */ while (*errstart && colonsleft) { if (*errstart == ':') { colonsleft--; } errstart++; } while (isspace(*errstart)) { errstart++; } if (strlen(errstart) > 60) { snprintf(buf, bufsiz, "Last Error: %.60s...", errstart); } else { snprintf(buf, bufsiz, "Last Error: %s", errstart); } lines.push_back(buf); } if (M_last) { lines.push_back("------------------------------ Test Terminated " "--------------------------------"); } else if (quitting) { lines.push_back( "------- Waiting for active calls to end. Press [q] again " "to force exit. -------"); } else if (paused) { lines.push_back("----------------- Traffic Paused - Press [p] again to " "resume ------------------"); } else if (cpu_max) { lines.push_back("-------------------------------- CPU CONGESTED " "---------------------------------"); } else if (outbound_congestion) { lines.push_back("------------------------------ OUTBOUND CONGESTION " "-----------------------------"); } else { if (creationMode == MODE_CLIENT) { switch (thirdPartyMode) { case MODE_MASTER: lines.push_back( "-----------------------3PCC extended mode - Master " "side -------------------------"); break; case MODE_3PCC_CONTROLLER_A: lines.push_back( "----------------------- 3PCC Mode - Controller A " "side -------------------------"); break; case MODE_3PCC_NONE: lines.push_back( "------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit " "---- [p]: Pause traffic -----"); break; default: ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); } } else { assert(creationMode == MODE_SERVER); switch (thirdPartyMode) { case MODE_3PCC_A_PASSIVE: lines.push_back( "------------------ 3PCC Mode - Controller A side " "(passive) --------------------"); break; case MODE_3PCC_CONTROLLER_B: lines.push_back( "----------------------- 3PCC Mode - Controller B " "side -------------------------"); break; case MODE_MASTER_PASSIVE: lines.push_back( "------------------ 3PCC extended mode - Master " "side (passive) --------------------"); break; case MODE_SLAVE: lines.push_back( "----------------------- 3PCC extended mode - Slave " "side -------------------------"); break; case MODE_3PCC_NONE: lines.push_back( "------------------------------ SIPp Server Mode " "-------------------------------"); break; default: ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); } } } } bool do_hide = true; void ScreenPrinter::draw_scenario_screen() { unsigned const bufsiz = 80; char buf[bufsiz]; char left_buf[40]; char right_buf[bufsiz]; extern int pollnfds; unsigned long long total_calls = display_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + display_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); if (creationMode == MODE_SERVER) { lines.push_back(" Port Total-time Total-calls Transport"); snprintf(buf, bufsiz, " %-5d %6lu.%02lu s %8llu %s", local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } else { assert(creationMode == MODE_CLIENT); if (users >= 0) { lines.push_back(" Users (length) Port Total-time " "Total-calls Remote-host"); snprintf(buf, bufsiz, " %d (%d ms) %-5d %6lu.%02lu s %8llu %.20s:%d(%s)", users, duration, local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, remote_ip, remote_port, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } else { lines.push_back(" Call rate (length) Port Total-time " "Total-calls Remote-host"); snprintf( buf, bufsiz, " %3.1f(%d ms)/%5.3fs %-5d %6lu.%02lu s %8llu %.20s:%d(%s)", rate, duration, (double)rate_period_ms / 1000.0, local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, remote_ip, remote_port, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } } lines.push_back(""); /* 1st line */ unsigned long ms_since_last_tick = clock_tick - last_report_time; if (total_calls < stop_after) { snprintf(left_buf, 40, "%llu new calls during %lu.%03lu s period", display_scenario->stats->GetStat( CStat::CPT_PD_IncomingCallCreated) + display_scenario->stats->GetStat( CStat::CPT_PD_OutgoingCallCreated), ms_since_last_tick / 1000, ms_since_last_tick % 1000); } else { snprintf(left_buf, 40, "Call limit %lu hit, %0.1f s period ", stop_after, (double)ms_since_last_tick / 100.0); } snprintf(right_buf, 40, "%lu ms scheduler resolution", ms_since_last_tick / std::max(scheduling_loops, 1ul)); snprintf(buf, bufsiz, " %-38.38s %-37.37s", left_buf, right_buf); lines.push_back(buf); /* 2nd line */ if (creationMode == MODE_SERVER) { snprintf(left_buf, 40, "%llu calls", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)); } else { snprintf(left_buf, 40, "%llu calls (limit %u)", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall), open_calls_allowed); } snprintf( buf, bufsiz, " %-38s Peak was %llu calls, after %llu s", left_buf, display_scenario->stats->GetStat(CStat::CPT_C_CurrentCallPeak), display_scenario->stats->GetStat(CStat::CPT_C_CurrentCallPeakTime)); lines.push_back(buf); snprintf(buf, bufsiz, " %d Running, %d Paused, %d Woken up", last_running_calls, last_paused_calls, last_woken_calls); last_woken_calls = 0; lines.push_back(buf); /* 3rd line dead call msgs, and optional out-of-call msg */ snprintf(left_buf, 40, "%llu dead call msg (discarded)", display_scenario->stats->GetStat(CStat::CPT_G_C_DeadCallMsgs)); if (creationMode == MODE_CLIENT) { snprintf( buf, bufsiz, " %-38s %llu out-of-call msg (discarded)", left_buf, display_scenario->stats->GetStat(CStat::CPT_G_C_OutOfCallMsgs)); } else { snprintf(buf, bufsiz, " %-38s", left_buf); } lines.push_back(buf); if (compression) { snprintf(buf, bufsiz, " Comp resync: %d sent, %d recv", resynch_send, resynch_recv); lines.push_back(buf); } if (auto_answer) { snprintf(buf, 80, " %llu requests auto-answered", display_scenario->stats->GetStat(CStat::CPT_G_C_AutoAnswered)); lines.push_back(buf); } /* 4th line , sockets and optional errors */ snprintf(left_buf, 40, "%d open sockets", pollnfds); snprintf(buf, bufsiz, " %-38s %lu/%lu/%lu %s errors (send/recv/cong)", left_buf, nb_net_send_errors, nb_net_recv_errors, nb_net_cong, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); #ifdef PCAPPLAY /* if has media abilities */ if (hasMedia != 5) { snprintf(left_buf, 40, "%lu Total RTP pckts sent ", rtp_pckts_pcap); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp_bytes_pcap / ms_since_last_tick, rtp_bytes_pcap % ms_since_last_tick); } rtp_bytes_pcap = 0; rtp2_bytes_pcap = 0; lines.push_back(buf); } #endif /* if we have rtp stream thread running */ if (rtpstream_numthreads) { unsigned long TempABytes; unsigned long TempVBytes; if (ms_since_last_tick) { TempABytes= rtpstream_abytes_out; TempVBytes= rtpstream_vbytes_out; /* Calculate integer and fraction parts of rtp bandwidth; this value * will be saved and reused in the case where last_tick==last_report_time */ last_artpstream_rate_out= ((double)TempABytes)/ ms_since_last_tick; last_vrtpstream_rate_out= ((double)TempVBytes)/ ms_since_last_tick; /* Potential race condition betwen multiple threads updating the * rtpstream_bytes value. We subtract the saved TempBytes value * rather than setting it to zero to minimise the chances of missing * an update to rtpstream_bytes [update between printing stats and * zeroing the counter]. Ideally we would atomically subtract * TempBytes from rtpstream_bytes. */ rtpstream_abytes_out -= TempABytes; rtpstream_vbytes_out -= TempVBytes; TempABytes= rtpstream_abytes_in; TempVBytes= rtpstream_vbytes_in; last_artpstream_rate_in= ((double)TempABytes)/ ms_since_last_tick; last_vrtpstream_rate_in= ((double)TempVBytes)/ ms_since_last_tick; rtpstream_abytes_in -= TempABytes; rtpstream_vbytes_in -= TempVBytes; } snprintf(left_buf, 40, "%lu Total AUDIO RTP pckts sent", rtpstream_apckts); snprintf(buf, bufsiz, " %-38s %.3f kB/s AUDIO RTP OUT", left_buf, last_artpstream_rate_out); lines.push_back(buf); snprintf(left_buf, 40, "%lu Total VIDEO RTP pckts sent", rtpstream_vpckts); snprintf(buf, bufsiz, " %-38s %.3f KB/s VIDEO RTP OUT", left_buf, last_vrtpstream_rate_out); lines.push_back(buf); snprintf(left_buf, 40, "%lu RTP sending threads active", rtpstream_numthreads); snprintf(buf, bufsiz, " %-38s %.3f kB/s AUDIO RTP IN", left_buf, last_artpstream_rate_in); lines.push_back(buf); snprintf(buf, bufsiz, " %-38s %.3f KB/s VIDEO RTP IN", left_buf, last_vrtpstream_rate_in); lines.push_back(buf); } /* 5th line, RTP echo statistics */ if (rtp_echo_enabled && media_socket_audio > 0) { snprintf(left_buf, 40, "%lu Total echo RTP pckts 1st stream", rtp_pckts); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp_bytes / ms_since_last_tick, rtp_bytes % ms_since_last_tick); lines.push_back(buf); } snprintf(left_buf, 40, "%lu Total echo RTP pckts 2nd stream", rtp2_pckts); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp2_bytes / ms_since_last_tick, rtp2_bytes % ms_since_last_tick); lines.push_back(buf); } rtp_bytes = 0; rtp2_bytes = 0; } /* Scenario counters */ lines.push_back(""); if (!lose_packets) { snprintf(buf, bufsiz, " " "Messages Retrans Timeout Unexpected-Msg"); } else { snprintf(buf, bufsiz, " " "Messages Retrans Timeout Unexp. Lost"); } lines.push_back(buf); for (unsigned long index = 0; index < display_scenario->messages.size(); index++) { buf[0] = 0; message* curmsg = display_scenario->messages[index]; if (do_hide && curmsg->hide) { continue; } int buf_len = 0; if (SendingMessage* src = curmsg->send_scheme) { if (creationMode == MODE_SERVER) { if (src->isResponse()) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " <---------- %-10d ", src->getCode()); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " <---------- %-10s ", src->getMethod()); } } else { if (src->isResponse()) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10d ----------> ", src->getCode()); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10s ----------> ", src->getMethod()); } } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } if (curmsg->retrans_delay) { buf_len += snprintf( buf + buf_len, bufsiz - buf_len, "%-9lu %-9lu %-9lu %-9s %-9s", curmsg->nb_sent, curmsg->nb_sent_retrans, curmsg->nb_timeout, "" /* Unexpected */, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else { buf_len += snprintf( buf + buf_len, bufsiz - buf_len, "%-9lu %-9lu %-9s %-9s %-9s", curmsg->nb_sent, curmsg->nb_sent_retrans, "", /* Timeout. */ "" /* Unexpected. */, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } } else if (curmsg->recv_response) { if (creationMode == MODE_SERVER) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " ----------> %-10d ", curmsg->recv_response); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10d <---------- ", curmsg->recv_response); } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } buf_len += snprintf(buf + buf_len, bufsiz - buf_len, "%-9ld %-9ld %-9ld %-9ld %-9s", curmsg->nb_recv, curmsg->nb_recv_retrans, curmsg->nb_timeout, curmsg->nb_unexp, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else if (curmsg->pause_distribution || (curmsg->pause_variable != -1)) { char* desc = curmsg->pause_desc; if (!desc) { desc = (char*)malloc(24); if (curmsg->pause_distribution) { desc[0] = '\0'; curmsg->pause_distribution->timeDescr(desc, 23); } else { snprintf(desc, 23, "$%s", display_scenario->allocVars->getName( curmsg->pause_variable)); } desc[23] = '\0'; curmsg->pause_desc = desc; } int len = strlen(desc) < 9 ? 9 : strlen(desc); if (creationMode == MODE_SERVER) { snprintf(left_buf, 40, " [%9s] Pause%*s", desc, 23 - len > 0 ? 23 - len : 0, ""); } else { snprintf(left_buf, 40, " Pause [%9s]%*s", desc, 18 - len > 0 ? 18 - len : 0, ""); } snprintf(buf, bufsiz, "%s%-9d %-9lu", left_buf, curmsg->sessions, curmsg->nb_unexp); } else if (curmsg->recv_request) { if (creationMode == MODE_SERVER) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " ----------> %-10s ", curmsg->recv_request); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10s <---------- ", curmsg->recv_request); } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } buf_len += snprintf(buf + buf_len, bufsiz - buf_len, "%-9ld %-9ld %-9ld %-9ld %-9s", curmsg->nb_recv, curmsg->nb_recv_retrans, curmsg->nb_timeout, curmsg->nb_unexp, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else if (curmsg->M_type == MSG_TYPE_NOP) { if (curmsg->display_str) { snprintf(buf, bufsiz, " %s", curmsg->display_str); } else { snprintf(buf, bufsiz, " [ NOP ] "); } } else if (curmsg->M_type == MSG_TYPE_RECVCMD) { snprintf(left_buf, 40, " [ Received Command ] "); snprintf(buf, bufsiz, "%s%-9ld %-9s %-9s %-9s", left_buf, curmsg->M_nbCmdRecv, "", curmsg->retrans_delay ? std::to_string(curmsg->nb_timeout).c_str() : "", ""); } else if (curmsg->M_type == MSG_TYPE_SENDCMD) { snprintf(left_buf, 40, " [ Sent Command ] "); snprintf(buf, bufsiz, "%s%-9lu %-9s %-9s", left_buf, curmsg->M_nbCmdSent, "", ""); } else if (curmsg->M_type == MSG_TYPE_RECV) { WARNING(" without request/response?"); snprintf(buf, bufsiz, " [ recv? ] "); } else { ERROR("Scenario command %d not implemented in display", curmsg->M_type); } char buf_with_index[80] = {0}; snprintf(buf_with_index, 80, "%-2lu:%s", index, buf); lines.push_back(buf_with_index); if (curmsg->crlf) { lines.push_back(""); } } } // Warning! All DISPLAY_ macros must be called where 'bufsiz', 'buf' and 'lines' are defined. #define DISPLAY_CROSS_LINE() \ snprintf(buf, bufsiz, "-------------------------+---------------------------+--------------------------"); \ lines.push_back(buf); #define DISPLAY_HEADER() \ snprintf(buf, bufsiz, " Counter Name | Periodic value | Cumulative value"); \ lines.push_back(buf); #define DISPLAY_TXT_COL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %-25.25s | %-24.24s ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_VAL_RATEF_COL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %8.3f cps | %8.3f cps ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_2VAL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %8llu | %8llu ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_CUMUL(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | | %8llu ", T1, V1); \ lines.push_back(buf); #define DISPLAY_PERIO(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | %8llu | ", T1, V1); \ lines.push_back(buf); #define DISPLAY_TXT(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | %-52.52s ", T1, V1); \ lines.push_back(buf); #define DISPLAY_INFO(T1) \ snprintf(buf, bufsiz, " %-77.77s", T1); \ lines.push_back(buf); #define DISPLAY_REPART(T1, T2, V1) \ snprintf(buf, bufsiz, " %10d ms <= n < %10d ms : %10lu", T1, T2, V1); \ lines.push_back(buf); #define DISPLAY_LAST_REPART(T1, V1) \ snprintf(buf, bufsiz, " %14.14s n >= %10d ms : %10lu", "", T1, V1); \ lines.push_back(buf); void ScreenPrinter::draw_stats_screen() { long localElapsedTime, globalElapsedTime ; struct timeval currentTime; float averageCallRate; float realInstantCallRate; unsigned long numberOfCall; CStat* s = display_scenario->stats; unsigned const bufsiz = 256; char buf[bufsiz]; GET_TIME (¤tTime); // computing the real call rate globalElapsedTime = s->computeDiffTimeInMs (¤tTime, &s->M_startTime); localElapsedTime = s->computeDiffTimeInMs (¤tTime, &s->M_pdStartTime); // the call rate is for all the call : incoming and outgoing numberOfCall = (s->M_counters[s->CPT_C_IncomingCallCreated] + s->M_counters[s->CPT_C_OutgoingCallCreated]); averageCallRate = (globalElapsedTime > 0 ? 1000*(float)numberOfCall/(float)globalElapsedTime : 0.0); numberOfCall = (s->M_counters[s->CPT_PD_IncomingCallCreated] + s->M_counters[s->CPT_PD_OutgoingCallCreated]); realInstantCallRate = (localElapsedTime > 0 ? 1000*(float)numberOfCall / (float)localElapsedTime : 0.0); // build and display header info DISPLAY_TXT ("Start Time ", s->formatTime(&s->M_startTime)); DISPLAY_TXT ("Last Reset Time", s->formatTime(&s->M_pdStartTime)); DISPLAY_TXT ("Current Time", s->formatTime(¤tTime)); // printing the header in the middle DISPLAY_CROSS_LINE(); DISPLAY_HEADER(); DISPLAY_CROSS_LINE(); DISPLAY_TXT_COL ("Elapsed Time", s->msToHHMMSSus(localElapsedTime), s->msToHHMMSSus(globalElapsedTime)); DISPLAY_VAL_RATEF_COL ("Call Rate", realInstantCallRate, averageCallRate); DISPLAY_CROSS_LINE (); DISPLAY_2VAL ("Incoming calls created", s->M_counters[s->CPT_PD_IncomingCallCreated], s->M_counters[s->CPT_C_IncomingCallCreated]); DISPLAY_2VAL ("Outgoing calls created", s->M_counters[s->CPT_PD_OutgoingCallCreated], s->M_counters[s->CPT_C_OutgoingCallCreated]); DISPLAY_CUMUL ("Total Calls created", s->M_counters[s->CPT_C_IncomingCallCreated] + s->M_counters[s->CPT_C_OutgoingCallCreated]); DISPLAY_PERIO ("Current Calls", s->M_counters[s->CPT_C_CurrentCall]); if (s->M_genericMap.size()) { DISPLAY_CROSS_LINE (); } for (unsigned int i = 1; i < s->M_genericMap.size() + 1; i++) { char *disp = (char *)malloc(20 + strlen(s->M_genericDisplay[i])); sprintf(disp, "Counter %s", s->M_genericDisplay[i]); DISPLAY_2VAL(disp, s->M_genericCounters[(i - 1)* GENERIC_TYPES + GENERIC_PD], s->M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]); free(disp); } DISPLAY_CROSS_LINE (); DISPLAY_2VAL ("Successful call", s->M_counters[s->CPT_PD_SuccessfulCall], s->M_counters[s->CPT_C_SuccessfulCall]); DISPLAY_2VAL ("Failed call", s->M_counters[s->CPT_PD_FailedCall], s->M_counters[s->CPT_C_FailedCall]); DISPLAY_CROSS_LINE (); for (int i = 1; i <= s->nRtds(); i++) { char buf2[80]; snprintf(buf2, 80, "Response Time %s", s->M_revRtdMap[i]); DISPLAY_TXT_COL (buf2, s->msToHHMMSSus( (unsigned long)s->computeRtdMean(i, GENERIC_PD)), s->msToHHMMSSus( (unsigned long)s->computeRtdMean(i, GENERIC_C))); } DISPLAY_TXT_COL ("Call Length", s->msToHHMMSSus( (unsigned long)s->computeMean(s->CPT_PD_AverageCallLength_Sum, s->CPT_PD_NbOfCallUsedForAverageCallLength ) ), s->msToHHMMSSus( (unsigned long)s->computeMean(s->CPT_C_AverageCallLength_Sum, s->CPT_C_NbOfCallUsedForAverageCallLength) )); } void ScreenPrinter::draw_repartition_screen(int which) { unsigned const bufsiz = 80; char buf[bufsiz]; char buf2[bufsiz]; CStat* s = display_scenario->stats; if (which > s->nRtds()) { DISPLAY_INFO (" "); return; } snprintf(buf2, bufsiz, "Average Response Time Repartition %s", s->M_revRtdMap[which]); DISPLAY_INFO(buf2); draw_repartition_detailed(s->M_ResponseTimeRepartition[which - 1], s->M_SizeOfResponseTimeRepartition); if (which == 1) { // Primary repartition screen DISPLAY_INFO("Average Call Length Repartition"); draw_repartition_detailed(s->M_CallLengthRepartition, s->M_SizeOfCallLengthRepartition); } } void ScreenPrinter::draw_repartition_detailed(CStat::T_dynamicalRepartition * tabRepartition, int sizeOfTab) { unsigned const bufsiz = 80; char buf[bufsiz]; if(tabRepartition != NULL) { for(int i=0; i<(sizeOfTab-1); i++) { if(i==0) { DISPLAY_REPART(0, tabRepartition[i].borderMax, tabRepartition[i].nbInThisBorder); } else { DISPLAY_REPART(tabRepartition[i-1].borderMax, tabRepartition[i].borderMax, tabRepartition[i].nbInThisBorder); } } DISPLAY_LAST_REPART (tabRepartition[sizeOfTab-1].borderMax, tabRepartition[sizeOfTab-1].nbInThisBorder); } else { DISPLAY_INFO (" "); } } void ScreenPrinter::draw_vars_screen() { CActions* actions; CAction* action; bool found; unsigned const bufsiz = 80; char buf[bufsiz]; lines.push_back("Action defined Per Message :"); found = false; for (unsigned int i = 0; i < display_scenario->messages.size(); i++) { message* curmsg = display_scenario->messages[i]; actions = curmsg->M_actions; if (actions != NULL) { switch (curmsg->M_type) { case MSG_TYPE_RECV: snprintf(buf, bufsiz, "=> Message[%u] (Receive Message) - " "[%d] action(s) defined :", i, actions->getActionSize()); break; case MSG_TYPE_RECVCMD: snprintf(buf, bufsiz, "=> Message[%u] (Receive Command Message) - " "[%d] action(s) defined :", i, actions->getActionSize()); break; default: snprintf(buf, bufsiz, "=> Message[%u] - [%d] action(s) defined :", i, actions->getActionSize()); break; } lines.push_back(buf); for (int j = 0; j < actions->getActionSize(); j++) { action = actions->getAction(j); if (action != NULL) { int printed = snprintf(buf, bufsiz, " --> action[%d] = ", j); action->printInfo(buf + printed, bufsiz - printed); lines.push_back(buf); found = true; } } } } if (!found) { lines.push_back("=> No action found on any messages"); } lines.push_back(""); for (unsigned int i = 0; i < (display_scenario->messages.size() + 6 - lines.size()); i++) { lines.push_back(""); } } void ScreenPrinter::draw_tdm_screen() { int i = 0; int in_use = 0; int height = (tdm_map_a + 1) * (tdm_map_b + 1); int width = (tdm_map_c + 1); int total_circuits = height * width; unsigned const bufsiz = 80; char buf[bufsiz] = {0}; lines.push_back("TDM Circuits in use:"); while (i < total_circuits) { int buf_position = std::min(79, i % width); if (tdm_map[i]) { buf[buf_position] = '*'; in_use++; } else { buf[buf_position] = '.'; } i++; if (buf_position == (width - 1)) { lines.push_back(buf); memset(buf, 0, bufsiz); } } lines.push_back(""); snprintf(buf, bufsiz, "%d/%d circuits (%d%%) in use", in_use, total_circuits, int(100 * in_use / total_circuits)); lines.push_back(buf); for (unsigned int i = 0; i < (display_scenario->messages.size() + 8 - height); i++) { lines.push_back(""); } } sipp-3.7.2/src/send_packets.c0000664000000000000000000002547614525516253013012 0ustar /* * send_packets.c: from tcpreplay tools by Aaron Turner * http://tcpreplay.sourceforge.net/ * send_packets.c is under BSD license (see below) * SIPp is under GPL license * * * Copyright (c) 2001-2004 Aaron Turner. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright owners nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" #include "fileutil.h" #include "send_packets.h" #include "prepare_pcap.h" #include "config.h" #ifndef HAVE_UDP_UH_PREFIX #define uh_ulen len #define uh_sum check #define uh_sport source #define uh_dport dest #endif extern volatile unsigned long rtp_pckts_pcap; extern volatile unsigned long rtp_bytes_pcap; extern bool media_ip_is_ipv6; inline void timerdiv(struct timeval* tvp, float div) { double interval; if (div == 0 || div == 1) return; interval = ((double) tvp->tv_sec * 1000000 + tvp->tv_usec) / (double) div; tvp->tv_sec = interval / (int) 1000000; tvp->tv_usec = interval - (tvp->tv_sec * 1000000); } /* * converts a float to a timeval structure */ inline void float2timer(float time, struct timeval *tvp) { float n; n = time; tvp->tv_sec = n; n -= tvp->tv_sec; tvp->tv_usec = n * 100000; } int parse_play_args(const char* filename, pcap_pkts* pkts) { pkts->file = find_file(filename); prepare_pkts(pkts->file, pkts); return 1; } void free_pcaps(pcap_pkts* pkts) { pcap_pkt *it; for (it = pkts->pkts; it != pkts->max; ++it) { free(it->data); } free(pkts->pkts); free(pkts->file); free(pkts); } int parse_dtmf_play_args(const char* buffer, pcap_pkts* pkts, uint16_t start_seq_no) { pkts->file = strdup(buffer); return prepare_dtmf(pkts->file, pkts, start_seq_no); } void hexdump(char *p, int s) { int i; for (i = 0; i < s; i++) { fprintf(stderr, "%02x ", *(char *)(p+i)); } fprintf(stderr, "\n"); } /* Safe threaded version */ void do_sleep (struct timeval *, struct timeval *, struct timeval *, struct timeval *); void send_packets_cleanup(void *arg) { int * sock = (int *) arg; /* Close send socket */ close(*sock); } void send_packets_pcap_cleanup(void* arg) { play_args_t* play_args = arg; if (play_args->free_pcap_when_done) { free(play_args->pcap); play_args->pcap = NULL; } } void send_packets(play_args_t* play_args) { pthread_cleanup_push(send_packets_pcap_cleanup, ((void*)play_args)); int ret = 0, sock, port_diff; pcap_pkt *pkt_index, *pkt_max; uint16_t *from_port, *to_port; struct timeval didsleep = { 0, 0 }; struct timeval start = { 0, 0 }; struct timeval last = { 0, 0 }; pcap_pkts *pkts = play_args->pcap; /* to and from are pointers in case play_args (call sticky) gets modified! */ struct sockaddr_storage *to = &(play_args->to); struct sockaddr_storage *from = &(play_args->from); struct sockaddr_storage bind_addr = {0}; struct udphdr *udp; struct sockaddr_in6 to6, from6; char buffer[PCAP_MAXPACKET]; int temp_sum; socklen_t len; #ifndef MSG_DONTWAIT int fd_flags; #endif if (media_ip_is_ipv6) { sock = socket(PF_INET6, SOCK_RAW, IPPROTO_UDP); if (sock < 0) { ERROR("Can't create raw IPv6 socket (need to run as root?): %s", strerror(errno)); goto pop2; } from_port = &(((struct sockaddr_in6 *)from)->sin6_port); len = sizeof(struct sockaddr_in6); to_port = &(((struct sockaddr_in6 *)to)->sin6_port); } else { sock = socket(PF_INET, SOCK_RAW, IPPROTO_UDP); from_port = &(((struct sockaddr_in *)from)->sin_port); len = sizeof(struct sockaddr_in); to_port = &(((struct sockaddr_in *)to)->sin_port); if (sock < 0) { ERROR("Can't create raw IPv4 socket (need to run as root?): %s", strerror(errno)); goto pop2; } } // When binding a raw socket, it doesn't make sense to bind to a particular // port, as that's a UDP/TCP concept but the point of a raw socket is that // we're writing the headers ourselves. Some systems (like FreeBSD) are // strict about this and return EADDRNOTAVAIL if we specify a port, so bind // to a sockaddr structure copied from our sending address but with the // port set to 0. if (media_ip_is_ipv6) { memcpy(&bind_addr, from, sizeof(struct sockaddr_in6)); ((struct sockaddr_in6 *)&bind_addr)->sin6_port = 0; } else { memcpy(&bind_addr, from, sizeof(struct sockaddr_in)); ((struct sockaddr_in *)&bind_addr)->sin_port = 0; } if ((ret = bind(sock, (struct sockaddr *)&bind_addr, len))) { ERROR("Can't bind media raw socket: %s", strerror(errno)); goto pop2; } #ifndef MSG_DONTWAIT fd_flags = fcntl(sock, F_GETFL , NULL); fd_flags |= O_NONBLOCK; fcntl(sock, F_SETFL , fd_flags); #endif udp = (struct udphdr *)buffer; pkt_index = pkts->pkts; pkt_max = pkts->max; if (media_ip_is_ipv6) { memset(&to6, 0, sizeof(to6)); memset(&from6, 0, sizeof(from6)); to6.sin6_family = AF_INET6; from6.sin6_family = AF_INET6; memcpy(&(to6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) to)->sin6_addr.s6_addr), sizeof(to6.sin6_addr.s6_addr)); memcpy(&(from6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) from)->sin6_addr.s6_addr), sizeof(from6.sin6_addr.s6_addr)); } /* Ensure the sender socket is closed when the thread exits - this * allows the thread to be cancelled cleanly. */ pthread_cleanup_push(send_packets_cleanup, ((void *) &sock)); while (pkt_index < pkt_max) { memcpy(udp, pkt_index->data, pkt_index->pktlen); port_diff = ntohs(udp->uh_dport) - pkts->base; /* modify UDP ports */ udp->uh_sport = htons(port_diff + ntohs(*from_port)); udp->uh_dport = htons(port_diff + ntohs(*to_port)); if (!media_ip_is_ipv6) { temp_sum = checksum_carry( pkt_index->partial_check + check((uint16_t *) &(((struct sockaddr_in *)(void *) from)->sin_addr.s_addr), 4) + check((uint16_t *) &(((struct sockaddr_in *)(void *) to)->sin_addr.s_addr), 4) + check((uint16_t *) &udp->uh_sport, 4)); } else { temp_sum = checksum_carry( pkt_index->partial_check + check((uint16_t *) &(from6.sin6_addr.s6_addr), 16) + check((uint16_t *) &(to6.sin6_addr.s6_addr), 16) + check((uint16_t *) &udp->uh_sport, 4)); } #if !defined(_HPUX_LI) && defined(__HPUX) udp->uh_sum = (temp_sum>>16)+((temp_sum & 0xffff)<<16); #else udp->uh_sum = temp_sum; #endif do_sleep ((struct timeval *) &pkt_index->ts, &last, &didsleep, &start); #ifdef MSG_DONTWAIT if (!media_ip_is_ipv6) { ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT, (struct sockaddr *)to, sizeof(struct sockaddr_in)); } else { ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT, (struct sockaddr *)&to6, sizeof(struct sockaddr_in6)); } #else if (!media_ip_is_ipv6) { ret = sendto(sock, buffer, pkt_index->pktlen, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in)); } else { ret = sendto(sock, buffer, pkt_index->pktlen, 0, (struct sockaddr *)&to6, sizeof(struct sockaddr_in6)); } #endif if (ret < 0) { WARNING("send_packets.c: sendto failed with error: %s", strerror(errno)); goto pop1; } rtp_pckts_pcap++; rtp_bytes_pcap += pkt_index->pktlen - sizeof(*udp); memcpy (&last, &(pkt_index->ts), sizeof(struct timeval)); pkt_index++; } /* Closing the socket is handled by pthread_cleanup_push()/pthread_cleanup_pop() */ pop1: pthread_cleanup_pop(1); pop2: pthread_cleanup_pop(1); } /* * Given the timestamp on the current packet and the last packet sent, * calculate the appropriate amount of time to sleep and do so. */ void do_sleep(struct timeval* time, struct timeval* last, struct timeval* didsleep, struct timeval* start) { struct timeval nap, now, delta; struct timespec sleep; if (gettimeofday (&now, NULL) < 0) { fprintf (stderr, "Error gettimeofday: %s\n", strerror (errno)); } /* First time through for this file */ if (!timerisset (last)) { *start = now; timerclear (&delta); timerclear (didsleep); } else { timersub (&now, start, &delta); } if (timerisset (last) && timercmp (time, last, >)) { timersub (time, last, &nap); } else { /* * Don't sleep if this is our first packet, or if the * this packet appears to have been sent before the * last packet. */ timerclear (&nap); } timeradd (didsleep, &nap, didsleep); if (timercmp (didsleep, &delta, >)) { timersub (didsleep, &delta, &nap); sleep.tv_sec = nap.tv_sec; sleep.tv_nsec = nap.tv_usec * 1000; /* convert ms to ns */ while ((nanosleep (&sleep, &sleep) == -1) && (errno == -EINTR)); } } sipp-3.7.2/src/sip_parser.cpp0000664000000000000000000005413214525516253013045 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken * Walter Doekes */ #include #include #include "screen.hpp" #include "sip_parser.hpp" /*************************** Mini SIP parser (internals) ***************/ /* * SIP ABNF can be found here: * http://tools.ietf.org/html/rfc3261#section-25 * In 2014, there is a very helpful site that lets you browse the ABNF * easily: * http://www.in2eps.com/fo-abnf/tk-fo-abnf-sip.html */ static const char* internal_find_param(const char* ptr, const char* name); static const char* internal_find_header(const char* msg, const char* name, const char* shortname, bool content); static const char* internal_skip_lws(const char* ptr); /* Search for a character, but only inside this header. Returns NULL if * not found. */ static const char* internal_hdrchr(const char* ptr, const char needle); /* Seek to end of this header. Returns the position the next character, * which must be at the header-delimiting-CRLF or, if the message is * broken, at the ASCIIZ NUL. */ static const char* internal_hdrend(const char* ptr); static const char* internal_compact_header_name(const char* name); static char* internal_match_header(char* message, const char* hdr, const char* compact_hdr); /*************************** Mini SIP parser (externals) ***************/ char* get_peer_tag(const char* msg) { static char tag[MAX_HEADER_LEN]; const char * to_hdr; const char * ptr; int tag_i = 0; /* Find start of header */ to_hdr = internal_find_header(msg, "To", "t", true); if (!to_hdr) { WARNING("No valid To: header in reply"); return NULL; } /* Skip past display-name */ /* FIXME */ /* Skip past LA/RA-quoted addr-spec if any */ ptr = internal_hdrchr(to_hdr, '>'); if (!ptr) { /* Maybe an addr-spec without quotes */ ptr = to_hdr; } /* Find tag in this header */ ptr = internal_find_param(ptr, "tag"); if (!ptr) { return NULL; } while (*ptr && *ptr != ' ' && *ptr != ';' && *ptr != '\t' && *ptr != '\r' && *ptr != '\n') { tag[tag_i++] = *(ptr++); } tag[tag_i] = '\0'; return tag; } char* get_header_content(const char* message, const char* name) { return get_header(message, name, true); } /* If content is true, we only return the header's contents. */ char* get_header(const char* message, const char* name, bool content) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; const char *cptr; char *src, *src_copy, *dest, *start, *ptr; bool first_time = true; char header_with_newline[MAX_HEADER_LEN + 1]; const char* compact_header_with_newline; /* returns empty string in case of error */ last_header[0] = '\0'; if (!message || !*message) { return last_header; } /* for safety's sake */ if (!name || !strrchr(name, ':')) { WARNING("Can not search for header (no colon): %s", name ? name : "(null)"); return last_header; } snprintf(header_with_newline, MAX_HEADER_LEN, "\n%s", name); compact_header_with_newline = internal_compact_header_name(name); /* find end of SIP headers - perform search only until that */ cptr = strstr(message, "\r\n\r\n"); if (!cptr) { src_copy = strdup(message); } else if ((src_copy = (char*)malloc(cptr - message + 1))) { src_copy[cptr - message] = '\0'; memcpy(src_copy, message, cptr - message); } if (!src_copy) { ERROR("Out of memory"); return last_header; } src = src_copy; dest = last_header; while ((src = internal_match_header( src, header_with_newline, compact_header_with_newline))) { if (!content && first_time) { // Add the name to the string; dest += sprintf(dest, "%s", name); first_time = false; } if (content || !first_time) { /* Just want the header's content, so skip over the header * and newline */ /* Skip over header */ while (*src != ':') { src++; } src++; /* Skip over leading spaces. */ while (*src == ' ') { src++; } } else { /* Just skip the newline */ src++; } ptr = strchr(src, '\n'); /* Multiline headers always begin with a tab or a space * on the subsequent lines. Skip those lines. */ while (ptr && (*(ptr+1) == ' ' || *(ptr+1) == '\t')) { ptr = strchr(ptr + 1, '\n'); } if (ptr) { *ptr = 0; } // Add ", " when several headers are present if (dest != last_header) { /* Remove trailing whitespaces, tabs, and CRs */ while (dest > last_header && (*(dest-1) == ' ' || *(dest-1) == '\r' || *(dest-1) == '\n' || *(dest-1) == '\t')) { *(--dest) = 0; } if (*(dest-1) == ':') { dest += sprintf(dest, " "); } else { dest += sprintf(dest, ", "); } } dest += sprintf(dest, "%s", src); if (ptr) { *ptr = '\n'; } src++; } /* No header found? */ if (dest == last_header) { free(src_copy); return last_header; } *(dest--) = 0; /* Remove trailing whitespaces, tabs, and CRs */ while (dest > last_header && (*dest == ' ' || *dest == '\r' || *dest == '\t')) { *(dest--) = 0; } /* Remove leading whitespaces */ for (start = last_header; *start == ' '; start++); /* remove enclosed CRs in multilines */ /* don't remove enclosed CRs for multiple headers (e.g. Via) (Rhys) */ while ((ptr = strstr(last_header, "\r\n")) != NULL && (*(ptr + 2) == ' ' || *(ptr + 2) == '\r' || *(ptr + 2) == '\t')) { /* Use strlen(ptr) to include trailing zero */ memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double CR characters */ while ((ptr = strstr(last_header, "\r\r")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double Newline characters */ while ((ptr = strstr(last_header, "\n\n")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } free(src_copy); return start; } char* internal_match_header(char* message, const char* hdr, const char* compact_hdr) { // Attempt to find the header char* header_match = strcasestr(message, hdr); if (!compact_hdr) { // Exit when there is no compact header to compare to return header_match; } char* compact_header_match = strcasestr(message, compact_hdr); // Return the other if one is null if (header_match == nullptr) { return compact_header_match; } if (compact_header_match == nullptr) { return header_match; } // Value exists return the smaller of the two. if (header_match < compact_header_match) { return header_match; } return compact_header_match; } const char* internal_compact_header_name(const char* name) { if (!strcasecmp(name, "call-id:")) { return "\ni:"; } else if (!strcasecmp(name, "contact:")) { return "\nm:"; } else if (!strcasecmp(name, "content-encoding:")) { return "\ne:"; } else if (!strcasecmp(name, "content-length:")) { return "\nl:"; } else if (!strcasecmp(name, "content-type:")) { return "\nc:"; } else if (!strcasecmp(name, "from:")) { return "\nf:"; } else if (!strcasecmp(name, "to:")) { return "\nt:"; } else if (!strcasecmp(name, "via:")) { return "\nv:"; } return NULL; } char* get_first_line(const char* message) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; const char* src; /* returns empty string in case of error */ memset(last_header, 0, sizeof(last_header)); if (!message || !*message) { return last_header; } src = message; int i=0; while (*src) { if (*src == '\n' || *src == '\r') { break; } last_header[i] = *src; i++; src++; } return last_header; } char* get_call_id(const char* msg) { static char call_id[MAX_HEADER_LEN]; const char *content, *end_of_header; unsigned length; call_id[0] = '\0'; content = internal_find_header(msg, "Call-ID", "i", true); if (!content) { WARNING("(1) No valid Call-ID: header in reply '%s'", msg); return call_id; } /* Always returns something */ end_of_header = internal_hdrend(content); length = end_of_header - content; if (length + 1 > MAX_HEADER_LEN) { WARNING("(1) Call-ID: header too long in reply '%s'", msg); return call_id; } memcpy(call_id, content, length); call_id[length] = '\0'; return call_id; } unsigned long int get_cseq_value(const char* msg) { const char* ptr1; // no short form for CSeq: ptr1 = strstr(msg, "\r\nCSeq:"); if (!ptr1) { ptr1 = strstr(msg, "\r\nCSEQ:"); } if (!ptr1) { ptr1 = strstr(msg, "\r\ncseq:"); } if (!ptr1) { ptr1 = strstr(msg, "\r\nCseq:"); } if (!ptr1) { WARNING("No valid Cseq header in request %s", msg); return 0; } ptr1 += 7; while (*ptr1 == ' ' || *ptr1 == '\t') { ++ptr1; } if (!*ptr1) { WARNING("No valid Cseq data in header"); return 0; } return strtoul(ptr1, NULL, 10); } unsigned long get_reply_code(const char* msg) { while (msg && *msg != ' ' && *msg != '\t') ++msg; while (msg && (*msg == ' ' || *msg == '\t')) ++msg; if (msg && strlen(msg) > 0) { return atol(msg); } return 0; } static const char* internal_find_header(const char* msg, const char* name, const char* shortname, bool content) { const char *ptr = msg; int namelen = strlen(name); int shortnamelen = shortname ? strlen(shortname) : 0; while (1) { int is_short = 0; /* RFC3261, 7.3.1: When comparing header fields, field names * are always case-insensitive. Unless otherwise stated in * the definition of a particular header field, field values, * parameter names, and parameter values are case-insensitive. * Tokens are always case-insensitive. Unless specified * otherwise, values expressed as quoted strings are case- * sensitive. * * Ergo, strcasecmp, because: * To:...;tag=bla == TO:...;TAG=BLA * But: * Warning: "something" != Warning: "SoMeThInG" */ if (strncasecmp(ptr, name, namelen) == 0 || (shortname && (is_short = 1) && strncasecmp(ptr, shortname, shortnamelen) == 0)) { const char *tmp = ptr + (is_short ? strlen(shortname) : strlen(name)); while (*tmp == ' ' || *tmp == '\t') { ++tmp; } if (*tmp == ':') { /* Found */ if (content) { /* We just want the content */ ptr = internal_skip_lws(tmp + 1); } break; } } /* Seek to next line, but not past EOH */ ptr = strchr(ptr, '\n'); if (!ptr || ptr[-1] != '\r' || (ptr[1] == '\r' && ptr[2] == '\n')) { if (ptr && ptr[-1] != '\r') { WARNING("Missing CR during header scan at pos %ld", ptr - msg); /* continue? */ } return NULL; } ++ptr; } return ptr; } static const char* internal_hdrchr(const char* ptr, const char needle) { if (*ptr == '\n') { return NULL; /* stray LF */ } while (1) { if (*ptr == '\0') { return NULL; } else if (*ptr == needle) { return ptr; } else if (*ptr == '\n') { if (ptr[-1] == '\r' && ptr[1] != ' ' && ptr[1] != '\t') { return NULL; /* end of header */ } } ++ptr; } return NULL; /* never gets here */ } static const char* internal_hdrend(const char* ptr) { const char *p = ptr; while (*p) { if (p[0] == '\r' && p[1] == '\n' && (p[2] != ' ' && p[2] != '\t')) { return p; } ++p; } return p; } static const char* internal_find_param(const char* ptr, const char* name) { int namelen = strlen(name); while (1) { ptr = internal_hdrchr(ptr, ';'); if (!ptr) { return NULL; } ++ptr; ptr = internal_skip_lws(ptr); if (!ptr || !*ptr) { return NULL; } /* Case insensitive, see RFC 3261 7.3.1 notes above. */ if (strncasecmp(ptr, name, namelen) == 0 && *(ptr + namelen) == '=') { ptr += namelen + 1; return ptr; } } return NULL; /* never gets here */ } static const char* internal_skip_lws(const char* ptr) { while (1) { while (*ptr == ' ' || *ptr == '\t') { ++ptr; } if (ptr[0] == '\r' && ptr[1] == '\n') { if (ptr[2] == ' ' || ptr[2] == '\t') { ptr += 3; continue; } return NULL; /* end of this header */ } return ptr; } return NULL; /* never gets here */ } #ifdef GTEST #include "gtest/gtest.h" TEST(Parser, internal_find_header) { char data[] = "OPTIONS sip:server SIP/2.0\r\n" "Took: abc1\r\n" "To k: abc2\r\n" "To\t :\r\n abc3\r\n" "From: def\r\n" "\r\n"; const char *eq = strstr(data, "To\t :"); EXPECT_STREQ(eq, internal_find_header(data, "To", "t", false)); EXPECT_STREQ(eq + 8, internal_find_header(data, "To", "t", true)); } TEST(Parser, internal_find_header_no_callid_missing_cr_in_to) { char data[4096]; const char *pos; const char *p; /* If you remove the CR ("\r") from any header before the Call-ID, * the Call-ID will not be found. */ strncpy(data, "INVITE sip:3136455552@85.12.1.1:5065 SIP/2.0\r\n\ Via: SIP/2.0/UDP 85.55.55.12:5060;branch=z9hG4bK831a.2bb3de85.0\r\n\ From: \"3136456666\" ;tag=b62e0d72-be14-4d3c-bd6a-b4da593b6b17\r\n\ To: \n\ Contact: \r\n\ Call-ID: DLGCH_K0IEXzVwYzJiQlwKMGRkMX5GSAxiKmJ+exQADWYsZ2QsFQFb\r\n\ CSeq: 6476 INVITE\r\n\ Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER\r\n\ Supported: 100rel, timer, replaces, norefersub\r\n\ Session-Expires: 1800\r\n\ Min-SE: 90\r\n\ Max-Forwards: 70\r\n\ Content-Type: application/sdp\r\n\ Content-Length: 278\r\n\ \r\n\ v=0\r\n\ o=- 592907310 592907310 IN IP4 85.55.55.30\r\n\ s=Centrex v.1.0\r\n\ c=IN IP4 85.55.55.30\r\n\ t=0 0\r\n\ m=audio 41604 RTP/AVP 8 0 101\r\n\ a=rtpmap:8 PCMA/8000\r\n\ a=rtpmap:0 PCMU/8000\r\n\ a=rtpmap:101 telephone-event/8000\r\n\ a=fmtp:101 0-16\r\n\ a=ptime:20\r\n\ a=maxptime:150\r\n\ a=sendrecv\r\n\ a=rtcp:41605\r\n\ ", sizeof(data) - 1); if ((pos = internal_find_header(data, "Call-ID", "i", false)) && (p = strchr(pos, '\r'))) { data[p - data] = '\0'; /* Unexpected.. */ ASSERT_FALSE(1); EXPECT_STREQ(pos, "Call-ID: DLGCH_K0IEXzVwYzJiQlwKMGRkMX5GSAxiKmJ+exQADWYsZ2QsFQFb"); } else { /* Not finding any, because of missing CR. */ ASSERT_TRUE(1); } } TEST(Parser, get_header_mixed_form) { const char* data = "INVITE sip:3136455552@85.12.1.1:5065 SIP/2.0\r\n\ v: SIP/2.0/UDP 85.55.55.12:6090;branch=z9hG4bK831a.2bb3de85.0\r\n\ Via: SIP/2.0/UDP 85.55.55.12:5090;branch=z9hG4bK831a.2bb3de85.0\r\n\ Via:SIP/2.0/UDP 85.55.55.12:5060;branch=z9hG4bK831a.2bb3de87.0\r\n\ v: SIP/2.0/UDP 85.55.55.12:4060;branch=z9hG4bK831a.2bb3de86.0\r\n\ v:SIP/2.0/UDP 85.55.55.12:4050;branch=z9hG4bK831a.2bb3de86.0\r\n\ Record-Route: \r\n\ Record-Route: \r\n\ Record-Route: \r\n\ From: \"3136456666\" ;tag=b62e0d72-be14-4d3c-bd6a-b4da593b6b17\r\n\ To: \r\n\ Contact: \r\n\ Call-ID: DLGCH_K0IEXzVwYzJiQlwKMGRkMX5GSAxiKmJ+exQADWYsZ2QsFQFb\r\n\ CSeq: 6476 INVITE\r\n\ Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER\r\n\ Supported: 100rel, timer, replaces, norefersub\r\n\ Session-Expires: 1800\r\n\ Min-SE: 90\r\n\ Max-Forwards: 70\r\n\ Content-Type: application/sdp\r\n\ Content-Length: 278\r\n\ \r\n\ v=0\r\n\ o=- 592907310 592907310 IN IP4 85.55.55.30\r\n\ s=Centrex v.1.0\r\n\ c=IN IP4 85.55.55.30\r\n\ t=0 0\r\n\ m=audio 41604 RTP/AVP 8 0 101\r\n\ a=rtpmap:8 PCMA/8000\r\n\ a=rtpmap:0 PCMU/8000\r\n\ a=rtpmap:101 telephone-event/8000\r\n\ a=fmtp:101 0-16\r\n\ a=ptime:20\r\n\ a=maxptime:150\r\n\ a=sendrecv\r\n\ a=rtcp:41605\r\n\ "; EXPECT_STREQ("Via: SIP/2.0/UDP 85.55.55.12:6090;branch=z9hG4bK831a.2bb3de85.0, \ SIP/2.0/UDP 85.55.55.12:5090;branch=z9hG4bK831a.2bb3de85.0, \ SIP/2.0/UDP 85.55.55.12:5060;branch=z9hG4bK831a.2bb3de87.0, \ SIP/2.0/UDP 85.55.55.12:4060;branch=z9hG4bK831a.2bb3de86.0, \ SIP/2.0/UDP 85.55.55.12:4050;branch=z9hG4bK831a.2bb3de86.0", get_header(data, "Via:", false)); EXPECT_STREQ("SIP/2.0/UDP 85.55.55.12:6090;branch=z9hG4bK831a.2bb3de85.0, \ SIP/2.0/UDP 85.55.55.12:5090;branch=z9hG4bK831a.2bb3de85.0, \ SIP/2.0/UDP 85.55.55.12:5060;branch=z9hG4bK831a.2bb3de87.0, \ SIP/2.0/UDP 85.55.55.12:4060;branch=z9hG4bK831a.2bb3de86.0, \ SIP/2.0/UDP 85.55.55.12:4050;branch=z9hG4bK831a.2bb3de86.0", get_header(data, "Via:", true)); EXPECT_STREQ("Record-Route: , \ , \ ", get_header(data, "Record-Route:", false)); EXPECT_STREQ(", \ , \ ", get_header(data, "Record-Route:", true)); EXPECT_STREQ("", get_header(data, "Contact:", true)); } TEST(Parser, get_header_last) { const char* data = "INVITE sip:3136455552@85.12.1.1:5065 SIP/2.0\r\n\ From: SIP/2.0/UDP 85.55.55.12:6090;branch=z9hG4bK831a.2bb3de85.0\r\n\ \r\n\ "; EXPECT_STREQ("", get_header(data, "Via:", false)); } TEST(Parser, get_peer_tag__notag) { EXPECT_STREQ(NULL, get_peer_tag("...\r\nTo: \r\n;tag=notag\r\n\r\n")); } TEST(Parser, get_peer_tag__normal) { EXPECT_STREQ("normal", get_peer_tag("...\r\nTo: ;t2=x;tag=normal;t3=y\r\n\r\n")); } TEST(Parser, get_peer_tag__upper) { EXPECT_STREQ("upper", get_peer_tag("...\r\nTo: ;t2=x;TAG=upper;t3=y\r\n\r\n")); } TEST(Parser, get_peer_tag__normal_2) { EXPECT_STREQ("normal2", get_peer_tag("...\r\nTo: abc;tag=normal2\r\n\r\n")); } TEST(Parser, get_peer_tag__folded) { EXPECT_STREQ("folded", get_peer_tag("...\r\nTo: \r\n ;tag=folded\r\n\r\n")); } TEST(Parser, get_peer_tag__space) { EXPECT_STREQ("space", get_peer_tag("...\r\nTo: ;tag=space\r\n\r\n")); } TEST(Parser, get_peer_tag__space_2) { EXPECT_STREQ("space2", get_peer_tag("...\r\nTo \t:\r\n abc\r\n ;tag=space2\r\n\r\n")); } TEST(Parser, get_call_id_1) { EXPECT_STREQ("test1", get_call_id("...\r\nCall-ID: test1\r\n\r\n")); } TEST(Parser, get_call_id_2) { EXPECT_STREQ("test2", get_call_id("...\r\nCALL-ID:\r\n test2\r\n\r\n")); } TEST(Parser, get_call_id_3) { EXPECT_STREQ("test3", get_call_id("...\r\ncall-id:\r\n\t test3\r\n\r\n")); } TEST(Parser, get_call_id_short_1) { EXPECT_STREQ("testshort1", get_call_id("...\r\ni: testshort1\r\n\r\n")); } TEST(Parser, get_call_id_short_2) { /* The WS surrounding the colon belongs with HCOLON, but the * trailing WS does not. */ EXPECT_STREQ("testshort2 \t ", get_call_id("...\r\nI:\r\n \r\n \t testshort2 \t \r\n\r\n")); } TEST(Parser, get_short_header_via) { EXPECT_STREQ("\nv:", internal_compact_header_name("Via:")); EXPECT_STREQ("\nm:", internal_compact_header_name("Contact:")); } /* The 3pcc-A script sends "invalid" SIP that is parsed by this * sip_parser. We must accept headers without any leading request/ * response line: * * * * */ TEST(Parser, get_call_id_github_0101) { // github-#0101 const char *input = "Call-ID: 1-18220@127.0.0.1\r\n" "Content-Type: application/sdp\r\n" "Content-Length: 129\r\n\r\n" "v=0\r\no=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n" "s=-\r\nc=IN IP4 127.0.0.1\r\nt=0 0\r\n" "m=audio 6000 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000"; EXPECT_STREQ("1-18220@127.0.0.1", get_call_id(input)); } #endif //GTEST sipp-3.7.2/src/sipp.cpp0000664000000000000000000025134414525516253011655 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include #include #include #include #include #include #include #ifdef __APPLE__ /* Provide OSX version of extern char **environ; */ #include #define environ (*_NSGetEnviron()) #endif extern char** environ; #define GLOBALS_FULL_DEFINITION #include "sipp.hpp" #include "sip_parser.hpp" #include "socket.hpp" #include "logger.hpp" #include "assert.h" #include "config.h" #include "version.h" extern SIPpSocket *ctrl_socket; extern SIPpSocket *stdin_socket; /* These could be local to main, but for the option processing table. */ static int argiFileName; static std::atomic run_echo_thread(true); /***************** Option Handling Table *****************/ struct sipp_option { const char *option; const char *help; int type; void *data; /* Pass 0: Help and other options that should exit immediately. */ /* Pass 1: All other options. */ /* Pass 2: Scenario parsing. */ int pass; }; #define SIPP_OPTION_HELP 1 #define SIPP_OPTION_INT 2 #define SIPP_OPTION_SETFLAG 3 #define SIPP_OPTION_UNSETFLAG 4 #define SIPP_OPTION_STRING 5 #define SIPP_OPTION_ARGI 6 #define SIPP_OPTION_TIME_SEC 7 #define SIPP_OPTION_FLOAT 8 #define SIPP_OPTION_BOOL 10 #define SIPP_OPTION_VERSION 11 #define SIPP_OPTION_TRANSPORT 12 #define SIPP_OPTION_NEED_SSL 13 #define SIPP_OPTION_IP 14 #define SIPP_OPTION_MAX_SOCKET 15 #define SIPP_OPTION_CSEQ 16 #define SIPP_OPTION_SCENARIO 17 #define SIPP_OPTION_RSA 18 #define SIPP_OPTION_LIMIT 19 #define SIPP_OPTION_USERS 20 #define SIPP_OPTION_KEY 21 #define SIPP_OPTION_3PCC 22 #define SIPP_OPTION_TDMMAP 23 #define SIPP_OPTION_TIME_MS 24 #define SIPP_OPTION_SLAVE_CFG 25 #define SIPP_OPTION_3PCC_EXTENDED 26 #define SIPP_OPTION_INPUT_FILE 27 #define SIPP_OPTION_TIME_MS_LONG 28 #define SIPP_OPTION_LONG 29 #define SIPP_OPTION_LONG_LONG 30 #define SIPP_OPTION_DEFAULTS 31 #define SIPP_OPTION_OOC_SCENARIO 32 #define SIPP_OPTION_INDEX_FILE 33 #define SIPP_OPTION_VAR 34 #define SIPP_OPTION_RTCHECK 35 #define SIPP_OPTION_LFNAME 36 #define SIPP_OPTION_LFOVERWRITE 37 #define SIPP_OPTION_PLUGIN 38 #define SIPP_OPTION_NEED_SCTP 39 #define SIPP_HELP_TEXT_HEADER 255 /* Put each option, its help text, and type in this table. */ struct sipp_option options_table[] = { {"h", NULL, SIPP_OPTION_HELP, NULL, 0}, {"help", NULL, SIPP_OPTION_HELP, NULL, 0}, {"", "Scenario file options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"sd", "Dumps a default scenario (embedded in the SIPp executable)", SIPP_OPTION_SCENARIO, NULL, 0}, {"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.", SIPP_OPTION_SCENARIO, NULL, 2}, {"oocsf", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, {"oocsn", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, { "sn", "Use a default scenario (embedded in the SIPp executable). If this option is omitted, the Standard SipStone UAC scenario is loaded.\n" "Available values in this version:\n\n" "- 'uac' : Standard SipStone UAC (default).\n" "- 'uas' : Simple UAS responder.\n" "- 'regexp' : Standard SipStone UAC - with regexp and variables.\n" "- 'branchc' : Branching and conditional branching in scenarios - client.\n" "- 'branchs' : Branching and conditional branching in scenarios - server.\n\n" "Default 3pcc scenarios (see -3pcc option):\n\n" "- '3pcc-C-A' : Controller A side (must be started after all other 3pcc scenarios)\n" "- '3pcc-C-B' : Controller B side.\n" "- '3pcc-A' : A side.\n" "- '3pcc-B' : B side.\n", SIPP_OPTION_SCENARIO, NULL, 2 }, {"", "IP, port and protocol options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, { "t", "Set the transport mode:\n" "- u1: UDP with one socket (default),\n" "- un: UDP with one socket per call,\n" "- ui: UDP with one socket per IP address. The IP addresses must be defined in the injection file.\n" "- t1: TCP with one socket,\n" "- tn: TCP with one socket per call,\n" #ifdef USE_TLS "- l1: TLS with one socket,\n" "- ln: TLS with one socket per call,\n" #endif #ifdef USE_SCTP "- s1: SCTP with one socket,\n" "- sn: SCTP with one socket per call,\n" #endif "- c1: u1 + compression (only if compression plugin loaded),\n" "- cn: un + compression (only if compression plugin loaded). This plugin is not provided with SIPp.\n" , SIPP_OPTION_TRANSPORT, NULL, 1 }, {"i", "Set the local IP address for 'Contact:','Via:', and 'From:' headers. Default is primary host IP address.\n", SIPP_OPTION_IP, local_ip, 1}, {"p", "Set the local port number. Default is a random free port chosen by the system.", SIPP_OPTION_INT, &user_port, 1}, {"bind_local", "Bind socket to local IP address, i.e. the local IP address is used as the source IP address. If SIPp runs in server mode it will only listen on the local IP address instead of all IP addresses.", SIPP_OPTION_SETFLAG, &bind_local, 1}, {"ci", "Set the local control IP address", SIPP_OPTION_IP, control_ip, 1}, {"cp", "Set the local control port number. Default is 8888.", SIPP_OPTION_INT, &control_port, 1}, {"max_socket", "Set the max number of sockets to open simultaneously. This option is significant if you use one socket per call. Once this limit is reached, traffic is distributed over the sockets already opened. Default value is 50000", SIPP_OPTION_MAX_SOCKET, NULL, 1}, {"max_reconnect", "Set the the maximum number of reconnection.", SIPP_OPTION_INT, &reset_number, 1}, {"reconnect_close", "Should calls be closed on reconnect?", SIPP_OPTION_BOOL, &reset_close, 1}, {"reconnect_sleep", "How long (in milliseconds) to sleep between the close and reconnect?", SIPP_OPTION_TIME_MS, &reset_sleep, 1}, {"rsa", "Set the remote sending address to host:port for sending the messages.", SIPP_OPTION_RSA, NULL, 1}, #ifdef USE_TLS {"tls_cert", "Set the name for TLS Certificate file. Default is 'cacert.pem'", SIPP_OPTION_STRING, &tls_cert_name, 1}, {"tls_key", "Set the name for TLS Private Key file. Default is 'cakey.pem'", SIPP_OPTION_STRING, &tls_key_name, 1}, {"tls_ca", "Set the name for TLS CA file. If not specified, X509 verification is not activated.", SIPP_OPTION_STRING, &tls_ca_name, 1}, {"tls_crl", "Set the name for Certificate Revocation List file. If not specified, X509 CRL is not activated.", SIPP_OPTION_STRING, &tls_crl_name, 1}, {"tls_version", "Set the TLS protocol version to use (1.0, 1.1, 1.2) -- default is autonegotiate", SIPP_OPTION_FLOAT, &tls_version, 1}, #else {"tls_cert", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_key", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_ca", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_crl", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_version", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, #endif #ifdef USE_SCTP {"multihome", "Set multihome address for SCTP", SIPP_OPTION_IP, multihome_ip, 1}, {"heartbeat", "Set heartbeat interval in ms for SCTP", SIPP_OPTION_INT, &heartbeat, 1}, {"assocmaxret", "Set association max retransmit counter for SCTP", SIPP_OPTION_INT, &assocmaxret, 1}, {"pathmaxret", "Set path max retransmit counter for SCTP", SIPP_OPTION_INT, &pathmaxret, 1}, {"pmtu", "Set path MTU for SCTP", SIPP_OPTION_INT, &pmtu, 1}, {"gracefulclose", "If true, SCTP association will be closed with SHUTDOWN (default).\n If false, SCTP association will be closed by ABORT.\n", SIPP_OPTION_BOOL, &gracefulclose, 1}, #else {"multihome", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"heartbeat", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"assocmaxret", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"pathmaxret", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"pmtu", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"gracefulclose", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, #endif {"", "SIPp overall behavior options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"v", "Display version and copyright information.", SIPP_OPTION_VERSION, NULL, 0}, {"bg", "Launch SIPp in background mode.", SIPP_OPTION_SETFLAG, &backgroundMode, 1}, {"nostdin", "Disable stdin.\n", SIPP_OPTION_SETFLAG, &nostdin, 1}, {"plugin", "Load a plugin.", SIPP_OPTION_PLUGIN, NULL, 1}, {"sleep", "How long to sleep for at startup. Default unit is seconds.", SIPP_OPTION_TIME_SEC, &sleeptime, 1}, {"skip_rlimit", "Do not perform rlimit tuning of file descriptor limits. Default: false.", SIPP_OPTION_SETFLAG, &skip_rlimit, 1}, {"buff_size", "Set the send and receive buffer size.", SIPP_OPTION_INT, &buff_size, 1}, {"sendbuffer_warn", "Produce warnings instead of errors on SendBuffer failures.", SIPP_OPTION_BOOL, &sendbuffer_warn, 1}, {"lost", "Set the number of packets to lose by default (scenario specifications override this value).", SIPP_OPTION_FLOAT, &global_lost, 1}, {"key", "keyword value\nSet the generic parameter named \"keyword\" to \"value\".", SIPP_OPTION_KEY, NULL, 1}, {"set", "variable value\nSet the global variable parameter named \"variable\" to \"value\".", SIPP_OPTION_VAR, NULL, 3}, {"tdmmap", "Generate and handle a table of TDM circuits.\n" "A circuit must be available for the call to be placed.\n" "Format: -tdmmap {0-3}{99}{5-8}{1-31}", SIPP_OPTION_TDMMAP, NULL, 1}, {"dynamicStart", "variable value\nSet the start offset of dynamic_id variable", SIPP_OPTION_INT, &startDynamicId, 1}, {"dynamicMax", "variable value\nSet the maximum of dynamic_id variable ", SIPP_OPTION_INT, &maxDynamicId, 1}, {"dynamicStep", "variable value\nSet the increment of dynamic_id variable", SIPP_OPTION_INT, &stepDynamicId, 1}, {"", "Call behavior options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"aa", "Enable automatic 200 OK answer for INFO, NOTIFY, OPTIONS and UPDATE.", SIPP_OPTION_SETFLAG, &auto_answer, 1}, {"base_cseq", "Start value of [cseq] for each call.", SIPP_OPTION_CSEQ, NULL, 1}, {"cid_str", "Call ID string (default %u-%p@%s). %u=call_number, %s=ip_address, %p=process_number, %%=% (in any order).", SIPP_OPTION_STRING, &call_id_string, 1}, {"d", "Controls the length of calls. More precisely, this controls the duration of 'pause' instructions in the scenario, if they do not have a 'milliseconds' section. Default value is 0 and default unit is milliseconds.", SIPP_OPTION_TIME_MS, &duration, 1}, {"deadcall_wait", "How long the Call-ID and final status of calls should be kept to improve message and error logs (default unit is ms).", SIPP_OPTION_TIME_MS, &deadcall_wait, 1}, {"auth_uri", "Force the value of the URI for authentication.\n" "By default, the URI is composed of remote_ip:remote_port.", SIPP_OPTION_STRING, &auth_uri, 1}, {"au", "Set authorization username for authentication challenges. Default is taken from -s argument", SIPP_OPTION_STRING, &auth_username, 1}, {"ap", "Set the password for authentication challenges. Default is 'password'", SIPP_OPTION_STRING, &auth_password, 1}, {"s", "Set the username part of the request URI. Default is 'service'.", SIPP_OPTION_STRING, &service, 1}, {"default_behaviors", "Set the default behaviors that SIPp will use. Possible values are:\n" "- all\tUse all default behaviors\n" "- none\tUse no default behaviors\n" "- bye\tSend byes for aborted calls\n" "- abortunexp\tAbort calls on unexpected messages\n" "- pingreply\tReply to ping requests\n" "- cseq\tCheck CSeq of ACKs\n" "If a behavior is prefaced with a -, then it is turned off. Example: all,-bye\n", SIPP_OPTION_DEFAULTS, &default_behaviors, 1}, {"nd", "No Default. Disable all default behavior of SIPp which are the following:\n" "- On UDP retransmission timeout, abort the call by sending a BYE or a CANCEL\n" "- On receive timeout with no ontimeout attribute, abort the call by sending a BYE or a CANCEL\n" "- On unexpected BYE send a 200 OK and close the call\n" "- On unexpected CANCEL send a 200 OK and close the call\n" "- On unexpected PING send a 200 OK and continue the call\n" "- On unexpected ACK CSeq do nothing\n" "- On any other unexpected message, abort the call by sending a BYE or a CANCEL\n", SIPP_OPTION_UNSETFLAG, &default_behaviors, 1}, {"pause_msg_ign", "Ignore the messages received during a pause defined in the scenario ", SIPP_OPTION_SETFLAG, &pause_msg_ign, 1}, {"callid_slash_ign", "Don't treat a triple-slash in Call-IDs as indicating an extra SIPp prefix.", SIPP_OPTION_SETFLAG, &callidSlash, 1}, {"", "Injection file options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"inf", "Inject values from an external CSV file during calls into the scenarios.\n" "First line of this file say whether the data is to be read in sequence (SEQUENTIAL), random (RANDOM), or user (USER) order.\n" "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 ...)", SIPP_OPTION_INPUT_FILE, NULL, 1}, {"infindex", "file field\nCreate an index of file using field. For example -inf ../path/to/users.csv -infindex users.csv 0 creates an index on the first key.", SIPP_OPTION_INDEX_FILE, NULL, 1 }, {"ip_field", "Set which field from the injection file contains the IP address from which the client will send its messages.\n" "If this option is omitted and the '-t ui' option is present, then field 0 is assumed.\n" "Use this option together with '-t ui'", SIPP_OPTION_INT, &peripfield, 1}, {"", "RTP behaviour options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"mi", "Set the local media IP address (default: local primary host IP address)", SIPP_OPTION_IP, media_ip, 1}, {"rtp_echo", "Enable RTP echo. RTP/UDP packets received on media port are echoed to their sender.\n" "RTP/UDP packets coming on this port + 2 are also echoed to their sender (used for sound and video echo).", SIPP_OPTION_SETFLAG, &rtp_echo_enabled, 1}, {"mb", "Set the RTP echo buffer size (default: 2048).", SIPP_OPTION_INT, &media_bufsize, 1}, {"min_rtp_port", "Minimum port number for RTP socket range.", SIPP_OPTION_INT, &min_rtp_port, 1}, {"max_rtp_port", "Maximum port number for RTP socket range.", SIPP_OPTION_INT, &max_rtp_port, 1}, {"rtp_payload", "RTP default payload type.", SIPP_OPTION_INT, &rtp_default_payload, 1}, {"rtp_threadtasks", "RTP number of playback tasks per thread.", SIPP_OPTION_INT, &rtp_tasks_per_thread, 1}, {"rtp_buffsize", "Set the rtp socket send/receive buffer size.", SIPP_OPTION_INT, &rtp_buffsize, 1}, {"rtpcheck_debug", "Write RTP check debug information to file", SIPP_OPTION_SETFLAG, &rtpcheck_debug, 1}, #ifdef USE_TLS {"srtpcheck_debug", "Write SRTP check debug information to file", SIPP_OPTION_SETFLAG, &srtpcheck_debug, 1}, #endif // USE_TLS {"audiotolerance", "Audio error tolerance for RTP checks (0.0-1.0) -- default: 1.0", SIPP_OPTION_FLOAT, &audiotolerance, 1}, {"videotolerance", "Video error tolerance for RTP checks (0.0-1.0) -- default: 1.0", SIPP_OPTION_FLOAT, &videotolerance, 1}, {"", "Call rate options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"r", "Set the call rate (in calls per seconds). This value can be" "changed during test by pressing '+', '_', '*' or '/'. Default is 10.\n" "pressing '+' key to increase call rate by 1 * rate_scale,\n" "pressing '-' key to decrease call rate by 1 * rate_scale,\n" "pressing '*' key to increase call rate by 10 * rate_scale,\n" "pressing '/' key to decrease call rate by 10 * rate_scale.\n", SIPP_OPTION_FLOAT, &rate, 1}, {"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).\n" "Example: -r 7 -rp 2000 ==> 7 calls every 2 seconds.\n -r 10 -rp 5s => 10 calls every 5 seconds.", SIPP_OPTION_TIME_MS, &rate_period_ms, 1}, {"rate_scale", "Control the units for the '+', '-', '*', and '/' keys.", SIPP_OPTION_FLOAT, &rate_scale, 1}, {"rate_increase", "Specify the rate increase every -rate_interval units (default is seconds). This allows you to increase the load for each independent logging period.\n" "Example: -rate_increase 10 -rate_interval 10s\n" " ==> increase calls by 10 every 10 seconds.", SIPP_OPTION_INT, &rate_increase, 1}, {"rate_max", "If -rate_increase is set, then quit after the rate reaches this value.\n" "Example: -rate_increase 10 -rate_max 100\n" " ==> increase calls by 10 until 100 cps is hit.", SIPP_OPTION_INT, &rate_max, 1}, {"rate_interval", "Set the interval by which the call rate is increased. Defaults to the value of -fd.", SIPP_OPTION_TIME_SEC, &rate_increase_freq, 1}, {"no_rate_quit", "If -rate_increase is set, do not quit after the rate reaches -rate_max.", SIPP_OPTION_UNSETFLAG, &rate_quit, 1}, {"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:\n" " (3 * call_duration (s) * rate).", SIPP_OPTION_LIMIT, NULL, 1}, {"m", "Stop the test and exit when 'calls' calls are processed", SIPP_OPTION_LONG, &stop_after, 1}, {"users", "Instead of starting calls at a fixed rate, begin 'users' calls at startup, and keep the number of calls constant.", SIPP_OPTION_USERS, NULL, 1}, {"", "Retransmission and timeout options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"recv_timeout", "Global receive timeout. Default unit is milliseconds. If the expected message is not received, the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_recv_timeout, 1}, {"send_timeout", "Global send timeout. Default unit is milliseconds. If a message is not sent (due to congestion), the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_send_timeout, 1}, {"timeout", "Global timeout. Default unit is seconds. If this option is set, SIPp quits after nb units (-timeout 20s quits after 20 seconds).", SIPP_OPTION_TIME_SEC, &global_timeout, 1}, {"timeout_error", "SIPp fails if the global timeout is reached is set (-timeout option required).", SIPP_OPTION_SETFLAG, &timeout_error, 1}, {"max_retrans", "Maximum number of UDP retransmissions before call ends on timeout. Default is 5 for INVITE transactions and 7 for others.", SIPP_OPTION_INT, &max_udp_retrans, 1}, {"max_invite_retrans", "Maximum number of UDP retransmissions for invite transactions before call ends on timeout.", SIPP_OPTION_INT, &max_invite_retrans, 1}, {"max_non_invite_retrans", "Maximum number of UDP retransmissions for non-invite transactions before call ends on timeout.", SIPP_OPTION_INT, &max_non_invite_retrans, 1}, {"nr", "Disable retransmission in UDP mode.", SIPP_OPTION_UNSETFLAG, &retrans_enabled, 1}, {"rtcheck", "Select the retransmission detection method: full (default) or loose.", SIPP_OPTION_RTCHECK, &rtcheck, 1}, {"T2", "Global T2-timer in milli seconds", SIPP_OPTION_TIME_MS, &global_t2, 1}, {"", "Third-party call control options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"3pcc", "Launch the tool in 3pcc mode (\"Third Party call control\"). The passed IP address depends on the 3PCC role.\n" "- When the first twin command is 'sendCmd' then this is the address of the remote twin socket. SIPp will try to connect to this address:port to send the twin command (This instance must be started after all other 3PCC scenarios).\n" " Example: 3PCC-C-A scenario.\n" "- When the first twin command is 'recvCmd' then this is the address of the local twin socket. SIPp will open this address:port to listen for twin command.\n" " Example: 3PCC-C-B scenario.", SIPP_OPTION_3PCC, NULL, 1}, {"master","3pcc extended mode: indicates the master number", SIPP_OPTION_3PCC_EXTENDED, &master_name, 1}, {"slave", "3pcc extended mode: indicates the slave number", SIPP_OPTION_3PCC_EXTENDED, &slave_number, 1}, {"slave_cfg", "3pcc extended mode: indicates the file where the master and slave addresses are stored", SIPP_OPTION_SLAVE_CFG, NULL, 1}, {"", "Performance and watchdog options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"timer_resol", "Set the timer resolution. Default unit is milliseconds. This option has an impact on timers precision." "Small values allow more precise scheduling but impacts CPU usage." "If the compression is on, the value is set to 50ms. The default value is 10ms.", SIPP_OPTION_TIME_MS, &timer_resolution, 1}, {"max_recv_loops", "Set the maximum number of messages received read per cycle. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_recv_loops, 1}, {"max_sched_loops", "Set the maximum number of calls run per event loop. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_sched_loops, 1}, {"watchdog_interval", "Set gap between watchdog timer firings. Default is 400.", SIPP_OPTION_TIME_MS, &watchdog_interval, 1}, {"watchdog_reset", "If the watchdog timer has not fired in more than this time period, then reset the max triggers counters. Default is 10 minutes.", SIPP_OPTION_TIME_MS, &watchdog_reset, 1}, {"watchdog_minor_threshold", "If it has been longer than this period between watchdog executions count a minor trip. Default is 500.", SIPP_OPTION_TIME_MS, &watchdog_minor_threshold, 1}, {"watchdog_major_threshold", "If it has been longer than this period between watchdog executions count a major trip. Default is 3000.", SIPP_OPTION_TIME_MS, &watchdog_major_threshold, 1}, {"watchdog_major_maxtriggers", "How many times the major watchdog timer can be tripped before the test is terminated. Default is 10.", SIPP_OPTION_INT, &watchdog_major_maxtriggers, 1}, {"watchdog_minor_maxtriggers", "How many times the minor watchdog timer can be tripped before the test is terminated. Default is 120.", SIPP_OPTION_INT, &watchdog_minor_maxtriggers, 1}, {"", "Tracing, logging and statistics options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"f", "Set the statistics report frequency on screen. Default is 1 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq, 1}, {"trace_stat", "Dumps all statistics in _.csv file. Use the '-h stat' option for a detailed description of the statistics file content.", SIPP_OPTION_SETFLAG, &dumpInFile, 1}, {"stat_delimiter", "Set the delimiter for the statistics file", SIPP_OPTION_STRING, &stat_delimiter, 1}, {"stf", "Set the file name to use to dump statistics", SIPP_OPTION_ARGI, &argiFileName, 1}, {"fd", "Set the statistics dump log report frequency. Default is 60 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq_dumpLog, 1}, {"periodic_rtd", "Reset response time partition counters each logging interval.", SIPP_OPTION_SETFLAG, &periodic_rtd, 1}, {"trace_msg", "Displays sent and received SIP messages in __messages.log", SIPP_OPTION_SETFLAG, &useMessagef, 1}, {"message_file", "Set the name of the message log file.", SIPP_OPTION_LFNAME, &message_lfi, 1}, {"message_overwrite", "Overwrite the message log file (default true).", SIPP_OPTION_LFOVERWRITE, &message_lfi, 1}, {"trace_shortmsg", "Displays sent and received SIP messages as CSV in __shortmessages.log", SIPP_OPTION_SETFLAG, &useShortMessagef, 1}, {"shortmessage_file", "Set the name of the short message log file.", SIPP_OPTION_LFNAME, &shortmessage_lfi, 1}, {"shortmessage_overwrite", "Overwrite the short message log file (default true).", SIPP_OPTION_LFOVERWRITE, &shortmessage_lfi, 1}, {"trace_counts", "Dumps individual message counts in a CSV file.", SIPP_OPTION_SETFLAG, &useCountf, 1}, {"trace_err", "Trace all unexpected messages in __errors.log.", SIPP_OPTION_SETFLAG, &print_all_responses, 1}, {"error_file", "Set the name of the error log file.", SIPP_OPTION_LFNAME, &error_lfi, 1}, {"error_overwrite", "Overwrite the error log file (default true).", SIPP_OPTION_LFOVERWRITE, &error_lfi, 1}, {"trace_error_codes", "Dumps the SIP response codes of unexpected messages to __error_codes.log.", SIPP_OPTION_SETFLAG, &useErrorCodesf, 1}, // {"trace_timeout", "Displays call ids for calls with timeouts in __timeout.log", SIPP_OPTION_SETFLAG, &useTimeoutf, 1}, {"trace_calldebug", "Dumps debugging information about aborted calls to __calldebug.log file.", SIPP_OPTION_SETFLAG, &useCallDebugf, 1}, {"calldebug_file", "Set the name of the call debug file.", SIPP_OPTION_LFNAME, &calldebug_lfi, 1}, {"calldebug_overwrite", "Overwrite the call debug file (default true).", SIPP_OPTION_LFOVERWRITE, &calldebug_lfi, 1}, {"trace_screen", "Dump statistic screens in the __screens.log file when quitting SIPp. Useful to get a final status report in background mode (-bg option).", SIPP_OPTION_SETFLAG, &useScreenf, 1}, {"screen_file", "Set the name of the screen file.", SIPP_OPTION_LFNAME, &screen_lfi, 1}, {"screen_overwrite", "Overwrite the screen file (default true).", SIPP_OPTION_LFOVERWRITE, &screen_lfi, 1}, {"trace_rtt", "Allow tracing of all response times in __rtt.csv.", SIPP_OPTION_SETFLAG, &dumpInRtt, 1}, {"rtt_freq", "freq is mandatory. Dump response times every freq calls in the log file defined by -trace_rtt. Default value is 200.", SIPP_OPTION_LONG, &report_freq_dumpRtt, 1}, {"trace_logs", "Allow tracing of actions in __logs.log.", SIPP_OPTION_SETFLAG, &useLogf, 1}, {"log_file", "Set the name of the log actions log file.", SIPP_OPTION_LFNAME, &log_lfi, 1}, {"log_overwrite", "Overwrite the log actions log file (default true).", SIPP_OPTION_LFOVERWRITE, &log_lfi, 1}, {"ringbuffer_files", "How many error, message, shortmessage and calldebug files should be kept after rotation?", SIPP_OPTION_INT, &ringbuffer_files, 1}, {"ringbuffer_size", "How large should error, message, shortmessage and calldebug files be before they get rotated?", SIPP_OPTION_LONG_LONG, &ringbuffer_size, 1}, {"max_log_size", "What is the limit for error, message, shortmessage and calldebug file sizes.", SIPP_OPTION_LONG_LONG, &max_log_size, 1}, }; static struct sipp_option *find_option(const char* option) { int i; int max = sizeof(options_table)/sizeof(options_table[0]); /* Allow options to start with '-' or '--' */ if (option[0] != '-') { return NULL; } option++; if (option[0] == '-') { option++; } for (i = 0; i < max; i++) { if (!strcmp(options_table[i].option, option)) { return &(options_table[i]); } } return NULL; } /******************** Recv Poll Processing *********************/ extern unsigned pollnfds; #ifdef HAVE_EPOLL extern int epollfd; extern struct epoll_event* epollevents; #endif extern SIPpSocket *sockets[SIPP_MAXFDS]; /************** Statistics display & User control *************/ static void sipp_sigusr1(int /* not used */) { /* Smooth exit: do not place any new calls and exit */ quitting+=10; } static void sipp_sigusr2(int /* not used */) { if (!signalDump) { signalDump = true; } } void timeout_alarm(int /*param*/) { if (timeout_error) { ERROR("%s timed out after '%.3lf' seconds", scenario_file, ((double)clock_tick / 1000LL)); } quitting = 1; timeout_exit = true; } /* Send loop & trafic generation*/ static void traffic_thread(int &rtp_errors, int &echo_errors) { /* create the file */ char L_file_name[MAX_PATH]; sprintf(L_file_name, "%s_%ld_screen.log", scenario_file, (long) getpid()); update_clock_tick(); /* Arm the global timer if needed */ if (global_timeout > 0) { signal(SIGALRM, timeout_alarm); alarm(global_timeout / 1000); } // Dump (to create file on disk) and showing screen at the beginning even if // the report period is not reached stattask::report(); screentask::report(false); while (1) { scheduling_loops++; update_clock_tick(); if (signalDump) { /* Screen dumping in a file */ if (useScreenf == 1) { print_screens(); } else { /* If the -trace_screen option has not been set, */ /* create the file at this occasion */ rotate_screenf(); print_screens(); } if (dumpInRtt) { main_scenario->stats->dumpDataRtt(); } signalDump = false; } while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { (*(sockets_pending_reset.begin()))->reset_connection(); sockets_pending_reset.erase(sockets_pending_reset.begin()); } if ((main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated)) >= stop_after) { quitting = 1; } if (quitting) { if (quitting > 11) { /* Force exit: abort all calls */ abort_all_tasks(); } /* Quitting and no more opened calls, close all */ if (!main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)) { /* We can have calls that do not count towards our open-call count (e.g., dead calls). */ abort_all_tasks(); rtp_errors = rtpstream_shutdown(main_scenario->fetchRtpTaskThreadIDs()); echo_errors = main_scenario->stats->getRtpEchoErrors(); /* Reverse order shutdown, because deleting reorders the * sockets list. */ for (int i = pollnfds - 1; i >= 0; --i) { sockets[i]->close(); if (sockets[i] == ctrl_socket) { ctrl_socket = NULL; } else if (sockets[i] == stdin_socket) { stdin_socket = NULL; } } screentask::report(true); stattask::report(); if (useScreenf == 1) { print_screens(); } return; } } update_clock_tick(); /* Schedule all pending calls and process their timers */ task_list *running_tasks; if ((clock_tick - last_timer_cycle) > timer_resolution) { /* Just for the count. */ running_tasks = get_running_tasks(); last_running_calls = running_tasks->size(); /* If we have expired paused calls, move them to the run queue. */ last_woken_calls += expire_paused_tasks(); last_paused_calls = paused_tasks_count(); last_timer_cycle = clock_tick; } /* We should never get so busy with running calls that we can't process some messages. */ int loops = max_sched_loops; /* Now we process calls that are on the run queue. */ running_tasks = get_running_tasks(); /* Workaround hpux problem with iterators. Deleting the * current object when iterating breaks the iterator and * leads to iterate again on the destroyed (deleted) * object. Thus, we have to wait ont step befere actual * deletion of the object*/ task * last = NULL; task_list::iterator iter; for (iter = running_tasks->begin(); iter != running_tasks->end(); iter++) { if (last) { last->run(); if (sockets_pending_reset.begin() != sockets_pending_reset.end()) { last = NULL; break; } } last = *iter; if (--loops <= 0) { break; } } if (last) { last->run(); } while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { (*(sockets_pending_reset.begin()))->reset_connection(); sockets_pending_reset.erase(sockets_pending_reset.begin()); } update_clock_tick(); /* Receive incoming messages */ SIPpSocket::pollset_process(running_tasks->empty()); } assert(0); } /*************** RTP ECHO THREAD ***********************/ /* param is a pointer to RTP socket */ static void rtp_echo_thread(void* param) { std::vector msg; msg.resize(media_bufsize); ssize_t nr, ns; sipp_socklen_t len; struct sockaddr_storage remote_rtp_addr; int sock = *(int *)param; int rc; sigset_t mask; sigfillset(&mask); /* Mask all allowed signals */ rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); if (rc) { WARNING("pthread_sigmask returned %d", rc); return; } // timeout after 100ms, to enable graceful termination of the thread struct timeval tv = {0, 100000}; if ((setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) || (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0)) { WARNING("Cannot set socket timeout. error: %d", errno); } while (run_echo_thread.load(std::memory_order_relaxed)) { len = sizeof(remote_rtp_addr); nr = recvfrom(sock, msg.data(), media_bufsize, 0, (sockaddr*)&remote_rtp_addr, &len); if (nr < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; WARNING("%s %i", "Error on RTP echo reception - stopping echo - errno=", errno); return; } if (!rtp_echo_state) { continue; } ns = sendto(sock, msg.data(), nr, 0, (sockaddr*)&remote_rtp_addr, len); if (ns != nr) { WARNING("%s %i", "Error on RTP echo transmission - stopping echo - errno=", errno); return; } if (*(int*)param == media_socket_audio) { rtp_pckts++; rtp_bytes += ns; } else { /* packets on the second RTP stream */ rtp2_pckts++; rtp2_bytes += ns; } } } /* Wrap the help text. */ static char* wrap(const char* in, int offset, int size) { int pos = 0; int i, j; int l = strlen(in); int alloced = l + 1; char* out = (char*)malloc(alloced); int indent = 0; if (!out) { ERROR_NO("malloc"); } for (i = j = 0; i < l; i++) { out[j++] = in[i]; if (in[i] == '\n') { out = (char*)realloc(out, alloced += offset); if (!out) { ERROR_NO("realloc"); } pos = 0; for (int k = 0; k < offset; k++) { out[j++] = ' '; } if (indent) { indent = 0; } } if (in[i] == '-' && i > 0 && in[i - 1] == '\n') { indent = 1; } if (++pos > size) { int k; for (k = j - 1; k > 0 && !isspace(out[k]); k--); int useoffset = offset; if (indent) { useoffset += 2; } if (k == 0 || out[k] == '\n') { pos = 0; out[j++] = '\n'; out = (char*)realloc(out, alloced += useoffset); if (!out) { ERROR_NO("realloc"); } for (k = 0; k < useoffset; k++) { out[j++] = ' '; } } else { int m; int move_back = 0; out[k] = '\n'; pos = j - k; // move_back is used to step back in the in and out buffers when a // word is longer than useoffset. if (i > (k + useoffset)) { move_back = i - (k + useoffset); i -= move_back; } k++; out = (char*)realloc(out, alloced += useoffset); if (!out) { ERROR_NO("realloc"); } for (m = 0; m < useoffset; m++) { if (k + useoffset + m < alloced) { out[k + useoffset + m] = out[k + m]; } out[k + m] = ' '; } j += useoffset - move_back; } } } out[j] = '\0'; return out; } /* If stdout is a TTY, wrap stdout in a call to PAGER (generally less(1)). * Returns a pid_t you'll have to pass to end_pager(). */ static pid_t begin_pager() { char pager[15] = "/usr/bin/pager"; char *argv[2] = {NULL, NULL}; int stdout_fd = fileno(stdout); int read_write[2]; pid_t ret; if (!isatty(stdout_fd)) { return 0; } /* Get pager first, so we can bail if it's not there */ argv[0] = getenv("PAGER"); if (!argv[0]) { argv[0] = pager; /* missing PAGER */ } else if (!*argv[0]) { return 0; /* blank PAGER */ } /* Should use euidaccess(3), but requires _GNU_SOURCE */ if (access(argv[0], X_OK) < 0) { perror(argv[0]); return 0; } /* Set up pipes and fork */ if (pipe(&read_write[0]) < 0) { perror("pipe"); return 0; } if ((ret = fork()) < 0) { perror("fork"); return 0; } /* Switch stdout FD in parent */ if (ret != 0) { fflush(stdout); close(stdout_fd); close(read_write[0]); if (dup2(read_write[1], stdout_fd) < 0) { perror("dup2"); } else { close(read_write[1]); } return ret; } /* Switch stdin FD and start pager in child */ if (setenv("LESS", "FRX", 1) < 0) { perror("setenv"); } close(STDIN_FILENO); close(read_write[1]); if (dup2(read_write[0], STDIN_FILENO) < 0) { perror("dup2"); } else { close(read_write[0]); } execve(argv[0], argv, environ); /* This was not supposed to happen. Missing binary? */ perror("execve"); return 0; } /* Make sure we flush and close, or the child won't get all the data (and know * when we're done). Wait for the child to exit first. */ void end_pager(pid_t pager) { fflush(stdout); fclose(stdout); while (pager != 0) { int wstatus; if (waitpid(pager, &wstatus, 0) == pager) { pager = 0; } } } /* Help screen */ static void help() { int i, max; pid_t pager = begin_pager(); printf ("\n" "Usage:\n" "\n" " sipp remote_host[:remote_port] [options]\n" "\n" "Example:\n" "\n" " Run SIPp with embedded server (uas) scenario:\n" " ./sipp -sn uas\n" " On the same host, run SIPp with embedded client (uac) scenario:\n" " ./sipp -sn uac 127.0.0.1\n" "\n" " Available options:\n" "\n"); /* We automatically generate the help messages based on the options array. * This should hopefully encourage people to write help text when they * introduce a new option and keep the code a bit cleaner. */ max = sizeof(options_table) / sizeof(options_table[0]); for (i = 0; i < max; i++) { char *formatted; if (!options_table[i].help) { continue; } formatted = wrap(options_table[i].help, 22, 77); if (options_table[i].type == SIPP_HELP_TEXT_HEADER) { printf("\n*** %s\n\n", formatted); } else { printf(" -%-16s: %s\n", options_table[i].option, formatted); } free(formatted); } printf ( "\n\nSignal handling:\n" "\n" " SIPp can be controlled using POSIX signals. The following signals\n" " are handled:\n" " USR1: Similar to pressing the 'q' key. It triggers a soft exit\n" " of SIPp. No more new calls are placed and all ongoing calls\n" " are finished before SIPp exits.\n" " Example: kill -SIGUSR1 732\n" " USR2: Triggers a dump of all statistics screens in\n" " __screens.log file. Especially useful \n" " in background mode to know what the current status is.\n" " Example: kill -SIGUSR2 732\n" "\n" "Exit codes:\n" "\n" " Upon exit (on fatal error or when the number of asked calls (-m\n" " option) is reached, SIPp exits with one of the following exit\n" " code:\n" " 0: All calls were successful\n" " 1: At least one call failed\n" " 97: Exit on internal command. Calls may have been processed\n" " 99: Normal exit without calls processed\n" " 253: RTP validation failure\n" " -1: Fatal error\n" " -2: Fatal error binding a socket\n"); end_pager(pager); } static void help_stats() { printf( "\n" " The -trace_stat option dumps all statistics in the\n" " file. The dump starts with one header\n" " line with all counters. All following lines are 'snapshots' of \n" " statistics counter given the statistics report frequency\n" " (-fd option). This file can be easily imported in any\n" " spreadsheet application, like Excel.\n" "\n" " In counter names, (P) means 'Periodic' - since last\n" " statistic row and (C) means 'Cumulative' - since SIPp was\n" " started.\n" "\n" " Available statistics are:\n" "\n" " - StartTime: \n" " Date and time when the test has started.\n" "\n" " - LastResetTime:\n" " Date and time when periodic counters were last reset.\n" "\n" " - CurrentTime:\n" " Date and time of the statistic row.\n" "\n" " - ElapsedTime:\n" " Elapsed time.\n" "\n" " - CallRate:\n" " Call rate (calls per seconds).\n" "\n" " - IncomingCall:\n" " Number of incoming calls.\n" "\n" " - OutgoingCall:\n" " Number of outgoing calls.\n" "\n" " - TotalCallCreated:\n" " Number of calls created.\n" "\n" " - CurrentCall:\n" " Number of calls currently ongoing.\n" "\n" " - SuccessfulCall:\n" " Number of successful calls.\n" "\n" " - FailedCall:\n" " Number of failed calls (all reasons).\n" "\n" " - FailedCannotSendMessage:\n" " Number of failed calls because SIPp cannot send the\n" " message (transport issue).\n" "\n" " - FailedMaxUDPRetrans:\n" " Number of failed calls because the maximum number of\n" " UDP retransmission attempts has been reached.\n" "\n" " - FailedUnexpectedMessage:\n" " Number of failed calls because the SIP message received\n" " is not expected in the scenario.\n" "\n" " - FailedCallRejected:\n" " Number of failed calls because of SIPp internal error.\n" " (a scenario sync command is not recognized, a scenario\n" " action failed or a scenario variable assignment failed).\n" "\n" " - FailedCmdNotSent:\n" " Number of failed calls because of inter-SIPp\n" " communication error (a scenario sync command failed to\n" " be sent).\n" "\n" " - FailedRegexpDoesntMatch:\n" " Number of failed calls because of regexp that doesn't\n" " match (there might be several regexp that don't match\n" " during the call but the counter is increased only by\n" " one).\n" "\n" " - FailedRegexpShouldntMatch:\n" " Number of failed calls because of regexp that shouldn't\n" " match but does (there might be several regexp that shouldn't match\n" " during the call but the counter is increased only by\n" " one).\n" "\n" " - FailedRegexpHdrNotFound:\n" " Number of failed calls because of regexp with 'hdr'\n" " option but no matching header found.\n" "\n" " - OutOfCallMsgs:\n" " Number of SIP messages received that cannot be associated\n" " to an existing call.\n" "\n" " - AutoAnswered:\n" " Number of unexpected specific messages received for new Call-ID.\n" " The message has been automatically answered by a 200 OK\n" " Currently, implemented for 'NOTIFY', 'INFO' and 'PING' messages.\n" "\n"); } /************* exit handler *****************/ static void print_last_stats() { interrupt = 1; if (sp) { sp->print_closing_stats(); } if (main_scenario) { stattask::report(); } } static void stop_all_traces() { message_lfi.fptr = NULL; log_lfi.fptr = NULL; dumpInRtt = 0; dumpInFile = 0; } static void freeInFiles() { for (file_map::iterator file_it = inFiles.begin(); file_it != inFiles.end(); file_it++) { delete file_it->second; } } static void freeUserVarMap() { for (int_vt_map::iterator vt_it = userVarMap.begin(); vt_it != userVarMap.end(); vt_it++) { vt_it->second->putTable(); userVarMap[vt_it->first] = NULL; } } static void manage_oversized_file(int signum) { FILE *f; char L_file_name[MAX_PATH]; struct timeval currentTime; static int managing = 0; // we can receive this signal more than once if (managing) { return; } managing = 1; snprintf(L_file_name, MAX_PATH, "%s_%ld_traces_oversized.log", scenario_file, (long) getpid()); f = fopen(L_file_name, "w"); if (!f) { ERROR_NO("Unable to open oversized log file"); } GET_TIME(¤tTime); fprintf(f, "-------------------------------------------- %s\n" "Max file size reached - no more logs\n", CStat::formatTime(¤tTime)); fflush(f); stop_all_traces(); print_all_responses = 0; error_lfi.fptr = NULL; } static void releaseGlobalAllocations() { delete main_scenario; delete ooc_scenario; delete aa_scenario; free_default_messages(); freeInFiles(); freeUserVarMap(); delete userVariables; delete globalVariables; } void sipp_exit(int rc, int rtp_errors, int echo_errors) { unsigned long counter_value_failed = 0; unsigned long counter_value_success = 0; /* Some signals may be delivered twice during exit() execution, and we must prevent all this from beeing done twice */ { static int already_exited = 0; if (already_exited) { return; } already_exited = 1; } screen_exit(); print_last_stats(); print_errors(); if (sp) { delete sp; sp = NULL; } /* Close open files. */ struct logfile_info** logfile_ptr; struct logfile_info* logfiles[] = { &screen_lfi, &calldebug_lfi, &message_lfi, &shortmessage_lfi, &log_lfi, &error_lfi, NULL}; for (logfile_ptr = logfiles; *logfile_ptr; ++logfile_ptr) { if ((*logfile_ptr)->fptr) { fclose((*logfile_ptr)->fptr); (*logfile_ptr)->fptr = NULL; } } // Get failed calls counter value before releasing objects if (display_scenario) { counter_value_failed = display_scenario->stats->GetStat(CStat::CPT_C_FailedCall); counter_value_success = display_scenario->stats->GetStat(CStat::CPT_C_SuccessfulCall); } else { rc = EXIT_TEST_FAILED; } releaseGlobalAllocations(); if (rc != EXIT_TEST_RES_UNKNOWN) { // Exit is not a normal exit. Just use the passed exit code. exit(rc); } else { // Normal exit: we need to determine if the calls were all // successful or not. In order to compute the return code, get // the counter of failed calls. If there is 0 failed calls, // then everything is OK! if ((rtp_errors > 0) || (echo_errors > 0)) { exit(EXIT_RTPCHECK_FAILED); } else { if (counter_value_failed == 0) { if ((timeout_exit) && (counter_value_success < 1)) { exit (EXIT_TEST_RES_INTERNAL); } else { exit(EXIT_TEST_OK); } } else { exit(EXIT_TEST_FAILED); } } } } static void sipp_sighandler(int signum) { sipp_exit(EXIT_TEST_RES_UNKNOWN, 0, 0); } static void sighandle_set() { struct sigaction action_quit = {}; struct sigaction action_file_size_exceeded = {}; action_quit.sa_handler = sipp_sighandler; action_file_size_exceeded.sa_handler = manage_oversized_file; sigaction(SIGTERM, &action_quit, NULL); sigaction(SIGINT, &action_quit, NULL); sigaction(SIGXFSZ, &action_file_size_exceeded, NULL); // avoid core dump if the max file size is exceeded } static void set_scenario(const char* name) { free(scenario_file); free(scenario_path); const char* sep = strrchr(name, '/'); if (sep) { ++sep; // include slash scenario_path = strndup(name, sep - name); } else { scenario_path = strdup(""); sep = name; } const char* ext = strrchr(sep, '.'); if (ext && strcmp(ext, ".xml") == 0) { scenario_file = strndup(sep, ext - sep); } else { scenario_file = strdup(sep); } } static int create_socket(struct sockaddr_storage* media_sa, int try_port, bool last_attempt, const char *type) { int s = socket(media_sa->ss_family, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) { ERROR_NO("Unable to create the %s RTP socket", type); } if (media_sa->ss_family == AF_INET) { (_RCAST(struct sockaddr_in*, media_sa))->sin_port = htons(try_port); } else { (_RCAST(struct sockaddr_in6*, media_sa))->sin6_port = htons(try_port); } if (::bind(s, (sockaddr*)media_sa, socklen_from_addr(media_sa)) != 0) { if (last_attempt) { ERROR_NO("Unable to bind %s RTP socket (IP=%s, port=%d)", type, media_ip, try_port); } ::close(s); return -1; } return s; } /** * Create and bind media_socket_audio, media_socket_video for RTP and * RCTP on try_port and try_port+2. * * Sets: media_socket_audio and media_socket_video. */ static int bind_rtp_sockets(struct sockaddr_storage* media_sa, int try_port, bool last_attempt) { /* Create RTP sockets for audio and video. */ media_socket_audio = create_socket(media_sa, try_port, last_attempt, "audio"); if (media_socket_audio == -1) { return -1; } /* Create and bind the second/video socket to try_port+2 */ /* (+1 is reserved for RTCP) */ media_socket_video = create_socket(media_sa, try_port + 2, last_attempt, "video"); if (media_socket_video == -1) { ::close(media_socket_audio); media_socket_audio = -1; return -1; } return 0; } /** * Set a bunch of globals and bind audio and video rtp sockets. * * Sets: media_ip, media_port, media_ip_is_ipv6, media_socket_audio, * media_socket_video. */ static void setup_media_sockets() { struct addrinfo hints = {0,}; struct addrinfo* local_addr; struct sockaddr_storage media_sockaddr = {0,}; int try_counter = 0; int max_tries = (min_rtp_port < (max_rtp_port - 2)) ? 100 : 1; media_port = min_rtp_port; // [JLTAG] // // RTPCHECK functionality needs port binding to happen only when rtp echo is in use // However since the refactoring in commit "99e847e2a129b5e4c4ccfdd502f79a029929ceb9" // was done media_ip needs to be set unconditionally so I have moved the media_ip // strcpy() to happen outside of the if-block... // /* Defaults for media sockets */ if (media_ip[0] == '\0') { strcpy(media_ip, local_ip); } // assert that an IPv6 'media_ip' is not surrounded by brackets? // hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; /* use local_ip_is_ipv6 as hint? */ /* Resolving local IP */ if (getaddrinfo(media_ip, NULL, &hints, &local_addr) != 0) { ERROR("Unknown RTP address '%s'.\n" "Use 'sipp -h' for details", media_ip); } memcpy(&media_sockaddr, local_addr->ai_addr, socklen_from_addr(_RCAST(struct sockaddr_storage*, local_addr->ai_addr))); freeaddrinfo(local_addr); media_ip_is_ipv6 = (media_sockaddr.ss_family == AF_INET6); media_socket_audio = -1; media_socket_video = -1; if (rtp_echo_enabled) { for (try_counter = 1; try_counter <= max_tries; try_counter++) { const bool last_attempt = ( try_counter == max_tries || media_port >= (max_rtp_port - 2)); if (bind_rtp_sockets(&media_sockaddr, media_port, last_attempt) == 0) { break; } // Old RFC 3551 says: // > RTP data SHOULD be carried on an even UDP port number and // > the corresponding RTCP packets SHOULD be carried on the // > next higher (odd) port number. // So, try only even numbers. media_port += 2; } } } /* Main */ int main(int argc, char *argv[]) { int argi = 0; pthread_t pthread2_id = 0, pthread3_id = 0; unsigned int generic_count = 0; bool slave_masterSet = false; int rtp_errors; int echo_errors; rtp_errors = 0; echo_errors = 0; generic[0] = NULL; /* At least one argument is needed */ if (argc < 2) { help(); exit(EXIT_OTHER); } { /* Ignore the SIGPIPE signal */ 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. */ #ifdef SIGWINCH sigaction(SIGWINCH, &action_pipe, NULL); #endif /* sig usr1 management */ struct sigaction action_usr1; memset(&action_usr1, 0, sizeof(action_usr1)); action_usr1.sa_handler = sipp_sigusr1; sigaction(SIGUSR1, &action_usr1, NULL); /* sig usr2 management */ struct sigaction action_usr2; memset(&action_usr2, 0, sizeof(action_usr2)); action_usr2.sa_handler = sipp_sigusr2; sigaction(SIGUSR2, &action_usr2, NULL); } pid = getpid(); memset(local_ip, 0, sizeof(local_ip)); #ifdef USE_SCTP memset(multihome_ip, 0, sizeof(multihome_ip)); #endif memset(media_ip, 0, sizeof(media_ip)); memset(control_ip, 0, sizeof(control_ip)); /* Initialize our global variable structure. */ globalVariables = new AllocVariableTable(NULL); userVariables = new AllocVariableTable(globalVariables); /* Command line parsing */ #define REQUIRE_ARG() if ((++argi) >= argc) { \ ERROR("Missing argument for param '%s'.\nUse 'sipp -h' for details", argv[argi - 1]); } #define CHECK_PASS() if (option->pass != pass) { break; } for (int pass = 0; pass <= 3; pass++) { for(argi = 1; argi < argc; argi++) { struct sipp_option *option = find_option(argv[argi]); if (!option) { if (argv[argi][0] != '-') { if ((pass == 0) && (remote_host[0] != 0)) { ERROR("remote_host given multiple times on command-line (%s and %s)", remote_host, argv[argi]); } strncpy(remote_host, argv[argi], sizeof(remote_host) - 1); continue; } help(); ERROR("Invalid argument: '%s'.\n" "Use 'sipp -h' for details", argv[argi]); } switch(option->type) { case SIPP_OPTION_HELP: if (argi + 1 < argc && !strcmp(argv[argi + 1], "stat")) { help_stats(); } else { help(); } exit(EXIT_OTHER); case SIPP_OPTION_VERSION: printf("\n %s.\n\n", /* SIPp v1.2.3-TLS-PCAP */ "SIPp " SIPP_VERSION #ifdef USE_TLS "-TLS" #endif #ifdef USE_SCTP "-SCTP" #endif #ifdef PCAPPLAY "-PCAP" #endif ); printf (" This program is free software; you can redistribute it and/or\n" " modify it under the terms of the GNU General Public License as\n" " published by the Free Software Foundation; either version 2 of\n" " the License, or (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public\n" " License along with this program; if not, write to the\n" " Free Software Foundation, Inc.,\n" " 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" "\n" " Author: see source files.\n\n"); exit(EXIT_OTHER); case SIPP_OPTION_INT: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = get_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_LONG: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_LONG_LONG: REQUIRE_ARG(); CHECK_PASS(); *((unsigned long long*)option->data) = get_long_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_TIME_SEC: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_time(argv[argi], argv[argi-1], 1000); break; case SIPP_OPTION_TIME_MS: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = get_time(argv[argi], argv[argi-1], 1); break; case SIPP_OPTION_TIME_MS_LONG: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_time(argv[argi], argv[argi-1], 1); break; case SIPP_OPTION_BOOL: REQUIRE_ARG(); CHECK_PASS(); *((bool*)option->data) = get_bool(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_FLOAT: REQUIRE_ARG(); CHECK_PASS(); *((double*)option->data) = get_double(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_STRING: REQUIRE_ARG(); CHECK_PASS(); *((char**)option->data) = argv[argi]; break; case SIPP_OPTION_ARGI: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = argi; break; case SIPP_OPTION_INPUT_FILE: { REQUIRE_ARG(); CHECK_PASS(); FileContents *data = new FileContents(argv[argi]); char *name = argv[argi]; if (strrchr(name, '/')) { name = strrchr(name, '/') + 1; } else if (strrchr(name, '\\')) { name = strrchr(name, '\\') + 1; } assert(name); inFiles[name] = data; /* By default, the first file is used for IP address input. */ if (!ip_file) { ip_file = name; } if (!default_file) { default_file = name; } } break; case SIPP_OPTION_INDEX_FILE: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); { char *fileName = argv[argi - 1]; char *endptr; int field; if (inFiles.find(fileName) == inFiles.end()) { ERROR("Could not find file for -infindex: %s", argv[argi - 1]); } field = strtoul(argv[argi], &endptr, 0); if (*endptr) { ERROR("Invalid field specification for -infindex: %s", argv[argi]); } inFiles[fileName]->index(field); } break; case SIPP_OPTION_SETFLAG: CHECK_PASS(); *((bool*)option->data) = true; break; case SIPP_OPTION_UNSETFLAG: CHECK_PASS(); *((bool*)option->data) = false; break; case SIPP_OPTION_TRANSPORT: REQUIRE_ARG(); CHECK_PASS(); if (strlen(argv[argi]) != 2) { ERROR("Invalid argument for -t param : '%s'.\n" "Use 'sipp -h' for details", argv[argi]); } switch(argv[argi][0]) { case 'u': transport = T_UDP; break; case 't': transport = T_TCP; break; case 's': #ifdef USE_SCTP transport = T_SCTP; #else ERROR("To use SCTP transport you must compile SIPp with lksctp"); #endif break; case 'l': #ifdef USE_TLS transport = T_TLS; if (TLS_init() != 1) { printf("TLS initialization problem\n"); exit(-1); } #else ERROR("To use TLS transport you must compile SIPp with OpenSSL or WolfSSL"); #endif break; case 'c': if (strlen(comp_error)) { ERROR("No " COMP_PLUGGIN " plugin available: %s", comp_error); } transport = T_UDP; compression = 1; } switch(argv[argi][1]) { case '1': multisocket = 0; peripsocket = 0; break; case 'n': multisocket = 1; peripsocket = 0; break; case 'i': multisocket = 1; peripsocket = 1; break; } if (peripsocket && transport != T_UDP) { ERROR("You can only use a perip socket with UDP!"); } break; case SIPP_OPTION_NEED_SCTP: CHECK_PASS(); ERROR("SCTP support is required for the %s option.", argv[argi]); break; case SIPP_OPTION_NEED_SSL: CHECK_PASS(); ERROR("TLS support is required for the %s option.", argv[argi]); break; case SIPP_OPTION_MAX_SOCKET: REQUIRE_ARG(); CHECK_PASS(); max_multi_socket = get_long(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_CSEQ: REQUIRE_ARG(); CHECK_PASS(); base_cseq = get_long(argv[argi], argv[argi - 1]); base_cseq--; break; case SIPP_OPTION_IP: { int dummy_port; char* ptr = (char*)option->data; REQUIRE_ARG(); CHECK_PASS(); strcpy(ptr, argv[argi]); get_host_and_port(ptr, ptr, &dummy_port); } break; case SIPP_OPTION_LIMIT: REQUIRE_ARG(); CHECK_PASS(); if (users >= 0) { ERROR("Can not set open call limit (-l) when -users is specified."); } open_calls_allowed = get_long(argv[argi], argv[argi - 1]); open_calls_user_setting = 1; break; case SIPP_OPTION_USERS: REQUIRE_ARG(); CHECK_PASS(); users = open_calls_allowed = get_long(argv[argi], argv[argi - 1]); open_calls_user_setting = 1; break; case SIPP_OPTION_KEY: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); if (generic_count + 1 >= sizeof(generic)/sizeof(generic[0])) { ERROR("Too many generic parameters %d",generic_count + 1); } generic[generic_count++] = &argv[argi - 1]; generic[generic_count] = NULL; break; case SIPP_OPTION_VAR: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); { int varId = globalVariables->find(argv[argi - 1], false); if (varId == -1) { globalVariables->dump(); ERROR("Can not set the global variable %s, because it does not exist.", argv[argi - 1]); } globalVariables->getVar(varId)->setString(strdup(argv[argi])); } break; case SIPP_OPTION_3PCC: if (slave_masterSet) { ERROR("-3PCC option is not compatible with -master and -slave options"); } if (extendedTwinSippMode) { ERROR("-3pcc and -slave_cfg options are not compatible"); } REQUIRE_ARG(); CHECK_PASS(); twinSippMode = true; strcpy(twinSippHost, argv[argi]); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); break; case SIPP_OPTION_SCENARIO: REQUIRE_ARG(); CHECK_PASS(); if (main_scenario) { ERROR("Internal error, main_scenario already set"); } else if (!strcmp(argv[argi - 1], "-sf")) { set_scenario(argv[argi]); if (useLogf == 1) { rotate_logfile(); } main_scenario = new scenario(argv[argi], 0); main_scenario->stats->setFileName(scenario_file, ".csv"); } else if (!strcmp(argv[argi - 1], "-sn")) { int i = find_scenario(argv[argi]); set_scenario(argv[argi]); main_scenario = new scenario(0, i); main_scenario->stats->setFileName(scenario_file, ".csv"); } else if (!strcmp(argv[argi - 1], "-sd")) { int i = find_scenario(argv[argi]); fprintf(stdout, "%s", default_scenario[i]); exit(EXIT_OTHER); } else { ERROR("Internal error, I don't recognize %s as a scenario option", argv[argi] - 1); } break; case SIPP_OPTION_OOC_SCENARIO: REQUIRE_ARG(); CHECK_PASS(); if (!strcmp(argv[argi - 1], "-oocsf")) { ooc_scenario = new scenario(argv[argi], 0); } else if (!strcmp(argv[argi - 1], "-oocsn")) { int i = find_scenario(argv[argi]); ooc_scenario = new scenario(0, i); } else { ERROR("Internal error, I don't recognize %s as a scenario option", argv[argi] - 1); } break; case SIPP_OPTION_SLAVE_CFG: REQUIRE_ARG(); CHECK_PASS(); if (twinSippMode) { ERROR("-slave_cfg and -3pcc options are not compatible"); } extendedTwinSippMode = true; slave_cfg_file = new char [strlen(argv[argi]) + 1]; sprintf(slave_cfg_file,"%s", argv[argi]); parse_slave_cfg(); break; case SIPP_OPTION_3PCC_EXTENDED: REQUIRE_ARG(); CHECK_PASS(); if (slave_masterSet) { ERROR("-slave and -master options are not compatible"); } if (twinSippMode) { ERROR("-master and -slave options are not compatible with -3PCC option"); } *((char**)option->data) = argv[argi]; slave_masterSet = true; break; case SIPP_OPTION_RSA: { REQUIRE_ARG(); CHECK_PASS(); char *remote_s_address; int remote_s_p = DEFAULT_PORT; int temp_remote_s_p; temp_remote_s_p = 0; remote_s_address = argv[argi]; get_host_and_port(remote_s_address, remote_s_address, &temp_remote_s_p); if (temp_remote_s_p != 0) { remote_s_p = temp_remote_s_p; } printf("Resolving remote sending address %s...\n", remote_s_address); /* FIXME: add DNS SRV support using liburli? */ if (gai_getsockaddr(&remote_sending_sockaddr, remote_s_address, remote_s_p, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", remote_s_address); } use_remote_sending_addr = 1; break; } case SIPP_OPTION_RTCHECK: REQUIRE_ARG(); CHECK_PASS(); if (!strcmp(argv[argi], "full")) { *((int*)option->data) = RTCHECK_FULL; } else if (!strcmp(argv[argi], "loose")) { *((int*)option->data) = RTCHECK_LOOSE; } else { ERROR("Unknown retransmission detection method: %s", argv[argi]); } break; case SIPP_OPTION_TDMMAP: { REQUIRE_ARG(); CHECK_PASS(); int i1, i2, i3, i4, i5, i6, i7; if (sscanf(argv[argi], "{%d-%d}{%d}{%d-%d}{%d-%d}", &i1, &i2, &i3, &i4, &i5, &i6, &i7) == 7) { use_tdmmap = true; tdm_map_a = i2 - i1; tdm_map_x = i1; tdm_map_h = i3; tdm_map_b = i5 - i4; tdm_map_y = i4; tdm_map_c = i7 - i6; tdm_map_z = i6; } else { ERROR("Parameter -tdmmap must be of form {%%d-%%d}{%%d}{%%d-%%d}{%%d-%%d}"); } break; } case SIPP_OPTION_DEFAULTS: { unsigned long *ptr = (unsigned long*)option->data; char *token; REQUIRE_ARG(); CHECK_PASS(); *ptr = 0; token = argv[argi]; while ((token = strtok(token, ","))) { if (!strcmp(token, "none")) { *ptr = 0; } else { unsigned long mask = 0; int mode = 1; char *p = token; if (token[0] == '+') { mode = 1; p++; } else if (token[0] == '-') { mode = -1; p++; } if (!strcmp(p, "all")) { mask = DEFAULT_BEHAVIOR_ALL; } else if (!strcmp(p, "bye")) { mask = DEFAULT_BEHAVIOR_BYE; } else if (!strcmp(p, "abortunexp")) { mask = DEFAULT_BEHAVIOR_ABORTUNEXP; } else if (!strcmp(p, "pingreply")) { mask = DEFAULT_BEHAVIOR_PINGREPLY; } else if (!strcmp(p, "cseq")) { mask = DEFAULT_BEHAVIOR_BADCSEQ; } else { ERROR("Unknown default behavior: '%s'", token); } switch(mode) { case 0: *ptr = mask; break; case 1: *ptr |= mask; break; case -1: *ptr &= ~mask; break; default: assert(0); } } token = NULL; } break; } case SIPP_OPTION_LFNAME: REQUIRE_ARG(); CHECK_PASS(); ((struct logfile_info*)option->data)->fixedname = true; strcpy(((struct logfile_info*)option->data)->file_name, argv[argi]); break; case SIPP_OPTION_LFOVERWRITE: REQUIRE_ARG(); CHECK_PASS(); ((struct logfile_info*)option->data)->fixedname = true; ((struct logfile_info*)option->data)->overwrite = get_bool(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_PLUGIN: { int ret; REQUIRE_ARG(); CHECK_PASS(); void* handle = dlopen(argv[argi], RTLD_NOW); if (!handle) { ERROR("Could not open plugin %s: %s", argv[argi], dlerror()); } int (*init)(); void* funcptr = dlsym(handle, "init"); /* http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c */ *reinterpret_cast(&init) = funcptr; // yuck const char* error; if ((error = dlerror())) { ERROR("Could not locate init function in %s: %s", argv[argi], error); } ret = init(); if (ret != 0) { ERROR("Plugin %s initialization failed.", argv[argi]); } } break; default: ERROR("Internal error: I don't recognize the option type for %s", argv[argi]); } } } /* Load compression plugin if needed/available. */ if (compression) { comp_load(); } if ((extendedTwinSippMode && !slave_masterSet) || (!extendedTwinSippMode && slave_masterSet)) { ERROR("-slave_cfg option must be used with -slave or -master option"); } if (peripsocket) { if (!ip_file) { ERROR("You must use the -inf option when using -t ui.\n" "Use 'sipp -h' for details"); } } if (ringbuffer_size && max_log_size) { ERROR("Ring Buffer options and maximum log size are mutually exclusive."); } if (global_lost) { lose_packets = 1; } /* If no scenario was selected, choose the uac one */ if (scenario_file == NULL) { assert(main_scenario == NULL); int i = find_scenario("uac"); set_scenario("uac"); main_scenario = new scenario(0, i); main_scenario->stats->setFileName(scenario_file, ".csv"); } #ifdef USE_TLS if ((transport == T_TLS) && (TLS_init_context() != TLS_INIT_NORMAL)) { ERROR("FI_init_ssl_context() failed"); } #endif if (useMessagef == 1) { rotate_messagef(); } if (useShortMessagef == 1) { rotate_shortmessagef(); } if (useCallDebugf) { rotate_calldebugf(); } if (useScreenf == 1) { rotate_screenf(); } // TODO: finish the -trace_timeout option implementation /* if (useTimeoutf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%d_timeout.log", scenario_file, getpid()); timeoutf = fopen(L_file_name, "w"); if (!timeoutf) { ERROR("Unable to create '%s'", L_file_name); } } */ if (useCountf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%ld_counts.csv", scenario_file, (long) getpid()); countf = fopen(L_file_name, "w"); if (!countf) { ERROR("Unable to create '%s'", L_file_name); } print_count_file(countf, 1); } if (useErrorCodesf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%ld_error_codes.csv", scenario_file, (long) getpid()); codesf = fopen(L_file_name, "w"); if (!codesf) { ERROR("Unable to create '%s'", L_file_name); } } if (dumpInRtt == 1) { main_scenario->stats->initRtt(scenario_file, ".csv", report_freq_dumpRtt); } if (rate_increase_freq == 0) { rate_increase_freq = report_freq_dumpLog; } // Check the soft limit on the number of open files, // error out if this does not allow us to open the // required number of signalling channels, and warn // if this may not allow enough media channels. if (!skip_rlimit) { struct rlimit rlimit; unsigned max_sockets_needed = multisocket ? max_multi_socket : 1; if (getrlimit (RLIMIT_NOFILE, &rlimit) < 0) { ERROR_NO("getrlimit error"); } if (max_sockets_needed > rlimit.rlim_cur) { ERROR("Maximum number of open sockets (%d) should be less than the maximum number " "of open files (%lu). Tune this with the `ulimit` command or the -max_socket " "option", max_sockets_needed, (unsigned long)rlimit.rlim_cur); } if ((open_calls_allowed + max_sockets_needed) > rlimit.rlim_cur) { WARNING("Maximum number of open sockets (%d) plus number of open calls (%d) " "should be less than the maximum number of open files (%lu) to " "allow for media support. Tune this with the `ulimit` command, " "the -l option or the -max_socket option", max_sockets_needed, open_calls_allowed, (unsigned long)rlimit.rlim_cur); } } /* if (!ooc_scenario) { ooc_scenario = new scenario(0, find_scenario("ooc_default")); ooc_scenario->stats->setFileName((char*)"ooc_default", (char*)".csv"); } */ display_scenario = main_scenario; aa_scenario = new scenario(0, find_scenario("ooc_dummy")); aa_scenario->stats->setFileName("ooc_dummy", ".csv"); init_default_messages(); for (int i = 1; i <= users; i++) { freeUsers.push_back(i); userVarMap[i] = new VariableTable(userVariables); } if (argiFileName) { main_scenario->stats->setFileName(argv[argiFileName]); } // setup option form cmd line call::maxDynamicId = maxDynamicId; call::startDynamicId = startDynamicId; call::dynamicId = startDynamicId; call::stepDynamicId = stepDynamicId; /* Now Initialize the scenarios. */ main_scenario->runInit(); if (ooc_scenario) { ooc_scenario->runInit(); } /* In which mode the tool is launched ? */ main_scenario->computeSippMode(); if (ooc_scenario && sendMode == MODE_SERVER) { ERROR("SIPp cannot use out-of-call scenarios when running in server mode"); } sp = new ScreenPrinter(); if (!sp->M_headless) { screen_init(); } sighandle_set(); /* 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); } } sipp_usleep(sleeptime * 1000); /* Create the statistics reporting task. */ stattask::initialize(); /* Create the screen update task. */ screentask::initialize(); /* Create the rate increase task. */ ratetask::initialize(); /* Create a watchdog task. */ if (watchdog_interval) { new watchdog(watchdog_interval, watchdog_reset, watchdog_major_threshold, watchdog_major_maxtriggers, watchdog_minor_threshold, watchdog_minor_maxtriggers); } /* Setting the rate and its dependant params (open_calls_allowed) */ /* If we are a client, then create the task to open new calls. */ if (creationMode == MODE_CLIENT) { CallGenerationTask::initialize(); CallGenerationTask::set_rate(rate); } #ifdef HAVE_EPOLL epollevents = (struct epoll_event*)malloc(sizeof(struct epoll_event) * max_recv_loops); epollfd = epoll_create(SIPP_MAXFDS); if (epollfd == -1) { ERROR_NO("Failed to open epoll FD"); } #endif open_connections(); /* Always create and Bind RTP socket */ /* to avoid ICMP errors from us. */ setup_media_sockets(); /* Creating the remote control socket thread */ setup_ctrl_socket(); if (!nostdin) { setup_stdin_socket(); } if (rtp_echo_enabled && media_socket_audio > 0) { if (pthread_create(&pthread2_id, NULL, (void *(*)(void *))rtp_echo_thread, &media_socket_audio) == -1) { ERROR_NO("Unable to create RTP echo thread"); } } /* Creating second RTP echo thread for video. */ if (rtp_echo_enabled && media_socket_video > 0) { if (pthread_create(&pthread3_id, NULL, (void *(*)(void *)) rtp_echo_thread, &media_socket_video) == -1) { ERROR_NO("Unable to create video RTP echo thread"); } } traffic_thread(rtp_errors, echo_errors); /* Cancel and join other threads. */ run_echo_thread.store(false, std::memory_order_relaxed); if (pthread2_id) { pthread_join(pthread2_id, NULL); } if (pthread3_id) { pthread_join(pthread3_id, NULL); } #ifdef HAVE_EPOLL close(epollfd); free(epollevents); #endif free(scenario_file); free(scenario_path); sipp_exit(EXIT_TEST_RES_UNKNOWN, rtp_errors, echo_errors); // MAIN EXIT PATH HERE...); } sipp-3.7.2/src/sipp_unittest.cpp0000664000000000000000000000262414525516253013607 0ustar /* * 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 * * Author : Rob Day - 11 May 2014 */ #define GLOBALS_FULL_DEFINITION #include "sipp.hpp" #include "gtest/gtest.h" #include "gmock/gmock.h" #include int main(int argc, char* argv[]) { globalVariables = new AllocVariableTable(NULL); userVariables = new AllocVariableTable(globalVariables); main_scenario = new scenario(0, 0); ::testing::GMOCK_FLAG(verbose) = "verbose"; ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } /* Quickfix to fix unittests that depend on sipp_exit availability, * now that sipp_exit has been moved into sipp.cpp which is not * included. */ void sipp_exit(int rc, int rtp_errors, int echo_errors) { exit(rc); } sipp-3.7.2/src/socket.cpp0000664000000000000000000027526414525516253012201 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include #include #include #include #include "config.h" #include "sipp.hpp" #include "socket.hpp" #include "logger.hpp" /* Older non C++11 gcc (4.6) does not have nullptr */ #define const_char_nullptr (reinterpret_cast(0)) extern bool do_hide; SIPpSocket *ctrl_socket = NULL; SIPpSocket *stdin_socket = NULL; static int stdin_fileno = -1; static int stdin_mode; /******************** Recv Poll Processing *********************/ unsigned pollnfds; #ifdef HAVE_EPOLL int epollfd; struct epoll_event epollfiles[SIPP_MAXFDS]; struct epoll_event* epollevents; #else struct pollfd pollfiles[SIPP_MAXFDS]; #endif SIPpSocket *sockets[SIPP_MAXFDS]; int pending_messages = 0; map map_perip_fd; int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, const char *service, int flags, int family) { const struct addrinfo hints = {flags, family,}; struct addrinfo* res; int error = getaddrinfo(host, service, &hints, &res); if (error == 0) { memcpy(ss, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } else { WARNING("getaddrinfo failed: %s", gai_strerror(error)); } return error; } int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, unsigned short port, int flags, int family) { if (port) { char service[NI_MAXSERV + 1]; snprintf(service, sizeof(service), "%d", port); return gai_getsockaddr(ss, host, service, flags, family); } else { return gai_getsockaddr(ss, host, const_char_nullptr, flags, family); } } void sockaddr_update_port(struct sockaddr_storage* ss, short port) { switch (ss->ss_family) { case AF_INET: _RCAST(struct sockaddr_in*, ss)->sin_port = htons(port); break; case AF_INET6: _RCAST(struct sockaddr_in6*, ss)->sin6_port = htons(port); break; default: ERROR("Unsupported family type"); } } static void process_set(char* what) { char *rest = strchr(what, ' '); if (rest) { *rest++ = '\0'; trim(rest); } else { WARNING("The set command requires two arguments (attribute and value)"); return; } if (!strcmp(what, "rate")) { char *end; double drest = strtod(rest, &end); if (users >= 0) { WARNING("Rates can not be set in a user-based benchmark."); } else if (*end) { WARNING("Invalid rate value: \"%s\"", rest); } else { CallGenerationTask::set_rate(drest); } } else if (!strcmp(what, "rate-scale")) { char *end; double drest = strtod(rest, &end); if (*end) { WARNING("Invalid rate-scale value: \"%s\"", rest); } else { rate_scale = drest; } } else if (!strcmp(what, "users")) { char *end; int urest = strtol(rest, &end, 0); if (users < 0) { WARNING("Users can not be changed at run time for a rate-based benchmark."); } else if (*end) { WARNING("Invalid users value: \"%s\"", rest); } else if (urest < 0) { WARNING("Invalid users value: \"%s\"", rest); } else { CallGenerationTask::set_users(urest); } } else if (!strcmp(what, "limit")) { char *end; unsigned long lrest = strtoul(rest, &end, 0); if (users >= 0) { WARNING("Can not set call limit for a user-based benchmark."); } else if (*end) { WARNING("Invalid limit value: \"%s\"", rest); } else { open_calls_allowed = lrest; open_calls_user_setting = 1; } } else if (!strcmp(what, "display")) { if (!strcmp(rest, "main")) { display_scenario = main_scenario; } else if (!strcmp(rest, "ooc") && ooc_scenario) { display_scenario = ooc_scenario; } else { WARNING("Unknown display scenario: %s", rest); } } else if (!strcmp(what, "hide")) { if (!strcmp(rest, "true")) { do_hide = true; } else if (!strcmp(rest, "false")) { do_hide = false; } else { WARNING("Invalid bool: %s", rest); } } else { WARNING("Unknown set attribute: %s", what); } } static void process_trace(char* what) { bool on = false; char *rest = strchr(what, ' '); if (rest) { *rest++ = '\0'; trim(rest); } else { WARNING("The trace command requires two arguments (log and [on|off])"); return; } if (!strcmp(rest, "on")) { on = true; } else if (!strcmp(rest, "off")) { on = false; } else if (!strcmp(rest, "true")) { on = true; } else if (!strcmp(rest, "false")) { on = false; } else { WARNING("The trace command's second argument must be on or off."); return; } if (!strcmp(what, "error")) { if (on == !!print_all_responses) { return; } if (on) { print_all_responses = 1; } else { print_all_responses = 0; log_off(&error_lfi); } } else if (!strcmp(what, "logs")) { if (on == !!log_lfi.fptr) { return; } if (on) { useLogf = 1; rotate_logfile(); } else { useLogf = 0; log_off(&log_lfi); } } else if (!strcmp(what, "messages")) { if (on == !!message_lfi.fptr) { return; } if (on) { useMessagef = 1; rotate_logfile(); } else { useMessagef = 0; log_off(&message_lfi); } } else if (!strcmp(what, "shortmessages")) { if (on == !!shortmessage_lfi.fptr) { return; } if (on) { useShortMessagef = 1; rotate_shortmessagef(); } else { useShortMessagef = 0; log_off(&shortmessage_lfi); } } else { WARNING("Unknown log file: %s", what); } } static void process_dump(char* what) { if (!strcmp(what, "tasks")) { dump_tasks(); } else if (!strcmp(what, "variables")) { display_scenario->allocVars->dump(); } else { WARNING("Unknown dump type: %s", what); } } static void process_reset(char* what) { if (!strcmp(what, "stats")) { main_scenario->stats->computeStat(CStat::E_RESET_C_COUNTERS); } else { WARNING("Unknown reset type: %s", what); } } static bool process_command(char* command) { trim(command); char *rest = strchr(command, ' '); if (rest) { *rest++ = '\0'; trim(rest); } if (!rest) { WARNING("The %s command requires at least one argument", command); } else if (!strcmp(command, "set")) { process_set(rest); } else if (!strcmp(command, "trace")) { process_trace(rest); } else if (!strcmp(command, "dump")) { process_dump(rest); } else if (!strcmp(command, "reset")) { process_reset(rest); } else { WARNING("Unrecognized command: \"%s\"", command); } return false; } int command_mode = 0; char *command_buffer = NULL; extern bool sipMsgCheck (const char *P_msg, SIPpSocket *socket); static const char* get_trimmed_call_id(const char* msg) { /* A call_id identifies a call and is generated by SIPp for each * new call. In client mode, it is mandatory to use the value * generated by SIPp in the "Call-ID" header. Otherwise, SIPp will * not recognise the answer to the message sent as being part of an * existing call. * * Note: [call_id] can be pre-pended with an arbitrary string using * '///'. * Example: Call-ID: ABCDEFGHIJ///[call_id] * - it will still be recognized by SIPp as part of the same call. */ const char *call_id = get_call_id(msg); const char *slashes = strstr(call_id, "///"); if ((!callidSlash) && slashes) { return slashes + 3; } return call_id; } static char* get_inet_address(const struct sockaddr_storage* addr, char* dst, int len) { if (getnameinfo(_RCAST(struct sockaddr*, addr), socklen_from_addr(addr), dst, len, NULL, 0, NI_NUMERICHOST) != 0) { snprintf(dst, len, "addr not supported"); } return dst; } static bool process_key(int c) { switch (c) { case '1': currentScreenToDisplay = DISPLAY_SCENARIO_SCREEN; print_statistics(0); break; case '2': currentScreenToDisplay = DISPLAY_STAT_SCREEN; print_statistics(0); break; case '3': currentScreenToDisplay = DISPLAY_REPARTITION_SCREEN; print_statistics(0); break; case '4': currentScreenToDisplay = DISPLAY_VARIABLE_SCREEN; print_statistics(0); break; case '5': if (use_tdmmap) { currentScreenToDisplay = DISPLAY_TDM_MAP_SCREEN; print_statistics(0); } break; /* Screens 6, 7, 8, 9 are for the extra RTD repartitions. */ case '6': case '7': case '8': case '9': currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; currentRepartitionToDisplay = (c - '6') + 2; print_statistics(0); break; case '+': if (users >= 0) { CallGenerationTask::set_users((int)(users + 1 * rate_scale)); } else { CallGenerationTask::set_rate(rate + 1 * rate_scale); } print_statistics(0); break; case '-': if (users >= 0) { CallGenerationTask::set_users((int)(users - 1 * rate_scale)); } else { CallGenerationTask::set_rate(rate - 1 * rate_scale); } print_statistics(0); break; case '*': if (users >= 0) { CallGenerationTask::set_users((int)(users + 10 * rate_scale)); } else { CallGenerationTask::set_rate(rate + 10 * rate_scale); } print_statistics(0); break; case '/': if (users >= 0) { CallGenerationTask::set_users((int)(users - 10 * rate_scale)); } else { CallGenerationTask::set_rate(rate - 10 * rate_scale); } print_statistics(0); break; case 'p': if (paused) { CallGenerationTask::set_paused(false); } else { CallGenerationTask::set_paused(true); } print_statistics(0); break; case 's': if (screenf) { print_screens(); } break; case 'q': quitting+=10; print_statistics(0); break; case 'Q': /* We are going to break, so we never have a chance to press q twice. */ quitting+=20; print_statistics(0); break; } return false; } int handle_ctrl_socket() { unsigned char bufrcv [SIPP_MAX_MSG_SIZE]; int ret = recv(ctrl_socket->ss_fd, bufrcv, sizeof(bufrcv) - 1, 0); if (ret <= 0) { return ret; } if (bufrcv[0] == 'c') { /* No 'c', but we need one for '\0'. */ char *command = (char *)malloc(ret); if (!command) { ERROR("Out of memory allocated command buffer."); } memcpy(command, bufrcv + 1, ret - 1); command[ret - 1] = '\0'; process_command(command); free(command); } else { process_key(bufrcv[0]); } return 0; } void setup_ctrl_socket() { int port, firstport; int try_counter = 60; struct sockaddr_storage ctl_sa; int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock == -1) { ERROR_NO("Unable to create remote control socket!"); } if (control_port) { port = control_port; /* If the user specified the control port, then we must assume they know * what they want, and should not cycle. */ try_counter = 1; } else { /* Allow 60 control sockets on the same system */ /* (several SIPp instances) */ port = DEFAULT_CTRL_SOCKET_PORT; } firstport = port; memset(&ctl_sa, 0, sizeof(struct sockaddr_storage)); if (control_ip[0]) { if (gai_getsockaddr(&ctl_sa, control_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown control address '%s'.\n" "Use 'sipp -h' for details", control_ip); } } else { ((struct sockaddr_in *)&ctl_sa)->sin_family = AF_INET; ((struct sockaddr_in *)&ctl_sa)->sin_addr.s_addr = INADDR_ANY; } while (try_counter) { ((struct sockaddr_in *)&ctl_sa)->sin_port = htons(port); if (!::bind(sock, (struct sockaddr *)&ctl_sa, sizeof(struct sockaddr_in))) { /* Bind successful */ break; } try_counter--; port++; } if (try_counter == 0) { if (control_port) { ERROR_NO("Unable to bind remote control socket to UDP port %d", control_port); } else { WARNING("Unable to bind remote control socket (tried UDP ports %d-%d): %s", firstport, port - 1, strerror(errno)); } return; } ctrl_socket = new SIPpSocket(0, T_UDP, sock, 0); if (!ctrl_socket) { ERROR_NO("Could not setup control socket!"); } } void reset_stdin() { fcntl(stdin_fileno, F_SETFL, stdin_mode); } void setup_stdin_socket() { stdin_fileno = fileno(stdin); stdin_mode = fcntl(stdin_fileno, F_GETFL); atexit(reset_stdin); fcntl(stdin_fileno, F_SETFL, stdin_mode | O_NONBLOCK); stdin_socket = new SIPpSocket(0, T_TCP, stdin_fileno, 0); if (!stdin_socket) { ERROR_NO("Could not setup keyboard (stdin) socket!"); } } #define SIPP_ENDL "\r\n" void handle_stdin_socket() { int c; int chars = 0; if (feof(stdin)) { stdin_socket->close(); stdin_socket = NULL; return; } while (((c = screen_readkey()) != -1)) { chars++; if (command_mode) { if (c == '\n') { bool quit = process_command(command_buffer); if (quit) { return; } command_buffer[0] = '\0'; command_mode = 0; } #ifndef __SUNOS else if (c == key_backspace || c == key_dc) #else else if (c == 14) #endif { int command_len = strlen(command_buffer); if (command_len > 0) { command_buffer[command_len--] = '\0'; } } else { int command_len = strlen(command_buffer); char *realloc_ptr = (char *)realloc(command_buffer, command_len + 2); if (realloc_ptr) { command_buffer = realloc_ptr; } else { free(command_buffer); ERROR("Out of memory"); return; } command_buffer[command_len++] = c; command_buffer[command_len] = '\0'; putchar(c); fflush(stdout); } } else if (c == 'c') { command_mode = 1; char *realloc_ptr = (char *)realloc(command_buffer, 1); if (realloc_ptr) { command_buffer = realloc_ptr; } else { free(command_buffer); ERROR("Out of memory"); return; } command_buffer[0] = '\0'; } else { process_key(c); } } if (chars == 0) { /* We did not read any characters, even though we should have. */ stdin_socket->close(); stdin_socket = NULL; } } /****************************** Network Interface *******************/ /* Our message detection states: */ #define CFM_NORMAL 0 /* No CR Found, searchign for \r\n\r\n. */ #define CFM_CONTROL 1 /* Searching for 27 */ #define CFM_CR 2 /* CR Found, Searching for \n\r\n */ #define CFM_CRLF 3 /* CRLF Found, Searching for \r\n */ #define CFM_CRLFCR 4 /* CRLFCR Found, Searching for \n */ #define CFM_CRLFCRLF 5 /* We've found the end of the headers! */ static void merge_socketbufs(struct socketbuf* socketbuf) { struct socketbuf *next = socketbuf->next; int newsize; char *newbuf; if (!next) { return; } if (next->offset) { ERROR("Internal error: can not merge a socketbuf with a non-zero offset."); } if (socketbuf->offset) { memmove(socketbuf->buf, socketbuf->buf + socketbuf->offset, socketbuf->len - socketbuf->offset); socketbuf->len -= socketbuf->offset; socketbuf->offset = 0; } newsize = socketbuf->len + next->len; newbuf = (char *)realloc(socketbuf->buf, newsize); if (!newbuf) { ERROR("Could not allocate memory to merge socket buffers!"); } memcpy(newbuf + socketbuf->len, next->buf, next->len); socketbuf->buf = newbuf; socketbuf->len = newsize; socketbuf->next = next->next; free_socketbuf(next); } /* Check for a message in the socket and return the length of the first * message. If this is UDP, the only check is if we have buffers. If this is * TCP or TLS we need to parse out the content-length. */ int SIPpSocket::check_for_message() { struct socketbuf *socketbuf = ss_in; int state = ss_control ? CFM_CONTROL : CFM_NORMAL; const char *l; if (!socketbuf) return 0; if (ss_transport == T_UDP || ss_transport == T_SCTP) { return socketbuf->len; } int len = 0; while (socketbuf->offset + len < socketbuf->len) { char c = socketbuf->buf[socketbuf->offset + len]; switch(state) { case CFM_CONTROL: /* For CMD Message the escape char is the end of message */ if (c == 27) { return len + 1; /* The plus one includes the control character. */ } break; case CFM_NORMAL: if (c == '\r') { state = CFM_CR; } break; case CFM_CR: if (c == '\n') { state = CFM_CRLF; } else { state = CFM_NORMAL; } break; case CFM_CRLF: if (c == '\r') { state = CFM_CRLFCR; } else { state = CFM_NORMAL; } break; case CFM_CRLFCR: if (c == '\n') { state = CFM_CRLFCRLF; } else { state = CFM_NORMAL; } break; } /* Head off failing because the buffer does not contain the whole header. */ if (socketbuf->offset + len == socketbuf->len - 1) { merge_socketbufs(socketbuf); } if (state == CFM_CRLFCRLF) { break; } len++; } /* We did not find the end-of-header marker. */ if (state != CFM_CRLFCRLF) { return 0; } char saved = socketbuf->buf[socketbuf->offset + len]; socketbuf->buf[socketbuf->offset + len] = '\0'; /* Find the content-length header. */ if ((l = strcasestr(socketbuf->buf + socketbuf->offset, "\r\nContent-Length:"))) { l += strlen("\r\nContent-Length:"); } else if ((l = strcasestr(socketbuf->buf + socketbuf->offset, "\r\nl:"))) { l += strlen("\r\nl:"); } socketbuf->buf[socketbuf->offset + len] = saved; /* There is no header, so the content-length is zero. */ if (!l) return len + 1; /* Skip spaces. */ while (isspace(*l)) { if (*l == '\r' || *l == '\n') { /* We ran into an end-of-line, so there is no content-length. */ return len + 1; } l++; } /* Do the integer conversion, we only allow '\r' or spaces after the integer. */ char *endptr; int content_length = strtol(l, &endptr, 10); if (*endptr != '\r' && !isspace(*endptr)) { content_length = 0; } /* Now that we know how large this message is, we make sure we have the whole thing. */ do { /* It is in this buffer. */ if (socketbuf->offset + len + content_length < socketbuf->len) { return len + content_length + 1; } if (socketbuf->next == NULL) { /* There is no buffer to merge, so we fail. */ return 0; } /* We merge ourself with the next buffer. */ merge_socketbufs(socketbuf); } while (1); } #ifdef USE_SCTP int SIPpSocket::handleSCTPNotify(char* buffer) { union sctp_notification *notifMsg; notifMsg = (union sctp_notification *)buffer; TRACE_MSG("SCTP Notification: %d\n", ntohs(notifMsg->sn_header.sn_type)); if (notifMsg->sn_header.sn_type == SCTP_ASSOC_CHANGE) { TRACE_MSG("SCTP_ASSOC_CHANGE\n"); if (notifMsg->sn_assoc_change.sac_state == SCTP_COMM_UP) { TRACE_MSG("SCTP_COMM_UP\n"); sctpstate = SCTP_UP; sipp_sctp_peer_params(); /* Send SCTP message right after association is up */ ss_congested = false; flush(); return -2; } else { TRACE_MSG("else: %d\n", notifMsg->sn_assoc_change.sac_state); return 0; } } else if (notifMsg->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { TRACE_MSG("SCTP_SHUTDOWN_EVENT\n"); return 0; } return -2; } void set_multihome_addr(SIPpSocket* socket, int port) { if (strlen(multihome_ip)>0) { struct sockaddr_storage secondaryaddress; if (gai_getsockaddr(&secondaryaddress, multihome_ip, port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Can't get multihome IP address in getaddrinfo, multihome_ip='%s'", multihome_ip); } int ret = sctp_bindx(socket->ss_fd, (struct sockaddr *) &secondaryaddress, 1, SCTP_BINDX_ADD_ADDR); if (ret < 0) { WARNING("Can't bind to multihome address, errno='%d'", errno); } } } #endif /* Pull up to tcp_readsize data bytes out of the socket into our local buffer. */ int SIPpSocket::empty() { int readsize=0; if (ss_transport == T_UDP || ss_transport == T_SCTP) { readsize = SIPP_MAX_MSG_SIZE; } else { readsize = tcp_readsize; } struct socketbuf *socketbuf; char *buffer; int ret = -1; /* Where should we start sending packets to, ideally we should begin to parse * the Via, Contact, and Route headers. But for now SIPp always sends to the * host specified on the command line; or for UAS mode to the address that * sent the last message. */ sipp_socklen_t addrlen = sizeof(struct sockaddr_storage); buffer = (char *)malloc(readsize); if (!buffer) { ERROR("Could not allocate memory for read!"); } socketbuf = alloc_socketbuf(buffer, readsize, NO_COPY, NULL); switch(ss_transport) { case T_TCP: case T_UDP: ret = recvfrom(ss_fd, buffer, readsize, 0, (struct sockaddr *)&socketbuf->addr, &addrlen); break; case T_TLS: #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) ret = SSL_read(ss_ssl, buffer, readsize); /* XXX: Check for clean shutdown. */ #else ERROR("TLS support is not enabled!"); #endif break; case T_SCTP: #ifdef USE_SCTP struct sctp_sndrcvinfo recvinfo; memset(&recvinfo, 0, sizeof(recvinfo)); int msg_flags = 0; ret = sctp_recvmsg(ss_fd, (void*)buffer, readsize, (struct sockaddr *) &socketbuf->addr, &addrlen, &recvinfo, &msg_flags); if (MSG_NOTIFICATION & msg_flags) { errno = 0; handleSCTPNotify(buffer); ret = -2; } #else ERROR("SCTP support is not enabled!"); #endif break; } if (ret <= 0) { free_socketbuf(socketbuf); return ret; } socketbuf->len = ret; buffer_read(socketbuf); /* Do we have a complete SIP message? */ if (!ss_msglen) { if (int msg_len = check_for_message()) { ss_msglen = msg_len; pending_messages++; } } return ret; } void SIPpSocket::invalidate() { unsigned pollidx; if (ss_invalid) { return; } #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) if (SSL *ssl = ss_ssl) { SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_free(ssl); } #endif /* In some error conditions, the socket FD has already been closed - if it hasn't, do so now. */ if (ss_fd != -1) { #ifdef HAVE_EPOLL int rc = epoll_ctl(epollfd, EPOLL_CTL_DEL, ss_fd, NULL); if (rc == -1) { WARNING_NO("Failed to delete FD from epoll"); } #endif } if (ss_fd != -1 && ss_fd != stdin_fileno) { if (ss_transport != T_UDP) { if (shutdown(ss_fd, SHUT_RDWR) < 0) { WARNING_NO("Failed to shutdown socket %d", ss_fd); } } #ifdef USE_SCTP if (ss_transport == T_SCTP && !gracefulclose) { struct linger ling = {1, 0}; if (setsockopt(ss_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0) { WARNING("Unable to set SO_LINGER option for SCTP close"); } } #endif if (::close(ss_fd) < 0) { WARNING_NO("Failed to close socket %d", ss_fd); } } if ((pollidx = ss_pollidx) >= pollnfds) { ERROR("Pollset error: index %d is greater than number of fds %d!", pollidx, pollnfds); } ss_fd = -1; ss_invalid = true; ss_pollidx = -1; /* Adds call sockets in the array */ assert(pollnfds > 0); pollnfds--; #ifdef HAVE_EPOLL if (pollidx < pollnfds) { epollfiles[pollidx] = epollfiles[pollnfds]; epollfiles[pollidx].data.u32 = pollidx; if (sockets[pollnfds]->ss_fd != -1) { int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, sockets[pollnfds]->ss_fd, &epollfiles[pollidx]); if ((rc == -1) && (errno != EPERM)) { // Ignore "Operation not supported" errors - // otherwise we get log spam when redirecting stdout // to /dev/null WARNING_NO("Failed to update FD within epoll"); } } } #else pollfiles[pollidx] = pollfiles[pollnfds]; #endif /* If unequal, move the last valid socket here. */ if (pollidx != pollnfds) { sockets[pollidx] = sockets[pollnfds]; sockets[pollidx]->ss_pollidx = pollidx; } sockets[pollnfds] = NULL; if (ss_msglen) { pending_messages--; } #ifdef USE_SCTP if (ss_transport == T_SCTP) { sctpstate = SCTP_DOWN; } #endif } void SIPpSocket::abort() { /* Disable linger - we'll send a RST when we close. */ struct linger flush; flush.l_onoff = 1; flush.l_linger = 0; setsockopt(ss_fd, SOL_SOCKET, SO_LINGER, &flush, sizeof(flush)); /* Mark the socket as non-blocking. It's not clear whether this is required but can't hurt. */ int flags = fcntl(ss_fd, F_GETFL, 0); fcntl(ss_fd, F_SETFL, flags | O_NONBLOCK); int count = --ss_count; if (count == 0) { invalidate(); sockets_pending_reset.erase(this); delete this; } else { ss_fd = -1; } } void SIPpSocket::close() { int count = --ss_count; if (count == 0) { invalidate(); sockets_pending_reset.erase(this); delete this; } } ssize_t SIPpSocket::read_message(char *buf, size_t len, struct sockaddr_storage *src) { size_t avail; if (!ss_msglen) return 0; if (ss_msglen > len) ERROR("There is a message waiting in sockfd(%d) that is bigger (%zu bytes) than the read size.", ss_fd, ss_msglen); len = ss_msglen; avail = ss_in->len - ss_in->offset; if (avail > len) { avail = len; } memcpy(buf, ss_in->buf + ss_in->offset, avail); memcpy(src, &ss_in->addr, sizeof(ss_in->addr)); /* Update our buffer and return value. */ buf[avail] = '\0'; /* For CMD Message the escape char is the end of message */ if ((ss_control) && buf[avail-1] == 27) buf[avail-1] = '\0'; ss_in->offset += avail; /* Have we emptied the buffer? */ if (ss_in->offset == ss_in->len) { struct socketbuf *next = ss_in->next; free_socketbuf(ss_in); ss_in = next; } if (int msg_len = check_for_message()) { ss_msglen = msg_len; } else { ss_msglen = 0; pending_messages--; } return avail; } void process_message(SIPpSocket *socket, char *msg, ssize_t msg_size, struct sockaddr_storage *src) { // TRACE_MSG(" msg_size %d and pollset_index is %d \n", msg_size, pollset_index)); if (msg_size <= 0) { return; } if (sipMsgCheck(msg, socket) == false) { if (msg_size == 4 && (memcmp(msg, "\r\n\r\n", 4) == 0 || memcmp(msg, "\x00\x00\x00\x00", 4) == 0)) { /* Common keepalives */; } else { WARNING("non SIP message discarded: \"%.*s\" (%zu)", (int)msg_size, msg, msg_size); } return; } const char *call_id = get_trimmed_call_id(msg); if (call_id[0] == '\0') { WARNING("SIP message without Call-ID discarded"); return; } listener *listener_ptr = get_listener(call_id); struct timeval currentTime; GET_TIME (¤tTime); if (useShortMessagef == 1) { TRACE_SHORTMSG("%s\tR\t%s\tCSeq:%s\t%s\n", CStat::formatTime(¤tTime), call_id, get_header_content(msg, "CSeq:"), get_first_line(msg)); } if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "%s %smessage received [%zu] bytes :\n\n%s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(socket->ss_transport), socket->ss_control ? "control " : "", msg_size, msg); } if (!listener_ptr) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_B || thirdPartyMode == MODE_3PCC_A_PASSIVE || thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_SLAVE) { // Adding a new OUTGOING call ! main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); call *new_ptr = new call(call_id, local_ip_is_ipv6, 0, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); if (!new_ptr) { ERROR("Out of memory allocating a call!"); } outbound_congestion = false; if ((socket != main_socket) && (socket != tcp_multiplex) && (socket != localTwinSippSocket) && (socket != twinSippSocket) && (!is_a_local_socket(socket))) { new_ptr->associate_socket(socket); socket->ss_count++; } else { /* We need to hook this call up to a real *call* socket. */ if (!multisocket) { switch(transport) { case T_UDP: new_ptr->associate_socket(main_socket); main_socket->ss_count++; break; case T_TCP: case T_SCTP: case T_TLS: new_ptr->associate_socket(tcp_multiplex); tcp_multiplex->ss_count++; break; } } } listener_ptr = new_ptr; } else if (creationMode == MODE_SERVER) { if (quitting >= 1) { CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); TRACE_MSG("Discarded message for new calls while quitting\n"); return; } // Adding a new INCOMING call ! main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); listener_ptr = new call(call_id, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src); if (!listener_ptr) { ERROR("Out of memory allocating a call!"); } } else { // mode != from SERVER and 3PCC Controller B // This is a message that is not relating to any known call if (ooc_scenario) { if (!get_reply_code(msg)) { char *msg_start = strdup(msg); char *msg_start_end = msg_start; while (!isspace(*msg_start_end) && (*msg_start_end != '\0')) { msg_start_end++; } *msg_start_end = '\0'; ooc_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); WARNING("Received out-of-call %s message, using the out-of-call scenario", msg_start); free(msg_start); /* This should have the real address that the message came from. */ call *call_ptr = new call(ooc_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false); if (!call_ptr) { ERROR("Out of memory allocating a call!"); } CStat::globalStat(CStat::E_AUTO_ANSWERED); call_ptr->process_incoming(msg, src); } else { /* We received a response not relating to any known call */ /* Do nothing, even if in auto answer mode */ CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); } } else if (auto_answer && ((strstr(msg, "INFO") == msg) || (strstr(msg, "NOTIFY") == msg) || (strstr(msg, "OPTIONS") == msg) || (strstr(msg, "UPDATE") == msg))) { // If auto answer mode, try to answer the incoming message // with automaticResponseMode // call is discarded before exiting the block if (!get_reply_code(msg)) { aa_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); /* This should have the real address that the message came from. */ call *call_ptr = new call(aa_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false); if (!call_ptr) { ERROR("Out of memory allocating a call!"); } CStat::globalStat(CStat::E_AUTO_ANSWERED); call_ptr->process_incoming(msg, src); } else { fprintf(stderr, "%s", msg); /* We received a response not relating to any known call */ /* Do nothing, even if in auto answer mode */ CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); } } else { CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); WARNING("Discarding message which can't be mapped to a known SIPp call:\n%s", msg); } } } /* If the call was not created above, we just drop this message. */ if (!listener_ptr) { return; } if ((socket == localTwinSippSocket) || (socket == twinSippSocket) || (is_a_local_socket(socket))) { listener_ptr -> process_twinSippCom(msg); } else { listener_ptr -> process_incoming(msg, src); } } SIPpSocket::SIPpSocket(bool use_ipv6, int transport, int fd, int accepting): ss_count(1), ss_ipv6(use_ipv6), ss_transport(transport), ss_control(false), ss_fd(fd), ss_bind_port(0), ss_comp_state(NULL), ss_changed_dest(false), ss_congested(false), ss_invalid(false), ss_in(NULL), ss_out(NULL), ss_out_tail(NULL), ss_msglen(0) { /* Initialize all sockets with our destination address. */ memcpy(&ss_dest, &remote_sockaddr, sizeof(ss_dest)); #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) ss_ssl = NULL; if (transport == T_TLS) { int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); if ((ss_bio = BIO_new_socket(fd, BIO_NOCLOSE)) == NULL) { ERROR("Unable to create BIO object:Problem with BIO_new_socket()"); } if (!(ss_ssl = (accepting ? SSL_new_server() : SSL_new_client()))) { ERROR("Unable to create SSL object : Problem with SSL_new()"); } SSL_set_bio(ss_ssl, ss_bio, ss_bio); } #endif /* Store this socket in the tables. */ ss_pollidx = pollnfds++; sockets[ss_pollidx] = this; #ifdef HAVE_EPOLL epollfiles[ss_pollidx].data.u32 = ss_pollidx; epollfiles[ss_pollidx].events = EPOLLIN; int rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { if (errno == EPERM) { // Attempted to use epoll on a file that does not support // it - this may happen legitimately when stdin/stdout is // redirected to /dev/null, so don't warn } else { ERROR_NO("Failed to add FD to epoll"); } } #else pollfiles[ss_pollidx].fd = ss_fd; pollfiles[ss_pollidx].events = POLLIN | POLLERR; pollfiles[ss_pollidx].revents = 0; #endif } static SIPpSocket* sipp_allocate_socket(bool use_ipv6, int transport, int fd) { return new SIPpSocket(use_ipv6, transport, fd, 0); } static int socket_fd(bool use_ipv6, int transport) { int socket_type = -1; int protocol = 0; int fd; switch(transport) { case T_UDP: socket_type = SOCK_DGRAM; protocol = IPPROTO_UDP; break; case T_SCTP: #ifndef USE_SCTP ERROR("You do not have SCTP support enabled!"); #else socket_type = SOCK_STREAM; protocol = IPPROTO_SCTP; #endif break; case T_TLS: #ifndef USE_TLS ERROR("You do not have TLS support enabled!"); #endif case T_TCP: socket_type = SOCK_STREAM; protocol = IPPROTO_TCP; break; } if ((fd = socket(use_ipv6 ? AF_INET6 : AF_INET, socket_type, protocol))== -1) { ERROR_NO("Unable to get a %s socket (3)", TRANSPORT_TO_STRING(transport)); } return fd; } SIPpSocket *new_sipp_socket(bool use_ipv6, int transport) { SIPpSocket *ret; int fd = socket_fd(use_ipv6, transport); ret = sipp_allocate_socket(use_ipv6, transport, fd); if (!ret) { close(fd); ERROR("Could not allocate new socket structure!"); } return ret; } SIPpSocket* SIPpSocket::new_sipp_call_socket(bool use_ipv6, int transport, bool *existing) { SIPpSocket *sock = NULL; static int next_socket; if (pollnfds >= max_multi_socket) { // we must take the main socket into account /* Find an existing socket that matches transport and ipv6 parameters. */ int first = next_socket; do { int test_socket = next_socket; next_socket = (next_socket + 1) % pollnfds; if (sockets[test_socket]->ss_call_socket) { /* Here we need to check that the address is the default. */ if (sockets[test_socket]->ss_ipv6 != use_ipv6) { continue; } if (sockets[test_socket]->ss_transport != transport) { continue; } if (sockets[test_socket]->ss_changed_dest) { continue; } sock = sockets[test_socket]; sock->ss_count++; *existing = true; break; } } while (next_socket != first); if (next_socket == first) { ERROR("Could not find an existing call socket to re-use!"); } } else { sock = new_sipp_socket(use_ipv6, transport); sock->ss_call_socket = true; *existing = false; } return sock; } SIPpSocket* SIPpSocket::accept() { SIPpSocket *ret; struct sockaddr_storage remote_sockaddr; int fd; sipp_socklen_t addrlen = sizeof(remote_sockaddr); if ((fd = ::accept(ss_fd, (struct sockaddr *)&remote_sockaddr, &addrlen))== -1) { ERROR("Unable to accept on a %s socket: %s", TRANSPORT_TO_STRING(transport), strerror(errno)); } #if defined(__SUNOS) if (fd < 256) { int newfd = fcntl(fd, F_DUPFD, 256); if (newfd <= 0) { // Typically, (24)(Too many open files) is the error here WARNING("Unable to get a different %s socket, errno=%d(%s)", TRANSPORT_TO_STRING(transport), errno, strerror(errno)); // Keep the original socket fd. newfd = fd; } else { ::close(fd); } fd = newfd; } #endif ret = new SIPpSocket(ss_ipv6, ss_transport, fd, 1); if (!ret) { ::close(fd); ERROR_NO("Could not allocate new socket!"); } /* We should connect back to the address which connected to us if we * experience a TCP failure. */ memcpy(&ret->ss_dest, &remote_sockaddr, sizeof(ret->ss_dest)); if (ret->ss_transport == T_TLS) { #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) int rc; int i = 0; while ((rc = SSL_accept(ret->ss_ssl)) < 0) { int err = SSL_get_error(ret->ss_ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_accept failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } ERROR("Error in SSL_accept: %s", SSL_error_string(err, rc)); break; } #else ERROR("You need to compile SIPp with TLS support"); #endif } return ret; } int sipp_bind_socket(SIPpSocket *socket, struct sockaddr_storage *saddr, int *port) { int ret; int len; #ifdef USE_SCTP if (transport == T_SCTP && multisocket == 1 && port && *port == -1) { sockaddr_update_port(saddr, 0); } #endif if (socket->ss_ipv6) { len = sizeof(struct sockaddr_in6); } else { len = sizeof(struct sockaddr_in); } if ((ret = ::bind(socket->ss_fd, (sockaddr *)saddr, len))) { return ret; } if (!port) { return 0; } if ((ret = getsockname(socket->ss_fd, (sockaddr *)saddr, (sipp_socklen_t *) &len))) { return ret; } if (socket->ss_ipv6) { socket->ss_port = ntohs((short)((_RCAST(struct sockaddr_in6 *, saddr))->sin6_port)); } else { socket->ss_port = ntohs((short)((_RCAST(struct sockaddr_in *, saddr))->sin_port)); } *port = socket->ss_port; #ifdef USE_SCTP if (transport == T_SCTP) { bool isany = false; if (socket->ss_ipv6) { if (memcmp(&(_RCAST(struct sockaddr_in6 *, saddr)->sin6_addr), &in6addr_any, sizeof(in6_addr)) == 0) isany = true; } else { isany = (_RCAST(struct sockaddr_in *, saddr)->sin_addr.s_addr == INADDR_ANY); } if (!isany) { set_multihome_addr(socket, *port); } } #endif return 0; } void SIPpSocket::set_bind_port(int bind_port) { ss_bind_port = bind_port; } int SIPpSocket::connect(struct sockaddr_storage* dest) { if (dest) { memcpy(&ss_dest, dest, sizeof(*dest)); } int ret; assert(ss_transport == T_TCP || ss_transport == T_TLS || ss_transport == T_SCTP); if (ss_transport == T_TCP || ss_transport == T_TLS) { struct sockaddr_storage with_optional_port; int port = -1; memcpy(&with_optional_port, &local_sockaddr, sizeof(struct sockaddr_storage)); if (local_ip_is_ipv6) { (_RCAST(struct sockaddr_in6*, &with_optional_port))->sin6_port = htons(ss_bind_port); } else { (_RCAST(struct sockaddr_in*, &with_optional_port))->sin_port = htons(ss_bind_port); } sipp_bind_socket(this, &with_optional_port, &port); #ifdef USE_SCTP } else if (ss_transport == T_SCTP) { int port = -1; sipp_bind_socket(this, &local_sockaddr, &port); #endif } int flags = fcntl(ss_fd, F_GETFL, 0); fcntl(ss_fd, F_SETFL, flags | O_NONBLOCK); errno = 0; ret = ::connect(ss_fd, _RCAST(struct sockaddr *, &ss_dest), socklen_from_addr(&ss_dest)); if (ret < 0) { if (errno == EINPROGRESS) { /* Block this socket until the connect completes - this is very similar to entering congestion, but we don't want to increment congestion statistics. */ enter_congestion(0); nb_net_cong--; } else { return ret; } } fcntl(ss_fd, F_SETFL, flags); if (ss_transport == T_TLS) { #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) int rc; int i = 0; while ((rc = SSL_connect(ss_ssl)) < 0) { int err = SSL_get_error(ss_ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_connect failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } WARNING("Error in SSL connection: %s", SSL_error_string(err, rc)); invalidate(); return err; } #else ERROR("You need to compile SIPp with TLS support"); #endif } #ifdef USE_SCTP if (ss_transport == T_SCTP) { sctpstate = SCTP_CONNECTING; } #endif return 0; } int SIPpSocket::reconnect() { if ((!ss_invalid) && (ss_fd != -1)) { WARNING("When reconnecting socket, already have file descriptor %d", ss_fd); abort(); } ss_fd = socket_fd(ss_ipv6, ss_transport); if (ss_fd == -1) { ERROR_NO("Could not obtain new socket: "); } if (ss_invalid) { #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) ss_ssl = NULL; if (transport == T_TLS) { if ((ss_bio = BIO_new_socket(ss_fd, BIO_NOCLOSE)) == NULL) { ERROR("Unable to create BIO object:Problem with BIO_new_socket()"); } if (!(ss_ssl = SSL_new_client())) { ERROR("Unable to create SSL object : Problem with SSL_new()"); } SSL_set_bio(ss_ssl, ss_bio, ss_bio); } #endif /* Store this socket in the tables. */ ss_pollidx = pollnfds++; sockets[ss_pollidx] = this; #ifdef HAVE_EPOLL epollfiles[ss_pollidx].data.u32 = ss_pollidx; epollfiles[ss_pollidx].events = EPOLLIN; #else pollfiles[ss_pollidx].fd = ss_fd; pollfiles[ss_pollidx].events = POLLIN | POLLERR; pollfiles[ss_pollidx].revents = 0; #endif ss_invalid = false; } #ifdef HAVE_EPOLL int rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { ERROR_NO("Failed to add FD to epoll"); } #endif return connect(); } /*************************** I/O functions ***************************/ /* Allocate a socket buffer. */ struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy, struct sockaddr_storage *dest) { struct socketbuf *socketbuf; socketbuf = (struct socketbuf *)malloc(sizeof(struct socketbuf)); if (!socketbuf) { ERROR("Could not allocate socket buffer!"); } memset(socketbuf, 0, sizeof(struct socketbuf)); if (copy) { socketbuf->buf = (char *)malloc(size); if (!socketbuf->buf) { ERROR("Could not allocate socket buffer data!"); } memcpy(socketbuf->buf, buffer, size); } else { socketbuf->buf = buffer; } socketbuf->len = size; socketbuf->offset = 0; if (dest) { memcpy(&socketbuf->addr, dest, sizeof(*dest)); } socketbuf->next = NULL; return socketbuf; } /* Free a poll buffer. */ void free_socketbuf(struct socketbuf *socketbuf) { free(socketbuf->buf); free(socketbuf); } #ifdef USE_SCTP void SIPpSocket::sipp_sctp_peer_params() { if (heartbeat > 0 || pathmaxret > 0) { struct sctp_paddrparams peerparam; memset(&peerparam, 0, sizeof(peerparam)); sockaddr* addresses; #ifdef __SUNOS /* Sun takes a void** instead of a struct sockaddr** */ int addresscount = sctp_getpaddrs(ss_fd, 0, (void**)&addresses); #else int addresscount = sctp_getpaddrs(ss_fd, 0, &addresses); #endif if (addresscount < 1) WARNING("sctp_getpaddrs, errno=%d", errno); for (int i = 0; i < addresscount; i++) { memset(&peerparam.spp_address, 0, sizeof(peerparam.spp_address)); struct sockaddr_storage* peeraddress = (struct sockaddr_storage*) &addresses[i]; memcpy(&peerparam.spp_address, peeraddress, sizeof(*peeraddress)); peerparam.spp_hbinterval = heartbeat; peerparam.spp_pathmaxrxt = pathmaxret; if (heartbeat > 0) peerparam.spp_flags = SPP_HB_ENABLE; if (pmtu > 0) { peerparam.spp_pathmtu = pmtu; peerparam.spp_flags |= SPP_PMTUD_DISABLE; } if (setsockopt(ss_fd, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &peerparam, sizeof(peerparam)) == -1) { sctp_freepaddrs(addresses); WARNING("setsockopt(SCTP_PEER_ADDR_PARAMS) failed, errno=%d", errno); } } sctp_freepaddrs(addresses); } } #endif void sipp_customize_socket(SIPpSocket *socket) { unsigned int buffsize = buff_size; /* Allows fast TCP reuse of the socket */ if (socket->ss_transport == T_TCP || socket->ss_transport == T_TLS || socket->ss_transport == T_SCTP) { int sock_opt = 1; if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1) { ERROR_NO("setsockopt(SO_REUSEADDR) failed"); } #ifdef USE_SCTP if (socket->ss_transport == T_SCTP) { struct sctp_event_subscribe event; memset(&event, 0, sizeof(event)); event.sctp_data_io_event = 1; event.sctp_association_event = 1; event.sctp_shutdown_event = 1; if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) == -1) { ERROR_NO("setsockopt(SCTP_EVENTS) failed, errno=%d", errno); } if (assocmaxret > 0) { struct sctp_assocparams associnfo; memset(&associnfo, 0, sizeof(associnfo)); associnfo.sasoc_asocmaxrxt = assocmaxret; if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_ASSOCINFO, &associnfo, sizeof(associnfo)) == -1) { WARNING("setsockopt(SCTP_ASSOCINFO) failed, errno=%d", errno); } } if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_NODELAY, (void *)&sock_opt, sizeof (sock_opt)) == -1) { WARNING("setsockopt(SCTP_NODELAY) failed, errno=%d", errno); } } #endif #ifndef SOL_TCP #define SOL_TCP 6 #endif if (socket->ss_transport != T_SCTP) { if (setsockopt(socket->ss_fd, SOL_TCP, TCP_NODELAY, (void *)&sock_opt, sizeof (sock_opt)) == -1) { { ERROR_NO("setsockopt(TCP_NODELAY) failed"); } } } { struct linger linger; linger.l_onoff = 1; linger.l_linger = 1; if (setsockopt (socket->ss_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)) < 0) { ERROR_NO("Unable to set SO_LINGER option"); } } } /* Increase buffer sizes for this sockets */ if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize))) { ERROR_NO("Unable to set socket sndbuf"); } buffsize = buff_size; if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_RCVBUF, &buffsize, sizeof(buffsize))) { ERROR_NO("Unable to set socket rcvbuf"); } } /* This socket is congested, mark it as such and add it to the poll files. */ int SIPpSocket::enter_congestion(int again) { if (!ss_congested) { nb_net_cong++; } ss_congested = true; TRACE_MSG("Problem %s on socket %d and poll_idx is %d \n", again == EWOULDBLOCK ? "EWOULDBLOCK" : "EAGAIN", ss_fd, ss_pollidx); #ifdef HAVE_EPOLL epollfiles[ss_pollidx].events |= EPOLLOUT; int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { WARNING_NO("Failed to set EPOLLOUT"); } #else pollfiles[ss_pollidx].events |= POLLOUT; #endif #ifdef USE_SCTP if (ss_transport == T_SCTP && sctpstate == SCTP_CONNECTING) return 0; #endif return -1; } int SIPpSocket::write_error(int ret) { const char *errstring = strerror(errno); #ifndef EAGAIN int again = (errno == EWOULDBLOCK) ? errno : 0; #else int again = ((errno == EAGAIN) || (errno == EWOULDBLOCK)) ? errno : 0; /* Scrub away EAGAIN from the rest of the code. */ if (errno == EAGAIN) { errno = EWOULDBLOCK; } #endif if (again) { return enter_congestion(again); } if ((ss_transport == T_TCP || ss_transport == T_SCTP) && errno == EPIPE) { nb_net_send_errors++; sockets_pending_reset.insert(this); abort(); if (reconnect_allowed()) { WARNING("Broken pipe on TCP connection, remote peer " "probably closed the socket"); } else { ERROR("Broken pipe on TCP connection, remote peer " "probably closed the socket"); } return -1; } #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) if (ss_transport == T_TLS) { errstring = SSL_error_string(SSL_get_error(ss_ssl, ret), ret); } #endif WARNING("Unable to send %s message: %s", TRANSPORT_TO_STRING(ss_transport), errstring); nb_net_send_errors++; return -1; } int SIPpSocket::read_error(int ret) { const char *errstring = strerror(errno); #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) if (ss_transport == T_TLS) { int err = SSL_get_error(ss_ssl, ret); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { /* This is benign - we just need to wait for the socket to be * readable/writable again, which will happen naturally as part * of the poll/epoll loop. */ WARNING("SSL_read failed with error: %s. Retrying...", SSL_error_string(err, ret)); return 1; } } #endif assert(ret <= 0); #ifdef EAGAIN /* Scrub away EAGAIN from the rest of the code. */ if (errno == EAGAIN) { errno = EWOULDBLOCK; } #endif /* We have only non-blocking reads, so this should not occur. The OpenSSL * functions don't set errno, though, so this check doesn't make sense * for TLS sockets. */ if (ret < 0 && ss_transport != T_TLS) { assert(errno != EAGAIN); } if (ss_transport == T_TCP || ss_transport == T_TLS) { if (ret == 0) { /* The remote side closed the connection. */ if (ss_control) { if (localTwinSippSocket) localTwinSippSocket->close(); if (extendedTwinSippMode) { close_peer_sockets(); close_local_sockets(); free_peer_addr_map(); WARNING("One of the twin instances has ended -> exiting"); quitting += 20; } else if (twinSippMode) { if (twinSippSocket) twinSippSocket->close(); if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { WARNING("3PCC controller A has ended -> exiting"); quitting += 20; } else { quitting = 1; } } } else { /* The socket was closed "cleanly", but we may have calls that need to * be destroyed. Also, if these calls are not complete, and attempt to * send again we may "ressurect" the socket by reconnecting it.*/ invalidate(); if (reset_close) { close_calls(); } } return 0; } sockets_pending_reset.insert(this); abort(); nb_net_recv_errors++; if (reconnect_allowed()) { WARNING("Error on TCP connection, remote peer probably closed the socket: %s", errstring); } else { ERROR("Error on TCP connection, remote peer probably closed the socket: %s", errstring); } return -1; } WARNING("Unable to receive %s message: %s", TRANSPORT_TO_STRING(ss_transport), errstring); nb_net_recv_errors++; return -1; } void SIPpSocket::buffer_write(const char *buffer, size_t len, struct sockaddr_storage *dest) { struct socketbuf *buf = ss_out; if (!buf) { ss_out = alloc_socketbuf(const_cast(buffer), len, DO_COPY, dest); /* NO BUG BECAUSE OF DO_COPY */ ss_out_tail = ss_out; TRACE_MSG("Added first buffered message to socket %d\n", ss_fd); return; } ss_out_tail->next = alloc_socketbuf(const_cast(buffer), len, DO_COPY, dest); /* NO BUG BECAUSE OF DO_COPY */ ss_out_tail = ss_out_tail->next; TRACE_MSG("Appended buffered message to socket %d\n", ss_fd); } void SIPpSocket::buffer_read(struct socketbuf *newbuf) { struct socketbuf *buf = ss_in; struct socketbuf *prev = buf; if (!buf) { ss_in = newbuf; return; } while (buf->next) { prev = buf; buf = buf->next; } prev->next = newbuf; } #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) static int send_nowait_tls(SSL* ssl, const void* msg, int len, int /*flags*/) { int initial_fd_flags; int rc; int fd; int fd_flags; int i = 0; if ((fd = SSL_get_fd(ssl)) == -1) { return -1; } fd_flags = fcntl(fd, F_GETFL, NULL); initial_fd_flags = fd_flags; fd_flags |= O_NONBLOCK; fcntl(fd, F_SETFL, fd_flags); while ((rc = SSL_write(ssl, msg, len)) < 0) { int err = SSL_get_error(ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_write failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } return rc; } if (rc == 0) { return rc; } fcntl(fd, F_SETFL, initial_fd_flags); return rc; } #endif static int send_nowait(int s, const void* msg, int len, int flags) { #if defined(MSG_DONTWAIT) && !defined(__SUNOS) return send(s, msg, len, flags | MSG_DONTWAIT); #else int fd_flags = fcntl(s, F_GETFL , NULL); int initial_fd_flags; int rc; initial_fd_flags = fd_flags; // fd_flags &= ~O_ACCMODE; // Remove the access mode from the value fd_flags |= O_NONBLOCK; fcntl(s, F_SETFL , fd_flags); rc = send(s, msg, len, flags); fcntl(s, F_SETFL , initial_fd_flags); return rc; #endif } #ifdef USE_SCTP int send_sctp_nowait(int s, const void *msg, int len, int flags) { struct sctp_sndrcvinfo sinfo; memset(&sinfo, 0, sizeof(sinfo)); sinfo.sinfo_flags = SCTP_UNORDERED; // according to RFC4168 5.1 sinfo.sinfo_stream = 0; #if defined(MSG_DONTWAIT) && !defined(__SUNOS) return sctp_send(s, msg, len, &sinfo, flags | MSG_DONTWAIT); #else int fd_flags = fcntl(s, F_GETFL, NULL); int initial_fd_flags; int rc; initial_fd_flags = fd_flags; fd_flags |= O_NONBLOCK; fcntl(s, F_SETFL , fd_flags); rc = sctp_send(s, msg, len, &sinfo, flags); fcntl(s, F_SETFL, initial_fd_flags); return rc; #endif } #endif ssize_t SIPpSocket::write_primitive(const char* buffer, size_t len, struct sockaddr_storage* dest) { ssize_t rc; /* Refuse to write to invalid sockets. */ if (ss_invalid) { WARNING("Returning EPIPE on invalid socket: %p (%d)", _RCAST(void*, this), ss_fd); errno = EPIPE; return -1; } /* Always check congestion before sending. */ if (ss_congested) { errno = EWOULDBLOCK; return -1; } switch(ss_transport) { case T_TLS: #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) rc = send_nowait_tls(ss_ssl, buffer, len, 0); #else errno = EOPNOTSUPP; rc = -1; #endif break; case T_SCTP: #ifdef USE_SCTP TRACE_MSG("socket_write_primitive %d\n", sctpstate); if (sctpstate == SCTP_DOWN) { errno = EPIPE; return -1; } else if (sctpstate == SCTP_CONNECTING) { errno = EWOULDBLOCK; return -1; } rc = send_sctp_nowait(ss_fd, buffer, len, 0); #else errno = EOPNOTSUPP; rc = -1; #endif break; case T_TCP: rc = send_nowait(ss_fd, buffer, len, 0); break; case T_UDP: if (compression) { static char comp_msg[SIPP_MAX_MSG_SIZE]; strncpy(comp_msg, buffer, sizeof(comp_msg) - 1); if (comp_compress(&ss_comp_state, comp_msg, (unsigned int *) &len) != COMP_OK) { ERROR("Compression plugin error"); } buffer = (char *)comp_msg; TRACE_MSG("---\nCompressed message len: %zu\n", len); } rc = sendto(ss_fd, buffer, len, 0, _RCAST(struct sockaddr*, dest), socklen_from_addr(dest)); break; default: ERROR("Internal error, unknown transport type %d", ss_transport); } return rc; } /* Flush any output buffers for this socket. */ int SIPpSocket::flush() { struct socketbuf *buf; int ret; while ((buf = ss_out)) { ssize_t size = buf->len - buf->offset; ret = write_primitive(buf->buf + buf->offset, size, &buf->addr); TRACE_MSG("Wrote %d of %zu bytes in an output buffer.\n", ret, size); if (ret == size) { /* Everything is great, throw away this buffer. */ ss_out = buf->next; free_socketbuf(buf); } else if (ret <= 0) { /* Handle connection closes and errors. */ return write_error(ret); } else { /* We have written more of the partial buffer. */ buf->offset += ret; errno = EWOULDBLOCK; enter_congestion(EWOULDBLOCK); return -1; } } return 0; } /* Write data to a socket. */ int SIPpSocket::write(const char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest) { int rc; if (ss_out) { rc = flush(); TRACE_MSG("Attempted socket flush returned %d\r\n", rc); if (rc < 0) { if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) { buffer_write(buffer, len, dest); return len; } else { return rc; } } } rc = write_primitive(buffer, len, dest); struct timeval currentTime; GET_TIME (¤tTime); if (rc == len) { /* Everything is great. */ if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "%s %smessage sent (%zu bytes):\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), ss_control ? "control " : "", len, (int)len, buffer); } if (useShortMessagef == 1) { char *msg = strdup(buffer); const char *call_id = get_trimmed_call_id(msg); TRACE_SHORTMSG("%s\tS\t%s\tCSeq:%s\t%s\n", CStat::formatTime(¤tTime), call_id, get_header_content(msg, "CSeq:"), get_first_line(msg)); free(msg); } } else if (rc <= 0) { if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) { buffer_write(buffer, len, dest); enter_congestion(errno); return len; } if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "Error sending %s message:\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), (int)len, buffer); } return write_error(errno); } else { /* We have a truncated message, which must be handled internally to the write function. */ if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "Truncation sending %s message (%d of %zu sent):\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), rc, len, (int)len, buffer); } buffer_write(buffer + rc, len - rc, dest); enter_congestion(errno); } return rc; } bool reconnect_allowed() { if (reset_number == -1) { return true; } return (reset_number > 0); } void SIPpSocket::reset_connection() { if (!reconnect_allowed()) { ERROR_NO("Max number of reconnections reached"); } if (reset_number != -1) { reset_number--; } if (reset_close) { WARNING("Closing calls, because of TCP reset or close!"); close_calls(); } /* Sleep for some period of time before the reconnection. */ usleep(1000 * reset_sleep); if (reconnect() < 0) { WARNING_NO("Could not reconnect TCP socket"); close_calls(); } else { WARNING("Socket required a reconnection."); } } /* Close just those calls for a given socket (e.g., if the remote end closes * the connection. */ void SIPpSocket::close_calls() { owner_list *owners = get_owners_for_socket(this); owner_list::iterator owner_it; socketowner *owner_ptr = NULL; for (owner_it = owners->begin(); owner_it != owners->end(); owner_it++) { owner_ptr = *owner_it; if (owner_ptr) { owner_ptr->tcpClose(); } } delete owners; } int open_connections() { int status=0; int family_hint = PF_UNSPEC; local_port = 0; if (!strlen(remote_host)) { if ((sendMode != MODE_SERVER)) { ERROR("Missing remote host parameter. This scenario requires it"); } } else { int temp_remote_port; get_host_and_port(remote_host, remote_host, &temp_remote_port); if (temp_remote_port != 0) { remote_port = temp_remote_port; } /* Resolving the remote IP */ { fprintf(stderr, "Resolving remote host '%s'... ", remote_host); /* FIXME: add DNS SRV support using liburli? */ if (gai_getsockaddr(&remote_sockaddr, remote_host, remote_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", remote_host); } get_inet_address(&remote_sockaddr, remote_ip, sizeof(remote_ip)); family_hint = remote_sockaddr.ss_family; if (remote_sockaddr.ss_family == AF_INET) { strcpy(remote_ip_w_brackets, remote_ip); } else { sprintf(remote_ip_w_brackets, "[%.39s]", remote_ip); } fprintf(stderr, "Done.\n"); } } { /* Yuck. Populate local_sockaddr with "our IP" first, and then * replace it with INADDR_ANY if we did not request a specific * IP to bind on. */ bool bind_specific = false; memset(&local_sockaddr, 0, sizeof(struct sockaddr_storage)); if (strlen(local_ip) || !strlen(remote_host)) { int ret; struct addrinfo * local_addr; struct addrinfo hints; memset((char*)&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = family_hint; if (strlen(local_ip)) { bind_specific = true; } else { /* Bind on gethostname() IP by default. This is actually * buggy. We should be able to bind on :: and decide on * accept() what Contact IP we use. Right now, if we do * that, we'd send [::] in the contact and :: in the RTP * as "our IP". */ if (gethostname(local_ip, sizeof(local_ip)) != 0) { ERROR_NO("Can't get local hostname"); } } /* Resolving local IP */ if ((ret = getaddrinfo(local_ip, NULL, &hints, &local_addr)) != 0) { switch (ret) { #ifdef EAI_ADDRFAMILY case EAI_ADDRFAMILY: ERROR("Network family mismatch for local (%s) and remote (%s, %d) IP", local_ip, remote_ip, family_hint); break; #endif default: ERROR("Can't get local IP address in getaddrinfo, " "local_ip='%s', ret=%d", local_ip, ret); } } memcpy(&local_sockaddr, local_addr->ai_addr, local_addr->ai_addrlen); freeaddrinfo(local_addr); if (!bind_specific) { get_inet_address(&local_sockaddr, local_ip, sizeof(local_ip)); } } else { /* Get temp socket on UDP to find out our local address */ int tmpsock = -1; socklen_t len = sizeof(local_sockaddr); if ((tmpsock = socket(remote_sockaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0 || ::connect(tmpsock, _RCAST(struct sockaddr*, &remote_sockaddr), socklen_from_addr(&remote_sockaddr)) < 0 || getsockname(tmpsock, _RCAST(struct sockaddr*, &local_sockaddr), &len) < 0) { if (tmpsock >= 0) { close(tmpsock); } ERROR_NO("Failed to find our local ip"); } close(tmpsock); get_inet_address(&local_sockaddr, local_ip, sizeof(local_ip)); } /* Store local addr info for rsa option */ memcpy(&local_addr_storage, &local_sockaddr, sizeof(local_sockaddr)); if (local_sockaddr.ss_family == AF_INET) { strcpy(local_ip_w_brackets, local_ip); if (!bind_specific) { _RCAST(struct sockaddr_in*, &local_sockaddr)->sin_addr.s_addr = INADDR_ANY; } } else { local_ip_is_ipv6 = true; sprintf(local_ip_w_brackets, "[%.39s]", local_ip); if (!bind_specific) { memcpy(&_RCAST(struct sockaddr_in6*, &local_sockaddr)->sin6_addr, &in6addr_any, sizeof(in6addr_any)); } } } /* Creating and binding the local socket */ if ((main_socket = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get the local socket"); } sipp_customize_socket(main_socket); /* Trying to bind local port */ char peripaddr[256]; if (!user_port) { unsigned short l_port; for (l_port = DEFAULT_PORT; l_port < (DEFAULT_PORT + 60); l_port++) { // Bind socket to local_ip if (bind_local || peripsocket) { if (peripsocket) { // On some machines it fails to bind to the self computed local // IP address. // For the socket per IP mode, bind the main socket to the // first IP address specified in the inject file. inFiles[ip_file]->getField(0, peripfield, peripaddr, sizeof(peripaddr)); if (gai_getsockaddr(&local_sockaddr, peripaddr, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } else { if (gai_getsockaddr(&local_sockaddr, local_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } } sockaddr_update_port(&local_sockaddr, l_port); if (sipp_bind_socket(main_socket, &local_sockaddr, &local_port) == 0) { break; } } } if (!local_port) { /* Not already bound, use user_port of 0 to leave * the system choose a port. */ if (bind_local || peripsocket) { if (peripsocket) { // On some machines it fails to bind to the self computed local // IP address. // For the socket per IP mode, bind the main socket to the // first IP address specified in the inject file. inFiles[ip_file]->getField(0, peripfield, peripaddr, sizeof(peripaddr)); if (gai_getsockaddr(&local_sockaddr, peripaddr, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } else { if (gai_getsockaddr(&local_sockaddr, local_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } } sockaddr_update_port(&local_sockaddr, user_port); if (sipp_bind_socket(main_socket, &local_sockaddr, &local_port)) { ERROR_NO("Unable to bind main socket"); } } if (peripsocket) { // Add the main socket to the socket per subscriber map map_perip_fd[peripaddr] = main_socket; } // Create additional server sockets when running in socket per // IP address mode. if (peripsocket && sendMode == MODE_SERVER) { struct sockaddr_storage server_sockaddr; char peripaddr[256]; SIPpSocket *sock; unsigned int lines = inFiles[ip_file]->numLines(); for (unsigned int i = 0; i < lines; i++) { inFiles[ip_file]->getField(i, peripfield, peripaddr, sizeof(peripaddr)); map::iterator j; j = map_perip_fd.find(peripaddr); if (j == map_perip_fd.end()) { if (gai_getsockaddr(&server_sockaddr, peripaddr, local_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } bool is_ipv6 = (server_sockaddr.ss_family == AF_INET6); if ((sock = new_sipp_socket(is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get server socket"); } sipp_customize_socket(sock); if (sipp_bind_socket(sock, &server_sockaddr, NULL)) { ERROR_NO("Unable to bind server socket"); } map_perip_fd[peripaddr] = sock; } } } if ((!multisocket) && (transport == T_TCP || transport == T_TLS || transport == T_SCTP) && (sendMode != MODE_SERVER)) { if ((tcp_multiplex = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get a TCP socket"); } /* If there is a user-supplied local port and we use a single * socket, then bind to the specified port. */ if (user_port) { tcp_multiplex->set_bind_port(local_port); } /* OJA FIXME: is it correct? */ if (use_remote_sending_addr) { remote_sockaddr = remote_sending_sockaddr; } sipp_customize_socket(tcp_multiplex); /* This fixes local_port keyword value when transport are TCP|TLS and it's defined by user with "-p" */ if (sipp_bind_socket(tcp_multiplex, &local_sockaddr, NULL)) { ERROR_NO("Unable to bind TCP socket"); } if (tcp_multiplex->connect(&remote_sockaddr)) { if (reset_number > 0) { WARNING("Failed to reconnect"); main_socket->close(); main_socket = NULL; reset_number--; return 1; } else { if (errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR_NO("Unable to connect a TCP socket, remote peer error.\n" "Use 'sipp -h' for details"); } else { ERROR_NO("Unable to connect a TCP socket.\n" "Use 'sipp -h' for details"); } } } } if (transport == T_TCP || transport == T_TLS || transport == T_SCTP) { if (listen(main_socket->ss_fd, 100)) { ERROR_NO("Unable to listen main socket"); } } /* Trying to connect to Twin Sipp in 3PCC mode */ if (twinSippMode) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_A || thirdPartyMode == MODE_3PCC_A_PASSIVE) { connect_to_peer(twinSippHost, twinSippPort, &twinSipp_sockaddr, twinSippIp, &twinSippSocket); } else if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { connect_local_twin_socket(twinSippHost); } else { ERROR("TwinSipp Mode enabled but thirdPartyMode is different " "from 3PCC_CONTROLLER_B and 3PCC_CONTROLLER_A\n"); } } else if (extendedTwinSippMode) { if (thirdPartyMode == MODE_MASTER || thirdPartyMode == MODE_MASTER_PASSIVE) { strncpy(twinSippHost, get_peer_addr(master_name), sizeof(twinSippHost) - 1); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); connect_local_twin_socket(twinSippHost); connect_to_all_peers(); } else if (thirdPartyMode == MODE_SLAVE) { strncpy(twinSippHost, get_peer_addr(slave_number), sizeof(twinSippHost) - 1); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); connect_local_twin_socket(twinSippHost); } else { ERROR("extendedTwinSipp Mode enabled but thirdPartyMode is different " "from MASTER and SLAVE\n"); } } return status; } void connect_to_peer(char *peer_host, int peer_port, struct sockaddr_storage *peer_sockaddr, char *peer_ip, SIPpSocket **peer_socket) { /* Resolving the peer IP */ printf("Resolving peer address : %s...\n", peer_host); bool is_ipv6 = false; /* Resolving twin IP */ if (gai_getsockaddr(peer_sockaddr, peer_host, peer_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown peer host '%s'.\n" "Use 'sipp -h' for details", peer_host); } if (peer_sockaddr->ss_family == AF_INET6) { is_ipv6 = true; } get_inet_address(peer_sockaddr, peer_ip, sizeof(peer_ip)); if ((*peer_socket = new_sipp_socket(is_ipv6, T_TCP)) == NULL) { ERROR_NO("Unable to get a twin sipp TCP socket"); } /* Mark this as a control socket. */ (*peer_socket)->ss_control = 1; if ((*peer_socket)->connect(peer_sockaddr)) { if (errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR_NO("Unable to connect a twin sipp TCP socket\n " ", remote peer error.\n" "Use 'sipp -h' for details"); } else { ERROR_NO("Unable to connect a twin sipp socket " "\n" "Use 'sipp -h' for details"); } } sipp_customize_socket(*peer_socket); } SIPpSocket **get_peer_socket(char * peer) { peer_map::iterator peer_it; peer_it = peers.find(peer_map::key_type(peer)); if (peer_it != peers.end()) { return &peer_it->second.peer_socket; } else { ERROR("get_peer_socket: Peer %s not found", peer); } return NULL; } char * get_peer_addr(char * peer) { char * addr; peer_addr_map::iterator peer_addr_it; peer_addr_it = peer_addrs.find(peer_addr_map::key_type(peer)); if (peer_addr_it != peer_addrs.end()) { addr = peer_addr_it->second; return addr; } else { ERROR("get_peer_addr: Peer %s not found", peer); } return NULL; } bool is_a_peer_socket(SIPpSocket *peer_socket) { peer_socket_map::iterator peer_socket_it; peer_socket_it = peer_sockets.find(peer_socket_map::key_type(peer_socket)); if (peer_socket_it == peer_sockets.end()) { return false; } else { return true; } } void connect_local_twin_socket(char * twinSippHost) { /* Resolving the listener IP */ printf("Resolving listener address : %s...\n", twinSippHost); bool is_ipv6 = false; /* Resolving twin IP */ if (gai_getsockaddr(&twinSipp_sockaddr, twinSippHost, twinSippPort, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown twin host '%s'.\n" "Use 'sipp -h' for details", twinSippHost); } if (twinSipp_sockaddr.ss_family == AF_INET6) { is_ipv6 = true; } get_inet_address(&twinSipp_sockaddr, twinSippIp, sizeof(twinSippIp)); if ((localTwinSippSocket = new_sipp_socket(is_ipv6, T_TCP)) == NULL) { ERROR_NO("Unable to get a listener TCP socket "); } memset(&localTwin_sockaddr, 0, sizeof(struct sockaddr_storage)); localTwin_sockaddr.ss_family = is_ipv6 ? AF_INET6 : AF_INET; sockaddr_update_port(&localTwin_sockaddr, twinSippPort); sipp_customize_socket(localTwinSippSocket); if (sipp_bind_socket(localTwinSippSocket, &localTwin_sockaddr, 0)) { ERROR_NO("Unable to bind twin sipp socket "); } if (listen(localTwinSippSocket->ss_fd, 100)) { ERROR_NO("Unable to listen twin sipp socket in "); } } void close_peer_sockets() { peer_map::iterator peer_it, __end; for (peer_it = peers.begin(), __end = peers.end(); peer_it != __end; ++peer_it) { T_peer_infos infos = peer_it->second; infos.peer_socket->close(); infos.peer_socket = NULL; peers[std::string(peer_it->first)] = infos; } peers_connected = 0; } void close_local_sockets() { for (int i = 0; i< local_nb; i++) { local_sockets[i]->close(); local_sockets[i] = NULL; } } void connect_to_all_peers() { peer_map::iterator peer_it; T_peer_infos infos; for (peer_it = peers.begin(); peer_it != peers.end(); peer_it++) { infos = peer_it->second; get_host_and_port(infos.peer_host, infos.peer_host, &infos.peer_port); connect_to_peer(infos.peer_host, infos.peer_port, &(infos.peer_sockaddr), infos.peer_ip, &(infos.peer_socket)); peer_sockets[infos.peer_socket] = peer_it->first; peers[std::string(peer_it->first)] = infos; } peers_connected = 1; } bool is_a_local_socket(SIPpSocket *s) { for (int i = 0; i< local_nb + 1; i++) { if (local_sockets[i] == s) return true; } return (false); } void free_peer_addr_map() { peer_addr_map::iterator peer_addr_it; for (peer_addr_it = peer_addrs.begin(); peer_addr_it != peer_addrs.end(); peer_addr_it++) { free(peer_addr_it->second); } } void SIPpSocket::pollset_process(int wait) { int rs; /* Number of times to execute recv(). For TCP with 1 socket per call: no. of events returned by poll For UDP and TCP with 1 global socket: recv_count is a flag that stays up as long as there's data to read */ #ifndef HAVE_EPOLL /* What index should we try reading from? */ static size_t read_index; int loops = max_recv_loops; // If not using epoll, we have a queue of pending messages to spin through. if (read_index >= pollnfds) { read_index = 0; } /* We need to process any messages that we have left over. */ while (pending_messages && loops > 0) { update_clock_tick(); if (sockets[read_index]->ss_msglen) { struct sockaddr_storage src; char msg[SIPP_MAX_MSG_SIZE]; ssize_t len = sockets[read_index]->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sockets[read_index], msg, len, &src); } else { assert(0); } loops--; } read_index = (read_index + 1) % pollnfds; } /* Don't read more data if we still have some left over. */ if (pending_messages) { return; } #endif /* Get socket events. */ #ifdef HAVE_EPOLL /* Ignore the wait parameter and always wait - when establishing TCP * connections, the alternative is that we tight-loop. */ rs = epoll_wait(epollfd, epollevents, max_recv_loops, 1); // If we're receiving as many epollevents as possible, flag CPU congestion cpu_max = (rs > (max_recv_loops - 2)); #else rs = poll(pollfiles, pollnfds, wait ? 1 : 0); #endif if (rs < 0 && errno == EINTR) { return; } /* We need to flush all sockets and pull data into all of our buffers. */ #ifdef HAVE_EPOLL for (int event_idx = 0; event_idx < rs; event_idx++) { int poll_idx = (int)epollevents[event_idx].data.u32; #else for (size_t poll_idx = 0; rs > 0 && poll_idx < pollnfds; poll_idx++) { #endif SIPpSocket *sock = sockets[poll_idx]; int events = 0; int ret = 0; assert(sock); #ifdef HAVE_EPOLL if (epollevents[event_idx].events & EPOLLOUT) { #else if (pollfiles[poll_idx].revents & POLLOUT) { #endif #ifdef USE_SCTP if (transport == T_SCTP && sock->sctpstate != SCTP_UP); else #endif { /* We can flush this socket. */ TRACE_MSG("Exit problem event on socket %d \n", sock->ss_fd); #ifdef HAVE_EPOLL epollfiles[poll_idx].events &= ~EPOLLOUT; int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, sock->ss_fd, &epollfiles[poll_idx]); if (rc == -1) { ERROR_NO("Failed to clear EPOLLOUT"); } #else pollfiles[poll_idx].events &= ~POLLOUT; #endif sock->ss_congested = false; sock->flush(); events++; } } #ifdef HAVE_EPOLL if (epollevents[event_idx].events & EPOLLIN) { #else if (pollfiles[poll_idx].revents & POLLIN) { #endif /* We can empty this socket. */ if ((transport == T_TCP || transport == T_TLS || transport == T_SCTP) && sock == main_socket) { SIPpSocket *new_sock = sock->accept(); if (!new_sock) { ERROR_NO("Accepting new TCP connection"); } } else if (sock == ctrl_socket) { handle_ctrl_socket(); } else if (sock == stdin_socket) { handle_stdin_socket(); } else if (sock == localTwinSippSocket) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { twinSippSocket = sock->accept(); if (!twinSippMode) { ERROR_NO("Accepting new TCP connection on Twin SIPp Socket"); } twinSippSocket->ss_control = 1; } else { /* 3pcc extended mode: open a local socket which will be used for reading the infos sent by this remote twin sipp instance (slave or master) */ if (local_nb == MAX_LOCAL_TWIN_SOCKETS) { ERROR("Max number of twin instances reached"); } SIPpSocket *localSocket = sock->accept(); localSocket->ss_control = 1; local_sockets[local_nb] = localSocket; local_nb++; if (!peers_connected) { connect_to_all_peers(); } } } else { if ((ret = sock->empty()) <= 0) { #ifdef USE_SCTP if (sock->ss_transport == T_SCTP && ret == -2); else #endif { ret = sock->read_error(ret); if (ret == 0) { /* If read_error() then the poll_idx now belongs * to the newest/last socket added to the sockets[]. * Need to re-do the same poll_idx for the "new" socket. * We do this differently when using epoll. */ #ifdef HAVE_EPOLL for (int event_idx2 = event_idx + 1; event_idx2 < rs; event_idx2++) { if (epollevents[event_idx2].data.u32 == pollnfds) { epollevents[event_idx2].data.u32 = poll_idx; } } #else poll_idx--; events++; rs--; #endif continue; } } } } events++; } /* Here the logic diverges; if we're using epoll, we want to stay in the * for-each-socket loop and handle messages on that socket. If we're not using * epoll, we want to wait until after that loop, and spin through our * pending_messages queue again. */ #ifdef HAVE_EPOLL unsigned old_pollnfds = pollnfds; update_clock_tick(); /* Keep processing messages until this socket is freed (changing the number of file descriptors) or we run out of messages. */ while ((pollnfds == old_pollnfds) && (sock->message_ready())) { char msg[SIPP_MAX_MSG_SIZE]; struct sockaddr_storage src; ssize_t len; len = sock->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sock, msg, len, &src); } else { assert(0); } } if (pollnfds != old_pollnfds) { /* Processing messages has changed the number of pollnfds, so update any remaining events */ for (int event_idx2 = event_idx + 1; event_idx2 < rs; event_idx2++) { if (epollevents[event_idx2].data.u32 == pollnfds) { epollevents[event_idx2].data.u32 = poll_idx; } } } #else if (events) { rs--; } pollfiles[poll_idx].revents = 0; #endif } #ifndef HAVE_EPOLL if (read_index >= pollnfds) { read_index = 0; } /* We need to process any new messages that we read. */ while (pending_messages && (loops > 0)) { update_clock_tick(); if (sockets[read_index]->ss_msglen) { char msg[SIPP_MAX_MSG_SIZE]; struct sockaddr_storage src; ssize_t len; len = sockets[read_index]->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sockets[read_index], msg, len, &src); } else { assert(0); } loops--; } read_index = (read_index + 1) % pollnfds; } cpu_max = (loops <= 0); #endif } /***************** Check of the message received ***************/ bool sipMsgCheck (const char *P_msg, SIPpSocket *socket) { const char C_sipHeader[] = "SIP/2.0"; if (socket == twinSippSocket || socket == localTwinSippSocket || is_a_peer_socket(socket) || is_a_local_socket(socket)) return true; if (strstr(P_msg, C_sipHeader) != NULL) { return true; } return false; } #ifdef GTEST #include "gtest/gtest.h" TEST(get_trimmed_call_id, noslashes) { EXPECT_STREQ("abc", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc\r\nCall-ID: def\r\n\r\n")); } TEST(get_trimmed_call_id, withslashes) { EXPECT_STREQ("abc2", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: ///abc2\r\nCall-ID: def\r\n\r\n")); EXPECT_STREQ("abc3", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc2///abc3\r\nCall-ID: def\r\n\r\n")); EXPECT_STREQ("abc4///abc5", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc3///abc4///abc5\r\nCall-ID: def\r\n\r\n")); } #endif //GTEST sipp-3.7.2/src/socketowner.cpp0000664000000000000000000001000214525516253013225 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include #include "sipp.hpp" socket_owner_map_map socket_to_owners; SIPpSocket *socketowner::associate_socket(SIPpSocket *socket) { if (socket) { this->call_socket = socket; add_owner_to_socket(socket); } return socket; } SIPpSocket *socketowner::dissociate_socket() { SIPpSocket *ret = this->call_socket; remove_owner_from_socket(this->call_socket); this->call_socket = NULL; return ret; } unsigned long socketowner::nextownerid = 1; socketowner::socketowner() { this->call_socket = NULL; this->ownerid = socketowner::nextownerid++; } socketowner::~socketowner() { if (this->call_socket) { dissociate_socket()->close(); } } void socketowner::add_owner_to_socket(SIPpSocket *socket) { socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_owners.end()) { socket_to_owners.insert(socket_map_pair(socket, new owner_map)); map_it = socket_to_owners.find(socket); assert(map_it != socket_to_owners.end()); } owner_map *socket_owner_map = (owner_map *) map_it->second; socket_owner_map->insert(long_owner_pair(this->ownerid, this)); } void socketowner::remove_owner_from_socket(SIPpSocket *socket) { socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* We must have a map for this socket. */ assert(map_it != socket_to_owners.end()); owner_map *socket_owner_map = (owner_map *) map_it->second; owner_map::iterator owner_it = socket_owner_map->find(this->ownerid); /* And our owner must exist in the map. */ assert(owner_it != socket_owner_map->end()); socket_owner_map->erase(owner_it); /* If we have no more calls, we can delete this entry. */ if (socket_owner_map->begin() == socket_owner_map->end()) { delete socket_owner_map; socket_to_owners.erase(map_it); } } /* The caller must delete this list. */ owner_list *get_owners_for_socket(SIPpSocket *socket) { owner_list *l = new owner_list; socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_owners.end()) { return l; } owner_map *socket_owner_map = (owner_map *) map_it->second; owner_map::iterator owner_it; for (owner_it = socket_owner_map->begin(); owner_it != socket_owner_map->end(); owner_it++) { l->insert(l->end(), owner_it->second); } return l; } sipp-3.7.2/src/sslsocket.cpp0000664000000000000000000003066014525516253012710 0ustar /* * 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 * * Author : Gundu RAO - 16 Jul 2004 * From Hewlett Packard Company. */ #include #include #include "sipp.hpp" #include "sslsocket.hpp" #if defined(USE_OPENSSL) || defined(USE_WOLFSSL) #define CALL_BACK_USER_DATA "ksgr" static SSL_CTX* sip_trp_ssl_ctx = NULL; /* For SSL cserver context */ static SSL_CTX* sip_trp_ssl_ctx_client = NULL; /* For SSL cserver context */ #if defined(USE_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10100000 #define MUTEX_TYPE pthread_mutex_t #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) #define THREAD_ID pthread_self() static MUTEX_TYPE *mutex_buf = NULL; static void locking_function(int mode, int n, const char *file, int line) { (void)file; /* unused, avoid warnings */ (void)line; /* unused, avoid warnings */ if (mode & CRYPTO_LOCK) MUTEX_LOCK(mutex_buf[n]); else MUTEX_UNLOCK(mutex_buf[n]); } #ifndef WIN32 static unsigned long id_function() { return (unsigned long)THREAD_ID; } #endif #endif static int thread_setup() { #if defined(USE_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10100000 int i; mutex_buf = (MUTEX_TYPE *)malloc(sizeof(MUTEX_TYPE) * CRYPTO_num_locks()); if (!mutex_buf) return 0; for (i = 0; i < CRYPTO_num_locks(); ++i) MUTEX_SETUP(mutex_buf[i]); #ifndef WIN32 /* For openssl>=1.0 it uses the address of errno for thread id. * Works for us. */ CRYPTO_set_id_callback(id_function); #endif /* > All OpenSSL code has now been transferred to use the new * > threading API, so the old one is no longer used and can be * > removed. [...] There is now no longer a need to set locking * > callbacks!! * https://github.com/openssl/openssl/commit/ * 2e52e7df518d80188c865ea3f7bb3526d14b0c08 */ CRYPTO_set_locking_callback(locking_function); #endif return 1; } static int passwd_call_back_routine(char *buf, int size, int /*flag*/, void *passwd) { strncpy(buf, (char *)(passwd), size); buf[size - 1] = '\0'; return(strlen(buf)); } /****** SSL error handling *************/ const char *SSL_error_string(int ssl_error, int orig_ret) { switch (ssl_error) { case SSL_ERROR_NONE: return "No error"; case SSL_ERROR_ZERO_RETURN: return "SSL connection has been closed. SSL returned: SSL_ERROR_ZERO_RETURN"; case SSL_ERROR_WANT_WRITE: return "SSL I/O function returned SSL_ERROR_WANT_WRITE"; case SSL_ERROR_WANT_READ: return "SSL I/O function returned SSL_ERROR_WANT_READ"; case SSL_ERROR_WANT_CONNECT: return "SSL I/O function returned SSL_ERROR_WANT_CONNECT"; case SSL_ERROR_WANT_ACCEPT: return "SSL I/O function returned SSL_ERROR_WANT_ACCEPT"; case SSL_ERROR_WANT_X509_LOOKUP: return "SSL I/O function returned SSL_ERROR_WANT_X509_LOOKUP"; case SSL_ERROR_SSL: return "SSL protocol error. SSL I/O function returned SSL_ERROR_SSL"; case SSL_ERROR_SYSCALL: if (orig_ret < 0) { /* not EOF */ return strerror(errno); } else { /* EOF */ return "Non-recoverable I/O error occurred. SSL I/O function returned SSL_ERROR_SYSCALL"; } } return "Unknown SSL Error."; } SSL* SSL_new_client() { return SSL_new(sip_trp_ssl_ctx_client); } SSL* SSL_new_server() { return SSL_new(sip_trp_ssl_ctx); } /****** Certificate Verification Callback FACILITY *************/ static int sip_tls_verify_callback(int ok , X509_STORE_CTX *store) { char data[512]; if (!ok) { X509 *cert = X509_STORE_CTX_get_current_cert(store); X509_NAME_oneline(X509_get_issuer_name(cert), data, 512); WARNING("TLS verification error for issuer: '%s'", data); X509_NAME_oneline(X509_get_subject_name(cert), data, 512); WARNING("TLS verification error for subject: '%s'", data); } return ok; } /*********** Load the CRL's into SSL_CTX **********************/ static int sip_tls_load_crls(SSL_CTX* ctx , const char* crlfile) { X509_STORE *store; X509_LOOKUP *lookup; /* Get the X509_STORE from SSL context */ if (!(store = SSL_CTX_get_cert_store(ctx))) { return (-1); } /* Add lookup file to X509_STORE */ if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) { return (-1); } /* Add the CRLS to the lookpup object */ #if defined(USE_WOLFSSL) if (X509_LOOKUP_load_file(lookup, crlfile, X509_FILETYPE_PEM) != 1) { #else if (X509_load_crl_file(lookup, crlfile, X509_FILETYPE_PEM) != 1) { #endif return (-1); } /* Set the flags of the store so that CRLS's are consulted */ #if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #else #warning This version of OpenSSL (<0.9.7) cannot handle CRL files in capath ERROR("This version of OpenSSL (<0.9.7) cannot handle CRL files in capath"); #endif return (1); } static SSL_CTX* instantiate_ssl_context(const char* context_name) { SSL_CTX* ssl_ctx = NULL; #if OPENSSL_VERSION_NUMBER >= 0x10100000 /* >= 1.1 */ int min_tls_version, max_tls_version; if (tls_version == 0.0) { #if !defined(USE_WOLFSSL) || defined(WOLFSSL_ALLOW_TLSV10) min_tls_version = TLS1_VERSION; #else min_tls_version = TLS1_1_VERSION; #endif max_tls_version = TLS_MAX_VERSION; } else if (tls_version == 1.0) { #if !defined(USE_WOLFSSL) || defined(WOLFSSL_ALLOW_TLSV10) max_tls_version = min_tls_version = TLS1_VERSION; #else ERROR("Old TLS version 1.0 is no longer supported for [%s] context.", context_name); return NULL; #endif } else if (tls_version == 1.1) { max_tls_version = min_tls_version = TLS1_1_VERSION; } else if (tls_version == 1.2) { max_tls_version = min_tls_version = TLS1_2_VERSION; } else { ERROR("Unrecognized TLS version for [%s] context: %1.1f", context_name, tls_version); return NULL; } if (!strncmp(context_name, "client", 6)) { ssl_ctx = SSL_CTX_new(TLS_client_method()); } else { ssl_ctx = SSL_CTX_new(TLS_server_method()); } SSL_CTX_set_min_proto_version(ssl_ctx, min_tls_version); if (max_tls_version != TLS_MAX_VERSION) { SSL_CTX_set_max_proto_version(ssl_ctx, max_tls_version); } #else /* OPENSSL_VERSION < 1.1 */ if (tls_version == 0.0) { if (!strncmp(context_name, "client", 6)) { ssl_ctx = SSL_CTX_new(SSLv23_client_method()); } else { ssl_ctx = SSL_CTX_new(SSLv23_server_method()); } } else if (tls_version == 1.0) { #if !defined(USE_WOLFSSL) || defined(WOLFSSL_ALLOW_TLSV10) if (!strncmp(context_name, "client", 6)) { ssl_ctx = SSL_CTX_new(TLSv1_client_method()); } else { ssl_ctx = SSL_CTX_new(TLSv1_server_method()); } #else ERROR("Old TLS version 1.0 is no longer supported for [%s] context.", context_name); ssl_ctx = NULL; #endif } else if (tls_version == 1.1) { if (!strncmp(context_name, "client", 6)) { ssl_ctx = SSL_CTX_new(TLSv1_1_client_method()); } else { ssl_ctx = SSL_CTX_new(TLSv1_1_server_method()); } } else if (tls_version == 1.2) { if (!strncmp(context_name, "client", 6)) { ssl_ctx = SSL_CTX_new(TLSv1_2_client_method()); } else { ssl_ctx = SSL_CTX_new(TLSv1_2_server_method()); } } else { ERROR("Unrecognized TLS version for [%s] context: %1.1f", context_name, tls_version); ssl_ctx = NULL; } #endif return ssl_ctx; } #endif // USE_OPENSSL || USE_WOLFSSL /************* Prepare the SSL context ************************/ enum tls_init_status TLS_init_context(void) { sip_trp_ssl_ctx = instantiate_ssl_context("generic"); if (sip_trp_ssl_ctx == NULL) { ERROR("TLS_init_context: SSL_CTX_new with TLS_method failed for generic context"); return TLS_INIT_ERROR; } sip_trp_ssl_ctx_client = instantiate_ssl_context("client"); if (sip_trp_ssl_ctx_client == NULL) { ERROR("TLS_init_context: SSL_CTX_new with TLS_method failed for client context"); return TLS_INIT_ERROR; } /* Load the trusted CA's */ if (strlen(tls_ca_name) != 0) { SSL_CTX_load_verify_locations(sip_trp_ssl_ctx, tls_ca_name, NULL); SSL_CTX_load_verify_locations(sip_trp_ssl_ctx_client, tls_ca_name, NULL); } /* TLS Verification only makes sense if an CA is specified or * we require CRL validation. */ if (strlen(tls_ca_name) != 0 || strlen(tls_crl_name) != 0) { if (sip_tls_load_crls(sip_trp_ssl_ctx, tls_crl_name) == -1) { ERROR("TLS_init_context: Unable to load CRL file (%s)", tls_crl_name); return TLS_INIT_ERROR; } if (sip_tls_load_crls(sip_trp_ssl_ctx_client, tls_crl_name) == -1) { ERROR("TLS_init_context: Unable to load CRL (client) file (%s)", tls_crl_name); return TLS_INIT_ERROR; } /* The following call forces to process the certificates with * the initialised SSL_CTX */ SSL_CTX_set_verify(sip_trp_ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, sip_tls_verify_callback); SSL_CTX_set_verify(sip_trp_ssl_ctx_client, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, sip_tls_verify_callback); } /* Selection Cipher suits - load the application specified ciphers */ SSL_CTX_set_default_passwd_cb_userdata(sip_trp_ssl_ctx, (void *)CALL_BACK_USER_DATA); SSL_CTX_set_default_passwd_cb_userdata(sip_trp_ssl_ctx_client, (void *)CALL_BACK_USER_DATA); SSL_CTX_set_default_passwd_cb(sip_trp_ssl_ctx, passwd_call_back_routine); SSL_CTX_set_default_passwd_cb(sip_trp_ssl_ctx_client, passwd_call_back_routine); if (SSL_CTX_use_certificate_file(sip_trp_ssl_ctx, tls_cert_name, SSL_FILETYPE_PEM) != 1) { char errbuf[256] = {'\0'}; ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); ERROR("TLS_init_context: SSL_CTX_use_certificate_file failed: %s", errbuf); return TLS_INIT_ERROR; } if (SSL_CTX_use_certificate_file(sip_trp_ssl_ctx_client, tls_cert_name, SSL_FILETYPE_PEM) != 1) { char errbuf[256] = {'\0'}; ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf)); ERROR("TLS_init_context: SSL_CTX_use_certificate_file (client) failed: %s", errbuf); return TLS_INIT_ERROR; } if (SSL_CTX_use_PrivateKey_file(sip_trp_ssl_ctx, tls_key_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_PrivateKey_file failed"); return TLS_INIT_ERROR; } if (SSL_CTX_use_PrivateKey_file(sip_trp_ssl_ctx_client, tls_key_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_PrivateKey_file (client) failed"); return TLS_INIT_ERROR; } return TLS_INIT_NORMAL; } int TLS_init() { if (!thread_setup() || !SSL_library_init()) { return -1; } SSL_load_error_strings(); return 1; } sipp-3.7.2/src/stat.cpp0000664000000000000000000020272414525516253011653 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * Wolfgang Beck * */ #include #include #include #include #include "config.h" #include "sipp.hpp" #include "scenario.hpp" #include "screen.hpp" #include "stat.hpp" /* ** Local definitions (macros) */ #define RESET_COUNTERS(PT) \ memset (PT, 0, CStat::E_NB_COUNTER * sizeof(unsigned long long)) #define RESET_C_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_C_OutOfCallMsgs; \ i <= CStat::CPT_G_C_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_C_IncomingCallCreated; \ i <= CStat::CPT_C_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_C] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } #define RESET_PD_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_PD_OutOfCallMsgs; \ i <= CStat::CPT_G_PD_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_PD_IncomingCallCreated; \ i <= CStat::CPT_PD_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_PD] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } #define RESET_PL_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_PL_OutOfCallMsgs; \ i <= CStat::CPT_G_PL_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_PL_IncomingCallCreated; \ i <= CStat::CPT_PL_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_PL] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } /* __________________________________________________________________________ C L A S S CS t a t __________________________________________________________________________ */ unsigned long long CStat::M_G_counters[E_NB_G_COUNTER - E_NB_COUNTER]; CStat::~CStat() { int i; dumpDataRtt(); for (i = 0; i < nRtds(); i++) { if (M_ResponseTimeRepartition[i] != NULL) { delete [] M_ResponseTimeRepartition[i]; } } free(M_ResponseTimeRepartition); if (M_CallLengthRepartition != NULL) delete [] M_CallLengthRepartition; if(M_outputStream != NULL) { M_outputStream->close(); delete M_outputStream; } if(M_fileName != NULL) delete [] M_fileName; if(M_outputStreamRtt != NULL) { M_outputStreamRtt->close(); delete M_outputStreamRtt; } if(M_fileNameRtt != NULL) delete [] M_fileNameRtt; if(M_dumpRespTime != NULL) delete [] M_dumpRespTime; free(M_rtdInfo); for (int_str_map::iterator i = M_revRtdMap.begin(); i != M_revRtdMap.end(); ++i) { free(i->second); } M_SizeOfResponseTimeRepartition = 0; M_SizeOfCallLengthRepartition = 0; M_CallLengthRepartition = NULL; M_fileName = NULL; M_outputStream = NULL; M_outputStreamRtt = NULL; M_fileNameRtt = NULL; M_dumpRespTime = NULL; } int CStat::init () { // reset of all counter RESET_COUNTERS(M_counters); GET_TIME (&M_startTime); memcpy (&M_pdStartTime, &M_startTime, sizeof (struct timeval)); memcpy (&M_plStartTime, &M_startTime, sizeof (struct timeval)); M_outputStream = NULL; M_headerAlreadyDisplayed = false; M_outputStreamRtt = NULL; M_headerAlreadyDisplayedRtt = false; std::vector error_codes(0); return(1); } int CStat::isWellFormed(char * P_listeStr, int * nombre) { char * ptr = P_listeStr; int sizeOf; bool isANumber; (*nombre) = 0; sizeOf = strlen(P_listeStr); // getting the number if(sizeOf > 0) { // is the string well formed ? [0-9] [,] isANumber = false; for(int i=0; i<=sizeOf; i++) { switch(ptr[i]) { case ',': if(isANumber == false) { return(0); } else { (*nombre)++; } isANumber = false; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': isANumber = true; break; case '\t': case ' ' : break; case '\0': if(isANumber == false) { return(0); } else { (*nombre)++; } break; default: return(0); } } // enf for } return(1); } int CStat::createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList) { int nb=0; char * ptr = P_listeStr; char * ptr_prev = P_listeStr; unsigned int current_int; if(isWellFormed(P_listeStr, sizeOfList) == 1) { (*listeInteger) = new unsigned int[(*sizeOfList)]; while((*ptr) != ('\0')) { if((*ptr) == ',') { sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; ptr_prev = ptr+1; } ptr++; } // on lit le dernier sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; return(1); } return(0); } void CStat::setFileName(const char* P_name, const char* P_extension) { int sizeOf, sizeOfExtension; if(P_name != NULL) { // +6 for PID sizeOf = strlen(P_name) + 6; if(sizeOf > 0) { if(P_extension != NULL) { sizeOfExtension = strlen(P_extension); if(sizeOfExtension > 0) { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, P_extension); } else { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, DEFAULT_EXTENSION); } } else { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, DEFAULT_EXTENSION); } } else { cerr << "new file name length is null - " << "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } void CStat::setFileName(const char* P_name) { int sizeOf; if(P_name != NULL) { sizeOf = strlen(P_name); if(sizeOf > 0) { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[sizeOf+1]; strcpy(M_fileName, P_name); } else { cerr << "new file name length is null - " "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } void CStat::initRtt(const char* P_name, const char* P_extension, unsigned long P_report_freq_dumpRtt) { int sizeOf, sizeOfExtension; if(P_name != NULL) { sizeOf = strlen(P_name) ; if(sizeOf > 0) { // 4 for '_rtt' and 6 for pid sizeOf += 10 ; sizeOfExtension = strlen(P_extension); if(M_fileNameRtt != NULL) delete [] M_fileNameRtt; sizeOf += sizeOfExtension; M_fileNameRtt = new char[sizeOf+1]; sprintf (M_fileNameRtt, "%s_%ld_rtt%s", P_name, (long) getpid(),P_extension); } else { cerr << "new file name length is null - " << "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } // initiate the table dump response time M_report_freq_dumpRtt = P_report_freq_dumpRtt ; M_dumpRespTime = new T_value_rtt [P_report_freq_dumpRtt] ; if ( M_dumpRespTime == NULL ) { cerr << "Memory allocation failure" << endl; exit(EXIT_FATAL_ERROR); } for (unsigned L_i = 0 ; L_i < P_report_freq_dumpRtt; L_i ++) { M_dumpRespTime[L_i].date = 0.0; M_dumpRespTime[L_i].rtd_no = 0; M_dumpRespTime[L_i].rtt = 0.0; } } void CStat::setRepartitionCallLength(char * P_listeStr) { unsigned int * listeInteger; int sizeOfListe; if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) { initRepartition(listeInteger, sizeOfListe, &M_CallLengthRepartition, &M_SizeOfCallLengthRepartition); } else { ERROR("Could not create table for call length repartition '%s'", P_listeStr); } delete [] listeInteger; listeInteger = NULL; } void CStat::setRepartitionResponseTime (char * P_listeStr) { unsigned int * listeInteger; int sizeOfListe; int i; for (i = 0; i < nRtds(); i++) { if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) { initRepartition(listeInteger, sizeOfListe, &M_ResponseTimeRepartition[i], &M_SizeOfResponseTimeRepartition); } else { ERROR("Could not create table for response time repartition '%s'", P_listeStr); } delete [] listeInteger; listeInteger = NULL; } } void CStat::setRepartitionCallLength(unsigned int* repartition, int nombre) { initRepartition(repartition, nombre, &M_CallLengthRepartition, &M_SizeOfCallLengthRepartition); } void CStat::setRepartitionResponseTime(unsigned int* repartition, int nombre) { for (int i = 0; i < nRtds(); i++) { initRepartition(repartition, nombre, &M_ResponseTimeRepartition[i], &M_SizeOfResponseTimeRepartition); } } void CStat::initRepartition(unsigned int* repartition, int nombre, T_dynamicalRepartition ** tabRepartition, int* tabNb) { bool sortDone; int i; unsigned int swap; if((nombre <= 0) || (repartition == NULL) ) { (*tabNb) = 0; (*tabRepartition) = NULL; return; } (*tabNb) = nombre + 1; (*tabRepartition) = new T_dynamicalRepartition[(*tabNb)]; // copying the repartition table in the local table for(i=0; i (*tabRepartition)[i+1].borderMax) { // swapping this two value and setting sortDone to false swap = (*tabRepartition)[i].borderMax; (*tabRepartition)[i].borderMax = (*tabRepartition)[i+1].borderMax; (*tabRepartition)[i+1].borderMax = swap; sortDone = false; } } } // setting the range for max <= value < infinity (*tabRepartition)[nombre].borderMax = (*tabRepartition)[nombre-1].borderMax; (*tabRepartition)[nombre].nbInThisBorder = 0; } void CStat::setRtpEchoErrors(int value) { M_rtpEchoErrors = value; } int CStat::getRtpEchoErrors() { return M_rtpEchoErrors; } int CStat::computeStat (E_Action P_action) { switch (P_action) { case E_CREATE_OUTGOING_CALL : M_counters [CPT_C_OutgoingCallCreated]++; M_counters [CPT_PD_OutgoingCallCreated]++; M_counters [CPT_PL_OutgoingCallCreated]++; M_counters [CPT_C_CurrentCall]++; if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_C_CurrentCallPeak]) { M_counters [CPT_C_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_C_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PD_CurrentCallPeak]) { M_counters [CPT_PD_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PD_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PL_CurrentCallPeak]) { M_counters [CPT_PL_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PL_CurrentCallPeakTime] = clock_tick / 1000; } break; case E_CREATE_INCOMING_CALL : M_counters [CPT_C_IncomingCallCreated]++; M_counters [CPT_PD_IncomingCallCreated]++; M_counters [CPT_PL_IncomingCallCreated]++; M_counters [CPT_C_CurrentCall]++; if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_C_CurrentCallPeak]) { M_counters [CPT_C_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_C_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PD_CurrentCallPeak]) { M_counters [CPT_PD_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PD_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PL_CurrentCallPeak]) { M_counters [CPT_PL_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PL_CurrentCallPeakTime] = clock_tick / 1000; } break; case E_CALL_FAILED : M_counters [CPT_C_FailedCall]++; M_counters [CPT_PD_FailedCall]++; M_counters [CPT_PL_FailedCall]++; M_counters [CPT_C_CurrentCall]--; break; case E_CALL_SUCCESSFULLY_ENDED : M_counters [CPT_C_SuccessfulCall]++; M_counters [CPT_PD_SuccessfulCall]++; M_counters [CPT_PL_SuccessfulCall]++; M_counters [CPT_C_CurrentCall]--; break; case E_FAILED_CANNOT_SEND_MSG : M_counters [CPT_C_FailedCallCannotSendMessage]++; M_counters [CPT_PD_FailedCallCannotSendMessage]++; M_counters [CPT_PL_FailedCallCannotSendMessage]++; break; case E_FAILED_MAX_UDP_RETRANS : M_counters [CPT_C_FailedCallMaxUdpRetrans]++; M_counters [CPT_PD_FailedCallMaxUdpRetrans]++; M_counters [CPT_PL_FailedCallMaxUdpRetrans]++; break; case E_FAILED_TCP_CONNECT : M_counters [CPT_C_FailedCallTcpConnect]++; M_counters [CPT_PD_FailedCallTcpConnect]++; M_counters [CPT_PL_FailedCallTcpConnect]++; break; case E_FAILED_TCP_CLOSED : M_counters [CPT_C_FailedCallTcpClosed]++; M_counters [CPT_PD_FailedCallTcpClosed]++; M_counters [CPT_PL_FailedCallTcpClosed]++; break; case E_FAILED_UNEXPECTED_MSG : M_counters [CPT_C_FailedCallUnexpectedMessage]++; M_counters [CPT_PD_FailedCallUnexpectedMessage]++; M_counters [CPT_PL_FailedCallUnexpectedMessage]++; break; case E_FAILED_CALL_REJECTED : M_counters [CPT_C_FailedCallCallRejected]++; M_counters [CPT_PD_FailedCallCallRejected]++; M_counters [CPT_PL_FailedCallCallRejected]++; break; case E_FAILED_CMD_NOT_SENT : M_counters [CPT_C_FailedCallCmdNotSent]++; M_counters [CPT_PD_FailedCallCmdNotSent]++; M_counters [CPT_PL_FailedCallCmdNotSent]++; break; case E_FAILED_REGEXP_DOESNT_MATCH : M_counters [CPT_C_FailedCallRegexpDoesntMatch]++; M_counters [CPT_PD_FailedCallRegexpDoesntMatch]++; M_counters [CPT_PL_FailedCallRegexpDoesntMatch]++; break; case E_FAILED_REGEXP_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallRegexpShouldntMatch]++; M_counters [CPT_PD_FailedCallRegexpShouldntMatch]++; M_counters [CPT_PL_FailedCallRegexpShouldntMatch]++; break; case E_FAILED_REGEXP_HDR_NOT_FOUND : M_counters [CPT_C_FailedCallRegexpHdrNotFound]++; M_counters [CPT_PD_FailedCallRegexpHdrNotFound]++; M_counters [CPT_PL_FailedCallRegexpHdrNotFound]++; break; case E_FAILED_OUTBOUND_CONGESTION : M_counters [CPT_C_FailedOutboundCongestion]++; M_counters [CPT_PD_FailedOutboundCongestion]++; M_counters [CPT_PL_FailedOutboundCongestion]++; break; case E_FAILED_TIMEOUT_ON_RECV : M_counters [CPT_C_FailedTimeoutOnRecv]++; M_counters [CPT_PD_FailedTimeoutOnRecv]++; M_counters [CPT_PL_FailedTimeoutOnRecv]++; break; case E_FAILED_TEST_DOESNT_MATCH : M_counters [CPT_C_FailedCallTestDoesntMatch]++; M_counters [CPT_PD_FailedCallTestDoesntMatch]++; M_counters [CPT_PL_FailedCallTestDoesntMatch]++; break; case E_FAILED_TEST_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallTestShouldntMatch]++; M_counters [CPT_PD_FailedCallTestShouldntMatch]++; M_counters [CPT_PL_FailedCallTestShouldntMatch]++; break; case E_FAILED_STRCMP_DOESNT_MATCH : M_counters [CPT_C_FailedCallStrcmpDoesntMatch]++; M_counters [CPT_PD_FailedCallStrcmpDoesntMatch]++; M_counters [CPT_PL_FailedCallStrcmpDoesntMatch]++; break; case E_FAILED_STRCMP_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallStrcmpShouldntMatch]++; M_counters [CPT_PD_FailedCallStrcmpShouldntMatch]++; M_counters [CPT_PL_FailedCallStrcmpShouldntMatch]++; break; case E_FAILED_TIMEOUT_ON_SEND : M_counters [CPT_C_FailedTimeoutOnSend]++; M_counters [CPT_PD_FailedTimeoutOnSend]++; M_counters [CPT_PL_FailedTimeoutOnSend]++; break; case E_RETRANSMISSION : M_counters [CPT_C_Retransmissions]++; M_counters [CPT_PD_Retransmissions]++; M_counters [CPT_PL_Retransmissions]++; break; case E_RESET_C_COUNTERS : RESET_C_COUNTERS; GET_TIME (&M_startTime); break; case E_RESET_PD_COUNTERS : //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", // "CStat::computeStat : RESET_PD_COUNTERS"); RESET_PD_COUNTERS; GET_TIME (&M_pdStartTime); break; case E_RESET_PL_COUNTERS : //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", // "C_Stat::computeStat : RESET_PL_COUNTERS"); RESET_PL_COUNTERS; GET_TIME (&M_plStartTime); if (periodic_rtd) { resetRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition); for (int i = 0; i < nRtds(); i++) { resetRepartition(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); } } break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } int CStat::globalStat (E_Action P_action) { switch (P_action) { case E_OUT_OF_CALL_MSGS : M_G_counters [CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_OutOfCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]++; break; case E_WATCHDOG_MAJOR : M_G_counters [CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_WatchdogMajor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1]++; break; case E_WATCHDOG_MINOR : M_G_counters [CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_WatchdogMinor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1]++; break; case E_DEAD_CALL_MSGS : M_G_counters [CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_DeadCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1]++; break; case E_FATAL_ERRORS : M_G_counters [CPT_G_C_FatalErrors - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_FatalErrors - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_FatalErrors - E_NB_COUNTER - 1]++; break; case E_WARNING : M_G_counters [CPT_G_C_Warnings - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_Warnings - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_Warnings - E_NB_COUNTER - 1]++; break; case E_AUTO_ANSWERED : // Let's count the automatic answered calls M_G_counters [CPT_G_C_AutoAnswered - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_AutoAnswered - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1]++; break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } void CStat::computeRtt (unsigned long long P_start_time, unsigned long long P_stop_time, int which) { M_dumpRespTime[M_counterDumpRespTime].date = (double)P_stop_time / (double)1000; M_dumpRespTime[M_counterDumpRespTime].rtd_no = which; M_dumpRespTime[M_counterDumpRespTime].rtt = ((double)(P_stop_time - P_start_time)) / (double)1000; M_counterDumpRespTime++ ; if (M_counterDumpRespTime > (M_report_freq_dumpRtt - 1)) { dumpDataRtt () ; } } unsigned long long CStat::GetStat (E_CounterName P_counter) { if (P_counter < E_NB_COUNTER) { return M_counters [P_counter]; } else { return M_G_counters [P_counter - E_NB_COUNTER - 1]; } } /* Get the current start time. */ void CStat::getStartTime(struct timeval *t) { memcpy(t, &M_startTime, sizeof(M_startTime)); } /* Use the short form standard deviation formula given the sum of the squares * and the sum. */ double CStat::computeStdev(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares) { if (M_counters[P_NbOfCallUsed] <= 0) return 0.0; double numerator = ((double)(M_counters[P_NbOfCallUsed]) * (double)(M_counters[P_Squares])) - ((double)(M_counters[P_SumCounter] * M_counters[P_SumCounter])); double denominator = (double)(M_counters[P_NbOfCallUsed]) * (((double)(M_counters[P_NbOfCallUsed])) - 1.0); return sqrt(numerator/denominator); } double CStat::computeMean(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed) { if (M_counters[P_NbOfCallUsed] == 0) return 0.0; return ((double)(M_counters[P_SumCounter]) / (double)(M_counters[P_NbOfCallUsed])); } double CStat::computeRtdMean(int which, int type) { unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; if (count == 0) return 0.0; return ((double)(sum) / (double)(count)); } double CStat::computeRtdStdev(int which, int type) { unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; unsigned long long sumsq = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUMSQ]; if (count <= 1) return 0.0; double numerator = ((double)count * (double)sumsq) - (double)(sum * sum); double denominator = (double)(count) * ((double)(count) - 1.0); return sqrt(numerator/denominator); } void CStat::updateAverageCounter(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares, unsigned long P_value) { if (M_counters [P_NbOfCallUsed] <= 0) { M_counters [P_NbOfCallUsed] ++; M_counters [P_SumCounter] = P_value; M_counters [P_Squares] = (P_value * P_value); } else { M_counters [P_SumCounter] += P_value; M_counters [P_Squares] += (P_value * P_value); M_counters [P_NbOfCallUsed] ++; } } int CStat::computeStat (E_Action P_action, unsigned long P_value) { return computeStat(P_action, P_value, 0); } int CStat::findCounter(const char *counter, bool alloc) { str_int_map::iterator it = M_genericMap.find(str_int_map::key_type(counter)); if (it != M_genericMap.end()) { return it->second; } if (!alloc) { return -1; } int ret = M_genericMap.size() + 1; M_genericMap[str_int_map::key_type(counter)] = ret; bool numeric = true; const char *p = counter; while (*p) { if (!isdigit(*p)) { numeric = false; break; } p++; } if (numeric) { char *s = new char[20]; snprintf(s, 20, "GenericCounter%s", counter); M_revGenericMap[ret] = s; M_genericDisplay[ret] = strdup(counter); } else { M_revGenericMap[ret] = strdup(counter); M_genericDisplay[ret] = strdup(counter); } M_genericCounters = (unsigned long long *)realloc(M_genericCounters, sizeof(unsigned long long) * GENERIC_TYPES * M_genericMap.size()); if (!M_genericCounters) { ERROR("Could not allocate generic counters!"); } M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_C] = 0; M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_PD] = 0; M_genericCounters[(ret - 1)* GENERIC_TYPES + GENERIC_PL] = 0; return ret; } int CStat::findRtd(const char *name, bool start) { str_int_map::iterator it = M_rtdMap.find(str_int_map::key_type(name)); if (it != M_rtdMap.end()) { if (start) { rtd_started[it->first] = true; } else { rtd_stopped[it->first] = true; } return it->second; } int ret = M_rtdMap.size() + 1; M_rtdMap[str_int_map::key_type(name)] = ret; M_revRtdMap[ret] = strdup(name); M_rtdInfo = (unsigned long long *)realloc(M_rtdInfo, sizeof(unsigned long long) * RTD_TYPES * GENERIC_TYPES * M_rtdMap.size()); if (!M_rtdInfo) { ERROR("Could not allocate RTD info!"); } M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; M_ResponseTimeRepartition = (T_dynamicalRepartition **)realloc(M_ResponseTimeRepartition, sizeof(T_dynamicalRepartition *) * M_rtdMap.size()); if (!M_ResponseTimeRepartition) { ERROR("Could not allocate RTD info!"); } M_ResponseTimeRepartition[ret - 1] = NULL; if (start) { rtd_started[name] = true; } else { rtd_stopped[name] = true; } return ret; } int CStat::nRtds() { return M_rtdMap.size(); } /* If you start an RTD, then you should be interested in collecting statistics for it. */ void CStat::validateRtds() { for (str_int_map::iterator it = rtd_started.begin(); it != rtd_started.end(); it++) { str_int_map::iterator stopit = rtd_stopped.find(it->first); if (stopit == rtd_stopped.end() || !stopit->second) { ERROR("You have started Response Time Duration %s, but have never stopped it!", it->first.c_str()); } } } int CStat::computeStat (E_Action P_action, unsigned long P_value, int which) { switch (P_action) { case E_ADD_CALL_DURATION : // Updating Cumulative Counter updateAverageCounter(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength, CPT_C_AverageCallLength_Squares, P_value); updateRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition, P_value); // Updating Periodical Diplayed counter updateAverageCounter(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength, CPT_PD_AverageCallLength_Squares, P_value); // Updating Periodical Logging counter updateAverageCounter(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength, CPT_PL_AverageCallLength_Squares, P_value); break; case E_ADD_GENERIC_COUNTER : M_genericCounters[which * GENERIC_TYPES + GENERIC_C] += P_value; M_genericCounters[which * GENERIC_TYPES + GENERIC_PD] += P_value; M_genericCounters[which * GENERIC_TYPES + GENERIC_PL] += P_value; break; case E_ADD_RESPONSE_TIME_DURATION : // Updating Cumulative Counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); updateRepartition(M_ResponseTimeRepartition[which], M_SizeOfResponseTimeRepartition, P_value); // Updating Periodical Diplayed counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); // Updating Periodical Logging counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } void CStat::updateRepartition(T_dynamicalRepartition* P_tabReport, int P_sizeOfTab, unsigned long P_value) { if(P_tabReport == NULL) { return; } for (int i = 0; i < P_sizeOfTab - 1; i++) { if (P_value < P_tabReport[i].borderMax) { P_tabReport[i].nbInThisBorder++; return; } } /* If this is not true, we never should have gotten here. */ assert(P_value >= P_tabReport[P_sizeOfTab-1].borderMax); P_tabReport[P_sizeOfTab-1].nbInThisBorder ++; } void CStat::resetRepartition(T_dynamicalRepartition* P_tabReport, int P_sizeOfTab) { if(P_tabReport == NULL) { return; } for (int i = 0; i < P_sizeOfTab; i++) { P_tabReport[i].nbInThisBorder = 0; } } CStat::CStat () { size_t L_size = 0; L_size += strlen(DEFAULT_FILE_NAME) ; L_size += strlen(DEFAULT_EXTENSION) ; L_size += 1 ; M_fileName = new char[L_size]; strcpy(M_fileName, DEFAULT_FILE_NAME); strcat(M_fileName, DEFAULT_EXTENSION); M_ResponseTimeRepartition = NULL; M_CallLengthRepartition = NULL; M_SizeOfResponseTimeRepartition = 0; M_SizeOfCallLengthRepartition = 0; M_fileNameRtt = NULL; M_genericCounters = NULL; M_time_ref = 0.0 ; M_dumpRespTime = NULL ; M_counterDumpRespTime = 0 ; M_dumpRespTime = NULL; M_fileNameRtt = NULL; M_rtdInfo = NULL; M_rtpEchoErrors = 0; init(); } char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition, int sizeOfTab, const char * P_repartitionName) { static char *repartitionHeader = NULL; char buffer[MAX_CHAR_BUFFER_SIZE]; int dlen = strlen(stat_delimiter); if(tabRepartition != NULL) { repartitionHeader = (char *)realloc(repartitionHeader, strlen(P_repartitionName) + dlen + 1); sprintf(repartitionHeader, "%s%s", P_repartitionName, stat_delimiter); for(int i=0; i<(sizeOfTab-1); i++) { sprintf(buffer, "%s_<%d%s", P_repartitionName, tabRepartition[i].borderMax, stat_delimiter); repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); strcat(repartitionHeader, buffer); } sprintf(buffer, "%s_>=%d%s", P_repartitionName, tabRepartition[sizeOfTab-1].borderMax, stat_delimiter); repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); strcat(repartitionHeader, buffer); } else { repartitionHeader = (char *)realloc(repartitionHeader, 2); strcpy(repartitionHeader, ""); } return(repartitionHeader); } char* CStat::sRepartitionInfo(T_dynamicalRepartition * tabRepartition, int sizeOfTab) { static char *repartitionInfo; char buffer[MAX_CHAR_BUFFER_SIZE]; int dlen = strlen(stat_delimiter); if(tabRepartition != NULL) { // if a repartition is present, this field match the repartition name repartitionInfo = (char *)realloc(repartitionInfo, dlen + 1); sprintf(repartitionInfo, "%s", stat_delimiter); for(int i=0; i<(sizeOfTab-1); i++) { sprintf(buffer, "%lu%s", tabRepartition[i].nbInThisBorder, stat_delimiter); repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); strcat(repartitionInfo, buffer); } sprintf(buffer, "%lu%s", tabRepartition[sizeOfTab-1].nbInThisBorder, stat_delimiter); repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); strcat(repartitionInfo, buffer); } else { repartitionInfo = (char *)realloc(repartitionInfo, 2); repartitionInfo[0] = '\0'; } return(repartitionInfo); } void CStat::dumpData () { long localElapsedTime, globalElapsedTime ; struct timeval currentTime; float averageCallRate; float realInstantCallRate; unsigned long numberOfCall; // computing the real call rate GET_TIME (¤tTime); globalElapsedTime = computeDiffTimeInMs (¤tTime, &M_startTime); localElapsedTime = computeDiffTimeInMs (¤tTime, &M_plStartTime); // the call rate is for all the call : incoming and outgoing numberOfCall = (M_counters[CPT_C_IncomingCallCreated] + M_counters[CPT_C_OutgoingCallCreated]); averageCallRate = (globalElapsedTime > 0 ? 1000*(float)numberOfCall/(float)globalElapsedTime : 0.0); numberOfCall = (M_counters[CPT_PL_IncomingCallCreated] + M_counters[CPT_PL_OutgoingCallCreated]); realInstantCallRate = (localElapsedTime > 0 ? 1000*(float)numberOfCall / (float)localElapsedTime : 0.0); if(M_outputStream == NULL) { // if the file is still not opened, we opened it now M_outputStream = new ofstream(M_fileName); M_headerAlreadyDisplayed = false; if(M_outputStream == NULL) { cerr << "Unable to open stat file '" << M_fileName << "' !" << endl; exit(EXIT_FATAL_ERROR); } #ifndef __osf__ if(!M_outputStream->is_open()) { cerr << "Unable to open stat file '" << M_fileName << "' !" << endl; exit(EXIT_FATAL_ERROR); } #endif } if(M_headerAlreadyDisplayed == false) { // header - it's dump in file only one time at the beginning of the file (*M_outputStream) << "StartTime" << stat_delimiter << "LastResetTime" << stat_delimiter << "CurrentTime" << stat_delimiter << "ElapsedTime(P)" << stat_delimiter << "ElapsedTime(C)" << stat_delimiter << "TargetRate" << stat_delimiter << "CallRate(P)" << stat_delimiter << "CallRate(C)" << stat_delimiter << "IncomingCall(P)" << stat_delimiter << "IncomingCall(C)" << stat_delimiter << "OutgoingCall(P)" << stat_delimiter << "OutgoingCall(C)" << stat_delimiter << "TotalCallCreated" << stat_delimiter << "CurrentCall" << stat_delimiter << "SuccessfulCall(P)" << stat_delimiter << "SuccessfulCall(C)" << stat_delimiter << "FailedCall(P)" << stat_delimiter << "FailedCall(C)" << stat_delimiter << "FailedCannotSendMessage(P)" << stat_delimiter << "FailedCannotSendMessage(C)" << stat_delimiter << "FailedMaxUDPRetrans(P)" << stat_delimiter << "FailedMaxUDPRetrans(C)" << stat_delimiter << "FailedTcpConnect(P)" << stat_delimiter << "FailedTcpConnect(C)" << stat_delimiter << "FailedTcpClosed(P)" << stat_delimiter << "FailedTcpClosed(C)" << stat_delimiter << "FailedUnexpectedMessage(P)" << stat_delimiter << "FailedUnexpectedMessage(C)" << stat_delimiter << "FailedCallRejected(P)" << stat_delimiter << "FailedCallRejected(C)" << stat_delimiter << "FailedCmdNotSent(P)" << stat_delimiter << "FailedCmdNotSent(C)" << stat_delimiter << "FailedRegexpDoesntMatch(P)" << stat_delimiter << "FailedRegexpDoesntMatch(C)" << stat_delimiter << "FailedRegexpShouldntMatch(P)" << stat_delimiter << "FailedRegexpShouldntMatch(C)" << stat_delimiter << "FailedRegexpHdrNotFound(P)" << stat_delimiter << "FailedRegexpHdrNotFound(C)" << stat_delimiter << "FailedOutboundCongestion(P)" << stat_delimiter << "FailedOutboundCongestion(C)" << stat_delimiter << "FailedTimeoutOnRecv(P)" << stat_delimiter << "FailedTimeoutOnRecv(C)" << stat_delimiter << "FailedTimeoutOnSend(P)" << stat_delimiter << "FailedTimeoutOnSend(C)" << stat_delimiter << "FailedTestDoesntMatch(P)" << stat_delimiter << "FailedTestDoesntMatch(C)" << stat_delimiter << "FailedTestShouldntMatch(P)" << stat_delimiter << "FailedTestShouldntMatch(C)" << stat_delimiter << "FailedStrcmpDoesntMatch(P)" << stat_delimiter << "FailedStrcmpDoesntMatch(C)" << stat_delimiter << "FailedStrcmpShouldntMatch(P)" << stat_delimiter << "FailedStrcmpShouldntMatch(C)" << stat_delimiter << "OutOfCallMsgs(P)" << stat_delimiter << "OutOfCallMsgs(C)" << stat_delimiter << "DeadCallMsgs(P)" << stat_delimiter << "DeadCallMsgs(C)" << stat_delimiter << "Retransmissions(P)" << stat_delimiter << "Retransmissions(C)" << stat_delimiter << "AutoAnswered(P)" << stat_delimiter << "AutoAnswered(C)" << stat_delimiter << "Warnings(P)" << stat_delimiter << "Warnings(C)" << stat_delimiter << "FatalErrors(P)" << stat_delimiter << "FatalErrors(C)" << stat_delimiter << "WatchdogMajor(P)" << stat_delimiter << "WatchdogMajor(C)" << stat_delimiter << "WatchdogMinor(P)" << stat_delimiter << "WatchdogMinor(C)" << stat_delimiter; for (int i = 1; i <= nRtds(); i++) { char s_P[80]; char s_C[80]; sprintf(s_P, "ResponseTime%s(P)%s", M_revRtdMap[i], stat_delimiter); sprintf(s_C, "ResponseTime%s(C)%s", M_revRtdMap[i], stat_delimiter); (*M_outputStream) << s_P << s_C; sprintf(s_P, "ResponseTime%sStDev(P)%s", M_revRtdMap[i], stat_delimiter); sprintf(s_C, "ResponseTime%sStDev(C)%s", M_revRtdMap[i], stat_delimiter); (*M_outputStream) << s_P << s_C; } (*M_outputStream) << "CallLength(P)" << stat_delimiter << "CallLength(C)" << stat_delimiter; (*M_outputStream) << "CallLengthStDev(P)" << stat_delimiter << "CallLengthStDev(C)" << stat_delimiter; for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) { (*M_outputStream) << M_revGenericMap[i] << "(P)" << stat_delimiter; (*M_outputStream) << M_revGenericMap[i] << "(C)" << stat_delimiter; } for (int i = 1; i <= nRtds(); i++) { char s[80]; sprintf(s, "ResponseTimeRepartition%s", M_revRtdMap[i]); (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i - 1], M_SizeOfResponseTimeRepartition, s); } (*M_outputStream) << sRepartitionHeader(M_CallLengthRepartition, M_SizeOfCallLengthRepartition, "CallLengthRepartition"); (*M_outputStream) << endl; M_headerAlreadyDisplayed = true; } // content (*M_outputStream) << formatTime(&M_startTime) << stat_delimiter; (*M_outputStream) << formatTime(&M_plStartTime) << stat_delimiter; (*M_outputStream) << formatTime(¤tTime) << stat_delimiter << msToHHMMSS(localElapsedTime) << stat_delimiter; (*M_outputStream) << msToHHMMSS(globalElapsedTime) << stat_delimiter; if (users >= 0) { (*M_outputStream) << users << stat_delimiter; } else { (*M_outputStream) << rate << stat_delimiter; } (*M_outputStream) << realInstantCallRate << stat_delimiter << averageCallRate << stat_delimiter << M_counters[CPT_PL_IncomingCallCreated] << stat_delimiter << M_counters[CPT_C_IncomingCallCreated] << stat_delimiter << M_counters[CPT_PL_OutgoingCallCreated] << stat_delimiter << M_counters[CPT_C_OutgoingCallCreated] << stat_delimiter << (M_counters[CPT_C_IncomingCallCreated]+ M_counters[CPT_C_OutgoingCallCreated])<< stat_delimiter << M_counters[CPT_C_CurrentCall] << stat_delimiter << M_counters[CPT_PL_SuccessfulCall] << stat_delimiter << M_counters[CPT_C_SuccessfulCall] << stat_delimiter << M_counters[CPT_PL_FailedCall] << stat_delimiter << M_counters[CPT_C_FailedCall] << stat_delimiter << M_counters[CPT_PL_FailedCallCannotSendMessage] << stat_delimiter << M_counters[CPT_C_FailedCallCannotSendMessage] << stat_delimiter << M_counters[CPT_PL_FailedCallMaxUdpRetrans] << stat_delimiter << M_counters[CPT_C_FailedCallMaxUdpRetrans ] << stat_delimiter << M_counters[CPT_PL_FailedCallTcpConnect] << stat_delimiter << M_counters[CPT_C_FailedCallTcpConnect] << stat_delimiter << M_counters[CPT_PL_FailedCallTcpClosed] << stat_delimiter << M_counters[CPT_C_FailedCallTcpClosed] << stat_delimiter << M_counters[CPT_PL_FailedCallUnexpectedMessage] << stat_delimiter << M_counters[CPT_C_FailedCallUnexpectedMessage] << stat_delimiter << M_counters[CPT_PL_FailedCallCallRejected] << stat_delimiter << M_counters[CPT_C_FailedCallCallRejected] << stat_delimiter << M_counters[CPT_PL_FailedCallCmdNotSent] << stat_delimiter << M_counters[CPT_C_FailedCallCmdNotSent] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpShouldntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpHdrNotFound] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpHdrNotFound] << stat_delimiter << M_counters[CPT_PL_FailedOutboundCongestion] << stat_delimiter << M_counters[CPT_C_FailedOutboundCongestion] << stat_delimiter << M_counters[CPT_PL_FailedTimeoutOnRecv] << stat_delimiter << M_counters[CPT_C_FailedTimeoutOnRecv] << stat_delimiter << M_counters[CPT_PL_FailedTimeoutOnSend] << stat_delimiter << M_counters[CPT_C_FailedTimeoutOnSend] << stat_delimiter << M_counters[CPT_PL_FailedCallTestDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallTestDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallTestShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallTestShouldntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallStrcmpDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallStrcmpDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallStrcmpShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallStrcmpShouldntMatch] << stat_delimiter << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_counters[CPT_PL_Retransmissions] << stat_delimiter << M_counters[CPT_C_Retransmissions] << stat_delimiter << M_G_counters[CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_Warnings - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_Warnings - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter; // SF917289 << M_counters[CPT_C_UnexpectedMessage] << stat_delimiter; for (int i = 1; i <= nRtds(); i++) { (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdMean(i, GENERIC_PL)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdMean(i, GENERIC_C)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdStdev(i, GENERIC_PL)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdStdev(i, GENERIC_C)) << stat_delimiter; } (*M_outputStream) << msToHHMMSSus( (unsigned long)computeMean(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength) ) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeMean(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength) ) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeStdev(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength, CPT_PL_AverageCallLength_Squares )) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeStdev(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength, CPT_C_AverageCallLength_Squares )) << stat_delimiter; for (unsigned int i = 0; i < M_genericMap.size(); i++) { (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_PL] << stat_delimiter; (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_C] << stat_delimiter; } for (int i = 0; i < nRtds(); i++) { (*M_outputStream) << sRepartitionInfo(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); } (*M_outputStream) << sRepartitionInfo(M_CallLengthRepartition, M_SizeOfCallLengthRepartition); (*M_outputStream) << endl; // flushing the output file to let the tail -f working ! (*M_outputStream).flush(); } /* end of logData () */ void CStat::dumpDataRtt () { if (M_counterDumpRespTime == 0) { return; } if(M_outputStreamRtt == NULL) { // if the file is still not opened, we opened it now M_outputStreamRtt = new ofstream(M_fileNameRtt); M_headerAlreadyDisplayedRtt = false; if(M_outputStreamRtt == NULL) { cerr << "Unable to open rtt file '" << M_fileNameRtt << "' !" << endl; exit(EXIT_FATAL_ERROR); } #ifndef __osf__ if(!M_outputStreamRtt->is_open()) { cerr << "Unable to open rtt file '" << M_fileNameRtt << "' !" << endl; exit(EXIT_FATAL_ERROR); } #endif } if(M_headerAlreadyDisplayedRtt == false) { (*M_outputStreamRtt) << "Date_ms" << stat_delimiter << "response_time_ms" << stat_delimiter << "rtd_no" << endl; M_headerAlreadyDisplayedRtt = true; } for (unsigned int L_i = 0; L_i < M_counterDumpRespTime ; L_i ++) { (*M_outputStreamRtt) << M_dumpRespTime[L_i].date << stat_delimiter ; (*M_outputStreamRtt) << M_dumpRespTime[L_i].rtt << stat_delimiter ; (*M_outputStreamRtt) << M_revRtdMap[M_dumpRespTime[L_i].rtd_no] << endl; (*M_outputStreamRtt).flush(); M_dumpRespTime[L_i].date = 0.0; M_dumpRespTime[L_i].rtt = 0.0; M_dumpRespTime[L_i].rtd_no = 0; } // flushing the output file (*M_outputStreamRtt).flush(); M_counterDumpRespTime = 0; } /* Time Gestion */ char* CStat::msToHHMMSS (unsigned long P_ms) { static char L_time [TIME_LENGTH]; unsigned long hh, mm, ss; P_ms = P_ms / 1000; hh = P_ms / 3600; mm = (P_ms - hh * 3600) / 60; ss = P_ms - (hh * 3600) - (mm * 60); sprintf (L_time, "%2.2lu:%2.2lu:%2.2lu", hh, mm, ss); return (L_time); } /* end of msToHHMMSS */ char* CStat::msToHHMMSSus (unsigned long P_ms) { static char L_time [TIME_LENGTH]; unsigned long sec, hh, mm, ss, us; sec = P_ms / 1000; hh = sec / 3600; mm = (sec - hh * 3600) / 60; ss = sec - (hh * 3600) - (mm * 60); us = 1000*(P_ms - (hh * 3600000) - (mm * 60000) - (ss*1000)); sprintf (L_time, "%2.2lu:%2.2lu:%2.2lu:%06lu", hh, mm, ss, us); return (L_time); } /* end of msToHHMMSSus */ char* CStat::formatTime (struct timeval* P_tv, bool with_epoch) { static char L_time [TIME_LENGTH]; struct tm * L_currentDate; // Get the current date and time L_currentDate = localtime ((const time_t *)&P_tv->tv_sec); // Format the time if (L_currentDate == NULL) { memset (L_time, 0, TIME_LENGTH); } else { if (with_epoch) { sprintf(L_time, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%06ld", L_currentDate->tm_year + 1900, L_currentDate->tm_mon + 1, L_currentDate->tm_mday, L_currentDate->tm_hour, L_currentDate->tm_min, L_currentDate->tm_sec, (long)P_tv->tv_usec); } else { sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d.%06ld\t%10.10ld.%06ld", L_currentDate->tm_year + 1900, L_currentDate->tm_mon + 1, L_currentDate->tm_mday, L_currentDate->tm_hour, L_currentDate->tm_min, L_currentDate->tm_sec, (long)P_tv->tv_usec, (long)P_tv->tv_sec, /* time_t is int on some bsds */ (long)P_tv->tv_usec); } } return (L_time); } /* end of formatTime */ long CStat::computeDiffTimeInMs (struct timeval* tf, struct timeval* ti) { long v1, v2; v1 = tf->tv_sec - ti->tv_sec; v2 = tf->tv_usec - ti->tv_usec; if (v2 < 0) v2 += 1000000, v1--; return (v1*1000 + v2/1000); } CSample::~CSample() { } /* Implementation of a fixed distribution. */ CFixed::CFixed(double value) { this->value = value; } double CFixed::sample() { return value; } int CFixed::textDescr(char *s, int len) { return snprintf(s, len, "%lf", value); } int CFixed::timeDescr(char *s, int len) { return time_string(value, s, len); } double CFixed::cdfInv(double /*percentile*/) { return value; } /* Implementation of the default pause time. */ CDefaultPause::CDefaultPause() { } double CDefaultPause::sample() { return (double)duration; } int CDefaultPause::textDescr(char *s, int len) { return snprintf(s, len, "%d", duration); } int CDefaultPause::timeDescr(char *s, int len) { return time_string(duration, s, len); } double CDefaultPause::cdfInv(double /*percentile*/) { return duration; } /* Implementation of a uniform distribution. */ static bool uniform_init = false; CUniform::CUniform(double min, double max) { if (!uniform_init) { uniform_init = true; srand(time(NULL)); } this->min = min; this->max = max; } double CUniform::sample() { double rval = ((double)rand())/((double)RAND_MAX); return min + (rval * (max - min)); } int CUniform::textDescr(char *s, int len) { return snprintf(s, len, "%lf/%lf", min, max); } int CUniform::timeDescr(char *s, int len) { int used = time_string(min, s, len); used += snprintf(s + used, len - used, "/"); used += time_string(max, s + used, len - used); return used; } double CUniform::cdfInv(double percentile) { return min + (max * percentile); } #ifdef HAVE_GSL gsl_rng *gsl_init() { static gsl_rng *rng = NULL; if (rng) { return rng; } gsl_rng_env_setup(); rng = gsl_rng_alloc(gsl_rng_default); if (!rng) { ERROR("Could not initialize GSL random number generator"); } return rng; } /* Normal distribution. */ CNormal::CNormal(double mean, double stdev) { this->mean = mean; this->stdev = stdev; rng = gsl_init(); } double CNormal::sample() { double val = gsl_ran_gaussian(rng, stdev); return val + mean; } int CNormal::textDescr(char *s, int len) { return snprintf(s, len, "N(%.3lf,%.3lf)", mean, stdev); } int CNormal::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "N("); used += time_string(mean, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(stdev, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CNormal::cdfInv(double percentile) { return mean + gsl_cdf_gaussian_Pinv(percentile, stdev); } /* Lognormal distribution. */ double CLogNormal::sample() { return gsl_ran_lognormal(rng, mean, stdev); } int CLogNormal::textDescr(char *s, int len) { if (len == 0) return 0; s[0] = 'L'; return 1 + this->CNormal::textDescr(s + 1, len - 1); } int CLogNormal::timeDescr(char *s, int len) { if (len == 0) return 0; s[0] = 'L'; return 1 + this->CNormal::timeDescr(s + 1, len - 1); } double CLogNormal::cdfInv(double percentile) { return gsl_cdf_lognormal_Pinv(percentile, mean, stdev); } /* Exponential distribution. */ CExponential::CExponential(double mean) { this->mean = mean; rng = gsl_init(); } double CExponential::sample() { return gsl_ran_exponential(rng, mean); } int CExponential::textDescr(char *s, int len) { return snprintf(s, len, "Exp(%lf)", mean); } int CExponential::timeDescr(char *s, int len) { int used = snprintf(s, len, "Exp("); used += time_string(mean, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CExponential::cdfInv(double percentile) { return gsl_cdf_exponential_Pinv(percentile, mean); } /* Weibull distribution. */ CWeibull::CWeibull(double lambda, double k) { this->lambda = lambda; this->k = k; rng = gsl_init(); } double CWeibull::sample() { return gsl_ran_weibull(rng, lambda, k); } int CWeibull::textDescr(char *s, int len) { return snprintf(s, len, "Wb(%.3lf,%.3lf)", lambda, k); } int CWeibull::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "Wb("); used += time_string(lambda, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CWeibull::cdfInv(double percentile) { return gsl_cdf_weibull_Pinv(percentile, lambda, k); } /* Pareto distribution. */ CPareto::CPareto(double k, double xsubm) { this->k = k; this->xsubm = xsubm; rng = gsl_init(); } double CPareto::sample() { return gsl_ran_pareto(rng, k, xsubm); } int CPareto::textDescr(char *s, int len) { return snprintf(s, len, "P(%.3lf,%.3lf)", k, xsubm); } int CPareto::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "P("); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(xsubm, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CPareto::cdfInv(double percentile) { return gsl_cdf_pareto_Pinv(percentile, k, xsubm); } /* Generalized Pareto distribution. */ CGPareto::CGPareto(double shape, double scale, double location) { this->shape = shape; this->scale = scale; this->location = location; rng = gsl_init(); } double CGPareto::sample() { return cdfInv(gsl_ran_flat(rng, 0.0, 1.0)); } int CGPareto::textDescr(char *s, int len) { return snprintf(s, len, "P(%.3lf,%.3lf,%.3f)", shape, scale, location); } int CGPareto::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "P("); used += time_string(shape, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(scale, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(location, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CGPareto::cdfInv(double percentile) { return location + ((scale * (pow(percentile, -shape) - 1))/shape); } /* Gamma distribution. */ CGamma::CGamma(double k, double theta) { this->k = k; this->theta = theta; rng = gsl_init(); } double CGamma::sample() { return gsl_ran_gamma(rng, k, theta); } int CGamma::textDescr(char *s, int len) { return snprintf(s, len, "G(%.3lf,%.3lf)", k, theta); } int CGamma::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "G("); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(theta, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CGamma::cdfInv(double percentile) { return gsl_cdf_gamma_Pinv(percentile, k, theta); } /* NegBin distribution. */ CNegBin::CNegBin(double p, double n) { this->p = p; this->n = n; rng = gsl_init(); } double CNegBin::sample() { return gsl_ran_negative_binomial(rng, n, p); } int CNegBin::textDescr(char *s, int len) { return snprintf(s, len, "NB(%.3lf,%.3lf)", p, n); } int CNegBin::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "NB("); used += time_string(p, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(n, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } /* We really don't implement this, but should so that sanity checking will * work. For now, just return zero. */ double CNegBin::cdfInv(double percentile) { return 0; } #endif sipp-3.7.2/src/strings.cpp0000664000000000000000000001445214525516253012370 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include "strings.hpp" #include #include #include #include void get_host_and_port(const char * addr, char * host, int * port) { /* Separate the port number (if any) from the host name. * Thing is, the separator is a colon (':'). The colon may also exist * in the host portion if the host is specified as an IPv6 address (see * RFC 2732). If that's the case, then we need to skip past the IPv6 * address, which should be contained within square brackets ('[',']'). */ const char *has_brackets; int len; int port_result = 0; has_brackets = strchr(addr, '['); if (has_brackets != NULL) { has_brackets = strchr(has_brackets, ']'); } if (has_brackets == NULL) { /* addr is not a []-enclosed IPv6 address, but might still be IPv6 (without * a port), or IPv4 or a hostname (with or without a port) */ char *first_colon_location; char *second_colon_location; len = strlen(addr) + 1; memmove(host, addr, len); first_colon_location = strchr(host, ':'); if (first_colon_location == NULL) { /* No colon - just set the port to 0 */ port_result = 0; } else { second_colon_location = strchr(first_colon_location + 1, ':'); if (second_colon_location != NULL) { /* Found a second colon in addr - so this is an IPv6 address * without a port. Set the port to 0 */ port_result = 0; } else { /* IPv4 address or hostname with a colon in it - convert the colon to * a NUL terminator, and set the value after it as the port */ *first_colon_location = '\0'; port_result = atol(first_colon_location + 1); } } } else { /* If '['..']' found, */ const char *initial_bracket; /* extract the remote_host */ char *second_bracket; char *colon_before_port; initial_bracket = strchr( addr, '[' ); initial_bracket++; /* Step forward one character */ len = strlen(initial_bracket) + 1; memmove(host, initial_bracket, len); second_bracket = strchr( host, ']' ); *second_bracket = '\0'; /* Check for a port specified after the ] */ colon_before_port = strchr(second_bracket + 1, ':'); if (colon_before_port != NULL) { port_result = atol(colon_before_port + 1); } else { port_result = 0; } } // Set the port argument if it wasn't NULL if (port != NULL) { *port = port_result; } } int get_decimal_from_hex(char hex) { if (isdigit(hex)) return hex - '0'; else return tolower(hex) - 'a' + 10; } void trim(char *s) { char *p = s; while(isspace(*p)) { p++; } int l = strlen(p); for (int i = l - 1; i >= 0 && isspace(p[i]); i--) { p[i] = '\0'; } memmove(s, p, l + 1); } #ifdef GTEST #include "gtest/gtest.h" TEST(GetHostAndPort, IPv6) { int port_result = -1; char host_result[255]; get_host_and_port("fe80::92a4:deff:fe74:7af5", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv6Brackets) { int port_result = -1; char host_result[255]; get_host_and_port("[fe80::92a4:deff:fe74:7af5]", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv6BracketsAndPort) { int port_result = -1; char host_result[255]; get_host_and_port("[fe80::92a4:deff:fe74:7af5]:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv4) { int port_result = -1; char host_result[255]; get_host_and_port("127.0.0.1", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, IPv4AndPort) { int port_result = -1; char host_result[255]; get_host_and_port("127.0.0.1:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, IgnorePort) { char host_result[255]; get_host_and_port("127.0.0.1", host_result, NULL); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, DNS) { int port_result = -1; char host_result[255]; get_host_and_port("sipp.sf.net", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("sipp.sf.net", host_result); } TEST(GetHostAndPort, DNSAndPort) { int port_result = -1; char host_result[255]; get_host_and_port("sipp.sf.net:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("sipp.sf.net", host_result); } #endif //GTEST sipp-3.7.2/src/task.cpp0000664000000000000000000002127614525516253011643 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include #include "sipp.hpp" task_list all_tasks; task_list running_tasks; timewheel paused_tasks; /* Get the overall list of running tasks. */ task_list* get_running_tasks() { return &running_tasks; } void abort_all_tasks() { for (task_list::iterator task_it = all_tasks.begin(); task_it != all_tasks.end(); task_it = all_tasks.begin()) { (*task_it)->abort(); } } void dump_tasks() { WARNING("---- %zu Active Tasks ----", all_tasks.size()); for (task_list::iterator task_it = all_tasks.begin(); task_it != all_tasks.end(); task_it++) { (*task_it)->dump(); } } int expire_paused_tasks() { return paused_tasks.expire_paused_tasks(); } int paused_tasks_count() { return paused_tasks.size(); } // Methods for the task class task::task() { this->taskit = all_tasks.insert(all_tasks.end(), this); add_to_runqueue(); } task::~task() { if (running) { remove_from_runqueue(); } else { paused_tasks.remove_paused_task(this); } all_tasks.erase(taskit); } /* Put this task in the run queue. */ void task::add_to_runqueue() { this->runit = running_tasks.insert(running_tasks.end(), this); this->running = true; } void task::add_to_paused_tasks(bool increment) { paused_tasks.add_paused_task(this, increment); } void task::recalculate_wheel() { add_to_paused_tasks(false); } /* Remove this task from the run queue. */ bool task::remove_from_runqueue() { if (!this->running) { return false; } running_tasks.erase(this->runit); this->running = false; return true; } void task::setRunning() { if (!running) { paused_tasks.remove_paused_task(this); add_to_runqueue(); } } void task::setPaused() { if (running) { if (!remove_from_runqueue()) { WARNING("Tried to remove a running call that wasn't running!"); assert(0); } } else { paused_tasks.remove_paused_task(this); } assert(running == false); add_to_paused_tasks(true); } void task::abort() { delete this; } // Methods for the timewheel class // Based on the time a given task should next be woken up, finds the // correct time wheel for it and returns a list of other tasks // occuring at that point. task_list *timewheel::task2list(task *task) { unsigned int wake = task->wake(); if (wake == 0) { return &forever_list; } assert(wake >= wheel_base); if (wheel_base > clock_tick) { ERROR("wheel_base is %lu, clock_tick is %lu - expected wheel_base to be less than or equal to clock_tick", wheel_base, clock_tick); assert(wheel_base <= clock_tick); } unsigned int slot_in_first_wheel = wake % LEVEL_ONE_SLOTS; unsigned int slot_in_second_wheel = (wake / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS; unsigned int slot_in_third_wheel = (wake / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS)); bool fits_in_first_wheel = ((wake / LEVEL_ONE_SLOTS) == (wheel_base / LEVEL_ONE_SLOTS)); bool fits_in_second_wheel = ((wake / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS)) == (wheel_base / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS))); bool fits_in_third_wheel = (slot_in_third_wheel < LEVEL_THREE_SLOTS); if (fits_in_first_wheel) { return &wheel_one[slot_in_first_wheel]; } else if (fits_in_second_wheel) { return &wheel_two[slot_in_second_wheel]; } else if (fits_in_third_wheel) { return &wheel_three[slot_in_third_wheel]; } else{ ERROR("Attempted to schedule a task too far in the future"); return NULL; } } /* Iterate through our sorted set of paused tasks, removing those that * should no longer be paused, and adding them to the run queue. */ int timewheel::expire_paused_tasks() { int found = 0; // This while loop counts up from the wheel_base (i.e. the time // this function last ran) to the current scheduler time (i.e. clock_tick). while (wheel_base < clock_tick) { int slot1 = wheel_base % LEVEL_ONE_SLOTS; /* If slot1 is 0 (i.e. wheel_base is a multiple of 4096ms), * we need to repopulate the first timer wheel with the * contents of the first available slot of the second wheel. */ if (slot1 == 0) { /* slot2 represents the slot in the second timer wheel * containing the tasks for the next ~4s. So when * wheel_base is 4096, wheel2[1] will be moved into wheel 1, * when wheel_base of 8192 wheel2[2] will be moved into * wheel 1, etc. */ int slot2 = (wheel_base / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS; /* If slot2 is also zero, we must migrate tasks from slot3 into slot2. */ if (slot2 == 0) { /* Same logic above, except that each slot of wheel3 contains the next 69 minutes of tasks, enough to completely fill wheel 2. */ int slot3 = ((wheel_base / LEVEL_ONE_SLOTS) / LEVEL_TWO_SLOTS); assert(slot3 < LEVEL_THREE_SLOTS); for (task_list::iterator l3it = wheel_three[slot3].begin(); l3it != wheel_three[slot3].end(); l3it++) { /* Migrate this task to wheel two. */ (*l3it)->recalculate_wheel(); } wheel_three[slot3].clear(); } /* Repopulate wheel 1 from wheel 2 (which will now be full of the tasks pulled from wheel 3, if that was necessary) */ for (task_list::iterator l2it = wheel_two[slot2].begin(); l2it != wheel_two[slot2].end(); l2it++) { /* Migrate this task to wheel one. */ (*l2it)->recalculate_wheel(); } wheel_two[slot2].clear(); } /* Move tasks from the current slot of wheel 1 (i.e. the tasks scheduled to fire in the 1ms interval represented by wheel_base) onto a run queue. */ found += wheel_one[slot1].size(); for(task_list::iterator it = wheel_one[slot1].begin(); it != wheel_one[slot1].end(); it++) { (*it)->add_to_runqueue(); // Decrement the total number of tasks in this wheel. count--; } wheel_one[slot1].clear(); wheel_base++; // Move wheel_base to the next 1ms interval } return found; } // Adds a task to the correct timewheel. When increment is false, does // not increment the count of tasks owned by this timewheel, and so // can be used for recalculating the wheel of an existing task. void timewheel::add_paused_task(task *task, bool increment) { task_list::iterator task_it; if (task->wake() && task->wake() < wheel_base) { task->add_to_runqueue(); return; } task_list *list = task2list(task); task_it = list->insert(list->end(), task); task->pauselist = list; task->pauseit = task_it; if (increment) { count++; } } void timewheel::remove_paused_task(task *task) { task_list *list = task->pauselist; list->erase(task->pauseit); count--; } timewheel::timewheel() { count = 0; wheel_base = clock_tick; } int timewheel::size() { return count; } sipp-3.7.2/src/time.cpp0000664000000000000000000000640314525516253011632 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken */ #include #include #include #if defined(__MACH__) && defined(__APPLE__) #include #include #endif #include "time.hpp" #include "sipp.hpp" #define MICROSECONDS_PER_SECOND 1000000LL #define MICROSECONDS_PER_MILLISECOND 1000LL #define NANOSECONDS_PER_MICROSECOND 1000LL // Returns the number of microseconds that have passed since SIPp // started. Also updates the current clock_tick. unsigned long long getmicroseconds() { struct timespec time; unsigned long long microseconds; static unsigned long long start_time = 0; #if defined(__MACH__) && defined(__APPLE__) // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); time.tv_sec = mts.tv_sec; time.tv_nsec = mts.tv_nsec; #else #if defined(CLOCK_MONOTONIC_COARSE) clock_gettime(CLOCK_MONOTONIC_COARSE, &time); #else clock_gettime(CLOCK_MONOTONIC, &time); #endif #endif microseconds = (MICROSECONDS_PER_SECOND * time.tv_sec) + (time.tv_nsec / NANOSECONDS_PER_MICROSECOND); if (start_time == 0) { start_time = microseconds - 1; } microseconds = microseconds - start_time; return microseconds; } void update_clock_tick() { clock_tick = getmilliseconds(); } // Returns the number of milliseconds that have passed since SIPp // started. Also updates the current clock_tick. unsigned long getmilliseconds() { return getmicroseconds() / MICROSECONDS_PER_MILLISECOND; } // Sleeps for the given number of microseconds. Avoids the potential // EINVAL when using usleep() to sleep for a second or more. void sipp_usleep(unsigned long usec) { if (usec >= 1000000) { sleep(usec / 1000000); } usec %= 1000000; usleep(usec); } sipp-3.7.2/src/urlcoder.cpp0000664000000000000000000000513114525516253012510 0ustar /* * 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 * * Author : Jérôme Poulin - 20 Apr 2021 */ #include #include #include std::string url_encode(const std::string &str) { std::string new_str; unsigned char c; int ic; const char *chars = str.c_str(); char bufHex[10]; size_t len = strlen(chars); for (unsigned int i = 0; i < len; i++) { c = chars[i]; ic = c; if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { new_str += c; } else { sprintf(bufHex, "%X", c); if (ic < 16) { new_str += "%0"; } else { new_str += "%"; } new_str += bufHex; } } return new_str; } std::string url_decode(std::string str) { std::string ret; char ch; size_t len = str.length(); for (unsigned int i = 0; i < len; i++) { unsigned int ii; if (str[i] != '%') { if (str[i] == '+') { ret += ' '; } else { ret += str[i]; } } else { sscanf(str.substr(i + 1, 2).c_str(), "%x", &ii); ch = static_cast(ii); ret += ch; i = i + 2; } } return ret; } #ifdef GTEST #include "gtest/gtest.h" TEST(url_encode, encoded_string_contains_entities_if_needed) { ASSERT_EQ(url_encode("user1@127.0.0.1:5060"), "user1%40127.0.0.1%3A5060"); ASSERT_EQ(url_encode("string with spaces"), "string%20with%20spaces"); ASSERT_EQ(url_encode("alphanum123"), "alphanum123"); ASSERT_EQ(url_encode("ûtf8"), "%C3%BBtf8"); } TEST(url_decode, decoded_string_contains_no_entities) { ASSERT_EQ(url_decode("user1%40127%2E0%2e0%2e1%3a5060"), "user1@127.0.0.1:5060"); ASSERT_EQ(url_decode("string%20with%20spaces"), "string with spaces"); ASSERT_EQ(url_decode("%C3%bbtf8"), "ûtf8"); } #endif // GTEST sipp-3.7.2/src/variables.cpp0000664000000000000000000002150514525516253012644 0ustar /* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * */ #include "sipp.hpp" /* __________________________________________________________________________ C L A S S C C a l l V a r i a b l e __________________________________________________________________________ */ bool CCallVariable::isSet() { if (M_type == E_VT_REGEXP) { if(M_nbOfMatchingValue >= 1) return(true); else return(false); } else if (M_type == E_VT_BOOL) { return M_bool; } else if (M_type == E_VT_DOUBLE) { return M_double; } return (M_type != E_VT_UNDEFINED); } bool CCallVariable::isDouble() { return (M_type == E_VT_DOUBLE); } bool CCallVariable::isBool() { return (M_type == E_VT_BOOL); } bool CCallVariable::isRegExp() { return (M_type == E_VT_REGEXP); } bool CCallVariable::isString() { return (M_type == E_VT_STRING); } // WARNING : setMatchingValue does't allocate the memory for the matching value // but the destructor free the memory void CCallVariable::setMatchingValue(char* P_matchingVal) { M_type = E_VT_REGEXP; if(M_matchingValue != NULL) { delete [] M_matchingValue; } M_matchingValue = P_matchingVal; M_nbOfMatchingValue++; } char* CCallVariable::getMatchingValue() { if (M_type != E_VT_REGEXP) { return NULL; } return(M_matchingValue); } void CCallVariable::setDouble(double val) { M_type = E_VT_DOUBLE; M_double = val; } double CCallVariable::getDouble() { if (M_type != E_VT_DOUBLE) { return 0.0; } return(M_double); } void CCallVariable::setString(char *P_val) { M_type = E_VT_STRING; free(M_stringValue); M_stringValue = P_val; } char *CCallVariable::getString() { if (M_type == E_VT_STRING) { return(M_stringValue); } else if (M_type == E_VT_REGEXP && M_matchingValue) { return(M_matchingValue); } else { return const_cast(""); /* BUG BUT NOT SO SERIOUS */ } } /* Convert this variable to a double. Returns true on success, false on failure. */ bool CCallVariable::toDouble(double *newValue) { char *p; switch(M_type) { case E_VT_REGEXP: if(M_nbOfMatchingValue < 1) { return false; } *newValue = strtod(M_matchingValue, &p); if (*p) { return false; } break; case E_VT_STRING: *newValue = strtod(M_stringValue, &p); if (*p) { return false; } break; case E_VT_DOUBLE: *newValue = getDouble(); break; case E_VT_BOOL: *newValue = (double)getBool(); break; default: return false; } return true; } void CCallVariable::setBool(bool val) { M_type = E_VT_BOOL; M_bool = val; } bool CCallVariable::getBool() { if (M_type != E_VT_BOOL) { return false; } return(M_bool); } // Constuctor and destructor CCallVariable::CCallVariable() { M_matchingValue = NULL; M_stringValue = NULL; M_nbOfMatchingValue = 0; M_type = E_VT_UNDEFINED; } CCallVariable::~CCallVariable() { if(M_matchingValue != NULL) { delete [] M_matchingValue; } M_matchingValue = NULL; free(M_stringValue); } #define LEVEL_BITS 8 VariableTable::VariableTable(VariableTable *parent, int size) { if (parent) { level = parent->level + 1; assert(level < (1 << LEVEL_BITS)); this->parent = parent->getTable(); } else { level = 0; this->parent = NULL; } count = 1; this->size = size; if (size == 0) { variableTable = NULL; return; } variableTable = (CCallVariable **)malloc(size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not allocate variable table!"); } for (int i = 0; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } } VariableTable::VariableTable(AllocVariableTable *src) { count = 1; this->level = src->level; if (src->parent) { this->parent = src->parent->getTable(); } else { this->parent = NULL; } if (level > 0) { assert(this->parent); } this->size = src->size; if (size == 0) { variableTable = NULL; return; } variableTable = (CCallVariable **)malloc(size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not allocate variable table!"); } for (int i = 0; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } } void VariableTable::expand(int size) { assert(size > this->size); if (size == this->size) { return; } variableTable = (CCallVariable **)realloc(variableTable, size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not expand variable table!"); } for (int i = this->size; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } this->size = size; } VariableTable::~VariableTable() { if (parent) { parent->putTable(); } for (int i = 0; i < size; i++) { delete variableTable[i]; } free(variableTable); } VariableTable *VariableTable::getTable() { count++; return this; } void VariableTable::putTable() { if (--count == 0) { delete this; } } CCallVariable *VariableTable::getVar(int i) { int thisLevel = i & ((1 << LEVEL_BITS) - 1); assert(thisLevel <= level); if (thisLevel == level) { i = i >> LEVEL_BITS; assert(i > 0); assert(i <= size ); return variableTable[i - 1]; } assert(parent); return parent->getVar(i); } AllocVariableTable::AllocVariableTable(AllocVariableTable *av_parent) : VariableTable((VariableTable *)av_parent, 0) { this->av_parent = av_parent; } AllocVariableTable::~AllocVariableTable() { clear_str_int(variableMap); clear_int_str(variableRevMap); clear_int_int(variableReferences); } int AllocVariableTable::find(const char *varName, bool allocate) { /* If this variable has already been used, then we have nothing to do. */ str_int_map::iterator var_it = variableMap.find(varName); if (var_it != variableMap.end()) { variableReferences[var_it->second]++; return var_it->second; } if (av_parent) { int ret = av_parent->find(varName, false); if (ret > 0) { return ret; } } if (allocate) { int varNum = size + 1; expand(varNum); varNum = (varNum << LEVEL_BITS) | level; variableMap[varName] = varNum; variableReferences[varNum] = 1; variableRevMap[varNum] = strdup(varName); return varNum; } return -1; } char *AllocVariableTable::getName(int i) { int thisLevel = i & ((1 << LEVEL_BITS) - 1); assert(thisLevel <= level); if (thisLevel == level) { return variableRevMap[i]; } assert(av_parent); return av_parent->getName(i); } void AllocVariableTable::dump() { if (av_parent) { av_parent->dump(); } WARNING("%zu level %d variables:", variableMap.size(), level); for (str_int_map::iterator i = variableMap.begin(); i != variableMap.end(); i++) { WARNING("%s", i->first.c_str()); } } void AllocVariableTable::validate() { for (str_int_map::iterator var_it = variableMap.begin(); var_it != variableMap.end(); var_it++) { if (variableReferences[var_it->second] < 2) { const char *varName = var_it->first.c_str(); int varRef = variableReferences[var_it->second]; if (strcmp(varName, "_") != 0) { ERROR("Variable $%s is referenced %d times!", varName, varRef); } } } if (av_parent) { av_parent->validate(); } } sipp-3.7.2/src/watchdog.cpp0000664000000000000000000001162714525516253012500 0ustar /* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" void watchdog::dump() { WARNING("Watchdog Task: interval = %d, major_threshold = %d (%d triggers left), minor_threshold = %d (%d triggers left)", interval, major_threshold, major_maxtriggers, minor_threshold, minor_maxtriggers); } watchdog::watchdog(int interval, int reset_interval, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers) { this->interval = interval; this->reset_interval = reset_interval; this->major_threshold = major_threshold; this->major_maxtriggers = major_maxtriggers; this->minor_threshold = minor_threshold; this->minor_maxtriggers = minor_maxtriggers; major_triggers = 0; minor_triggers = 0; last_trigger = last_fire = getmilliseconds(); } bool watchdog::run() { update_clock_tick(); unsigned expected_major_trigger_time = last_fire + this->major_threshold; unsigned expected_minor_trigger_time = last_fire + this->minor_threshold; bool major_watchdog_tripped = clock_tick > expected_major_trigger_time; bool minor_watchdog_tripped = clock_tick > expected_minor_trigger_time; // Check if either watchdog has taken longer than expected to run, // and if so, warn that we are overloaded. if (major_watchdog_tripped) { major_triggers++; CStat::globalStat(CStat::E_WATCHDOG_MAJOR); last_trigger = clock_tick; WARNING("Overload warning: the major watchdog timer %dms has been tripped (%lu), %d trips remaining.", major_threshold, clock_tick - last_fire, major_maxtriggers - major_triggers); } else if (minor_watchdog_tripped) { minor_triggers++; last_trigger = clock_tick; CStat::globalStat(CStat::E_WATCHDOG_MINOR); WARNING("Overload warning: the minor watchdog timer %dms has been tripped (%lu), %d trips remaining.", minor_threshold, clock_tick - last_fire, minor_maxtriggers - minor_triggers); } bool major_watchdog_failure = ((this->major_maxtriggers != -1) && (major_triggers > this->major_maxtriggers)); bool minor_watchdog_failure = ((this->minor_maxtriggers != -1) && (minor_triggers > this->minor_maxtriggers)); // If the watchdogs have tripped too many times, end the SIPp run. if (major_watchdog_failure) { ERROR("Overload error: the watchdog timer has tripped the major threshold of %dms too many times (%d out of %d allowed) (%d out of %d minor %dms timeouts tripped)", major_threshold, major_triggers, major_maxtriggers, minor_triggers, minor_maxtriggers, minor_threshold); } else if (minor_watchdog_failure) { ERROR("Overload error: the watchdog timer has tripped the minor threshold of %dms too many times (%d out of %d allowed) (%d out of %d major %dms timeouts tripped)", minor_threshold, minor_triggers, minor_maxtriggers, major_triggers, major_maxtriggers, major_threshold); } if ((reset_interval > 0) && (major_triggers || minor_triggers) && (clock_tick > (last_trigger + reset_interval))) { WARNING("Resetting watchdog timer trigger counts, as it has not been triggered in over %lums.", clock_tick - last_trigger); major_triggers = minor_triggers = 0; } last_fire = clock_tick; setPaused(); // Return this task to a paused state return true; } // Returns the clock_tick when this task should next run unsigned int watchdog::wake() { return last_fire + interval; } sipp-3.7.2/src/xp_parser.c0000664000000000000000000003655314525516253012350 0ustar /* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ /* * Mini xml parser: * * WARNING 1: Only supports printable * ASCII characters in xml files. '\0' * is not a valid character. Returned string are * NULL-terminated. * * WARNING 2: Does not supports multithreading. Works * with static buffer, no memory allocation. */ /******************* Include files *********************/ #include #include #include #include "xp_parser.h" #define strstartswith(haystack, needle) \ (!strncmp(haystack, needle, sizeof(needle) - 1)) /************* Constants and Global variables ***********/ #define XP_MAX_NAME_LEN 256 #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) /****************** 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 { const char *name; const char *value; } html_escapes[] = { { "amp", "&" }, { "gt", ">" }, { "lt", "<" }, { "quot", "\"" }, { NULL, NULL } }; struct escape *n; for (n = html_escapes; n->name; ++n) { if (strncmp(escape, n->name, len) == 0) return n->value; } 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) { while (*ptr) { if (*ptr == '<') { if (strstartswith(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 3; } else { return NULL; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { return ptr; } else if (*ptr == '"') { ptr++; while (*ptr) { if (*ptr == '\\') { ptr += 2; } else if (*ptr == '"') { ptr++; break; } else { ptr++; } } } else if (*ptr == '>') { return ptr; } else { ptr++; } } return ptr; } static char *xp_find_local_end() { char *ptr = xp_position[xp_stack]; int level = 0; while (*ptr) { if (*ptr == '<') { if (strstartswith(ptr, ""); if (!cdata_end) return NULL; ptr = cdata_end + 3; } else if (strstartswith(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 3; } else if (*(ptr+1) == '/') { level--; if (level < 0) return ptr; } else { level++; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { level--; if (level < 0) return ptr; } else if (*ptr == '"') { ptr++; while (*ptr) { if (*ptr == '\\') { ptr++; /* Skip the slash. */ } else if (*ptr == '"') { break; } ptr++; } } ptr++; } return ptr; } /********************* Interface routines ********************/ int xp_unescape(const char *source, char *dest) { const char *from; char *to; size_t pos; if (!source || !dest) { return -1; } from = source; to = dest; pos = strcspn(from, "&"); for (; from[pos] != '\0'; pos = strcspn(from, "&")) { size_t term; size_t escape_len; const char *escape; const char c = from[pos]; memcpy(to, from, pos); to += pos; from += pos + 1; if (c != '&') continue; term = strcspn(from, ";"); if (from[term] == '\0') { *to++ = '&'; pos = term; break; } escape = xp_find_escape(from, term); if (!escape) { *to++ = '&'; continue; } escape_len = strlen(escape); memcpy(to, escape, escape_len); to += escape_len; from += term + 1; } if (pos) { memcpy(to, from, pos); to += pos; } to[0] = '\0'; return to - dest; } int xp_set_xml_buffer_from_string(const char *str) { size_t len = strlen(str); if (len > XP_MAX_FILE_LEN) { return 0; } strcpy(xp_file, str); xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], "")) return 0; xp_position[xp_stack] = xp_position[xp_stack] + 2; return 1; } int xp_set_xml_buffer_from_file(const char *filename) { FILE *f = fopen(filename, "rb"); char *pos; int index = 0; int c; if (!f) { return 0; } while ((c = fgetc(f)) != EOF) { if (c == '\r') continue; xp_file[index++] = c; if (index >= XP_MAX_FILE_LEN) { xp_file[index++] = 0; xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; fclose(f); return 0; } } xp_file[index++] = 0; fclose(f); xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], ""))) return 0; xp_position[xp_stack] = pos + 2; return 1; } char *xp_open_element(int index) { char *ptr = xp_position[xp_stack]; int level = 0; int index_left = index; static char name[XP_MAX_NAME_LEN]; if (index > 0) { xp_history_pop(); } while (*ptr) { if (*ptr == '<') { if ((*(ptr+1) == '!') && (*(ptr+2) == '[') && (strstr(ptr, ""); if (!cdata_end) return NULL; ptr = cdata_end + 2; } else if ((*(ptr+1) == '!') && (*(ptr+2) == '-') && (strstr(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 2; } else if (strstartswith(ptr, ""); 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--; } else { xp_position[++xp_stack] = end; return name; } } /* We want to skip over this particular element .*/ ptr = end - 1; 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) { 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; } return 0; } const char *xp_get_value(const char *name) { int index = 0; static char buffer[XP_MAX_FILE_LEN + 1]; char *ptr, *end, *check; end = xp_find_start_tag_end(xp_position[xp_stack] + 1); if (!end) return NULL; ptr = xp_position[xp_stack]; while (*ptr) { ptr = strstr(ptr, name); if (!ptr) return NULL; if (ptr > end) return NULL; /* FIXME: potential BUG in parser: we must retrieve full word, * so the use of strstr as it is is not enough. * we should check that the retrieved word is not a piece of * another one. */ check = ptr - 1; if (check >= xp_position[xp_stack]) { if ((*check != '\r') && (*check != '\n') && (*check != '\t') && (*check != ' ' )) { ptr += strlen(name); continue; } } else return(NULL); ptr += strlen(name); while ((*ptr == '\r') || (*ptr == '\n') || (*ptr == '\t') || (*ptr == ' ' ) ) { ptr++; } if (*ptr != '=') continue; ptr++; while ((*ptr == '\r') || (*ptr == '\n') || (*ptr == '\t') || (*ptr == ' ') ) { ptr++; } ptr++; if (*ptr) { while (*ptr) { if (*ptr == '\\') { ptr++; switch(*ptr) { case '\\': buffer[index++] = '\\'; break; case '"': buffer[index++] = '"'; break; case 'n': buffer[index++] = '\n'; break; case 't': buffer[index++] = '\t'; break; case 'r': buffer[index++] = '\r'; break; default: buffer[index++] = '\\'; buffer[index++] = *ptr; break; } ptr++; } else if (*ptr == '"') { break; } else { buffer[index++] = *ptr++; } if (index > XP_MAX_FILE_LEN) return NULL; } buffer[index] = 0; return buffer; } } return NULL; } char* xp_get_cdata(void) { static char buffer[XP_MAX_FILE_LEN + 1]; const char *end = xp_find_local_end(); const char *ptr; ptr = strstr(xp_position[xp_stack], " end) return NULL; end = strstr(ptr, "]]>"); if (!end) { return NULL; } if ((end - ptr) > XP_MAX_FILE_LEN) return NULL; memcpy(buffer, ptr, (end - ptr)); buffer[end-ptr] = 0; return buffer; } int xp_get_content_length(const char *P_buffer) { const char *L_ctl_hdr; int L_content_length = -1; unsigned char short_form; short_form = 0; L_ctl_hdr = strstr(P_buffer, "\nContent-Length:"); if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nContent-length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\ncontent-Length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\ncontent-length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nCONTENT-LENGTH:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nl:"); short_form = 1; } if (L_ctl_hdr) { if (short_form) { L_ctl_hdr += 3; } else { L_ctl_hdr += 16; } while (isspace(*L_ctl_hdr)) L_ctl_hdr++; sscanf(L_ctl_hdr, "%d", &L_content_length); } /* L_content_length = -1 the message does not contain content-length */ return (L_content_length); } sipp-3.7.2/src/xp_parser_ut.cpp0000664000000000000000000001566714525516253013423 0ustar /* * 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 * * Author : Walter Doekes - 24 Sep 2014 */ /* This is a separate file because xp_parser.c is a C file, and GTEST * only works with C++ files. */ #include "xp_parser.h" #ifdef GTEST #include "gtest/gtest.h" TEST(xp_parser, set_xml_buffer_from_string__good) { int res; int i; const char *buffers[] = { ("\r\n" "\r\n" "\r\n" "\r\n" " \r\n" "\r\n"), // 1 ("" "" "" "" " " ""), // 2 ("" "" "" " " ""), // 3 ("" "" " " ""), // 4 NULL }; for (i = 0; buffers[i]; ++i) { const char *elem, *prop; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(i + 1, res * (i + 1)); // res == 1 if (!res) continue; elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); prop = xp_get_value("name"); EXPECT_STREQ("Some Scenario", prop); } } TEST(xp_parser, set_xml_buffer_from_string__bad) { int res; int i; const char *buffers[] = { // No " "" "" " " ""), // -1 // Missing ?> ("" "" " " ""), // -2 // Not even a DOCTYPE. ("" " " ""), // -3 NULL }; for (i = 0; buffers[i]; ++i) { const char *elem, *prop; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(-1 - i, (res - 1) * (i + 1)); // res == 0 if (!res) continue; elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); prop = xp_get_value("name"); EXPECT_STREQ("Some Scenario", prop); } } static void xp_traverse_stack() { const char *elem; int i = 0; while ((elem = xp_open_element(i++))) { xp_traverse_stack(); xp_close_element(); } } TEST(xp_parser, detect_unclosed_xml) { int res; int i; const char *buffers[] = { ("\r\n" "\r\n" "\r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" "\r\n"), /* 0th */ ("\r\n" "\r\n" "\r\n" " \r\n" /* missing slash */ " \r\n" " \r\n" " \r\n" "\r\n"), /* 1st */ ("\r\n" "\r\n" "\r\n" " \r\n" /* with slash */ " \r\n" /* and extra /recv */ " \r\n" " \r\n" " \r\n" "\r\n"), /* 2nd */ NULL }; for (i = 0; buffers[i]; ++i) { const char *elem; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(1, res); elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); xp_traverse_stack(); xp_close_element(); /* scenario */ EXPECT_EQ(!!i, xp_is_invalid()); /* all except 0 are invalid */ } } TEST(xp_unescape, empty) { char buffer[] = ""; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("", dst); } TEST(xp_unescape, noop) { char buffer[] = "no escape sequences"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("no escape sequences", dst); } TEST(xp_unescape, begin_and_end) { char buffer[] = "<xml>this && thatthis && that", dst); } TEST(xp_unescape, single_double_quote) { char buffer[] = """; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("\"", dst); } TEST(xp_unescape, escaped_escape) { char buffer[] = "&amp;"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("&", dst); } TEST(xp_unescape, unclosed_escape) { char buffer[] = "< & &"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("< & &", dst); } TEST(xp_unescape, late_closed_escape) { char buffer[] = "< & & >"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("< & & >", dst); } TEST(xp_unescape, unknown_escape) { char buffer[] = "<&garbage;>"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("<&garbage;>", dst); } #endif //GTEST sipp-3.7.2/validate-src.sh0000775000000000000000000000336414525516253012321 0ustar #!/bin/sh print_listing=true err=0 TAB=`printf '\t'` while getopts ':l' opt do case $opt in l) print_listing=false;; esac done list_sed='s/^/ /;s/:/: ^/' files_with_tabs=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -l "$TAB") if test -n "$files_with_tabs"; then echo "tabs encountered in one or more source files:" >&2 for file in $files_with_tabs; do echo " $file" >&2 if $print_listing; then grep -n "$TAB" $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi files_with_trailing_ws=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -l '[[:blank:][:cntrl:]]$') if test -n "$files_with_trailing_ws"; then echo "trailing whitespace found in one or more source files:" >&2 for file in $files_with_trailing_ws; do echo " $files_with_trailing_ws" >&2 if $print_listing; then grep -n '[[:blank:][:cntrl:]]$' $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi files_with_not_4_spaces=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -lE '^( \}| {1,3}[a-z_])') if test -n "$files_with_not_4_spaces"; then echo "files with non-standard indentation found:" >&2 for file in $files_with_not_4_spaces; do echo " $file" >&2 if $print_listing; then grep -nE '^( \}| {1,3}[a-z_])' $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi exit $err